Skip to content

Commit 7a55a2b

Browse files
security: prevent cross-instance auth bypass via query/body override (CVE pending, #2435) (#2549)
* security: prevent cross-instance auth bypass via query/body override (#2435) The auth guard in src/api/guards/auth.guard.ts validates instance ownership using req.params.instanceName. The abstract router then merged req.query (and, on /instance/create, req.body) into the instance object via Object.assign — which silently overwrote the already-authenticated instanceName. An attacker with one valid token could send: GET /chat/findMessages/MY_INSTANCE?instanceName=VICTIM_INSTANCE Auth passed for MY_INSTANCE, but dataValidate() then replaced the instance with VICTIM_INSTANCE before execute() ran — giving the caller full access to read/send messages, modify settings, and delete other tenants' instances. CWE-639: Authorization Bypass Through User-Controlled Key Fix: introduce sanitizeUntrustedInput() that strips PROTECTED_INSTANCE_FIELDS (instanceName, instanceId) from any untrusted source before merging into the instance object. Logs a warning on attempts so abuse is auditable. Closes #2435 Reported by @lighthousekeeper1212 via static analysis. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(lint): remove extra blank line introduced by recent merge Pre-push lint flagged whatsapp.baileys.service.ts:531 (double blank line after the stream:error 515 fix merged via #2509). Trivial prettier fix. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent e57bb36 commit 7a55a2b

2 files changed

Lines changed: 17 additions & 3 deletions

File tree

src/api/abstract/abstract.router.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,21 @@ type DataValidate<T> = {
1717

1818
const logger = new Logger('Validate');
1919

20+
const PROTECTED_INSTANCE_FIELDS = ['instanceName', 'instanceId'] as const;
21+
22+
function sanitizeUntrustedInput(source: Record<string, any> | undefined): Record<string, any> {
23+
if (!source || typeof source !== 'object') return {};
24+
const sanitized: Record<string, any> = {};
25+
for (const [key, value] of Object.entries(source)) {
26+
if ((PROTECTED_INSTANCE_FIELDS as readonly string[]).includes(key)) {
27+
logger.warn(`Ignoring attempt to override protected field "${key}" via untrusted input`);
28+
continue;
29+
}
30+
sanitized[key] = value;
31+
}
32+
return sanitized;
33+
}
34+
2035
export abstract class RouterBroker {
2136
constructor() {}
2237
public routerPath(path: string, param = true) {
@@ -34,11 +49,11 @@ export abstract class RouterBroker {
3449
const instance = request.params as unknown as InstanceDto;
3550

3651
if (request?.query && Object.keys(request.query).length > 0) {
37-
Object.assign(instance, request.query);
52+
Object.assign(instance, sanitizeUntrustedInput(request.query as Record<string, any>));
3853
}
3954

4055
if (request.originalUrl.includes('/instance/create')) {
41-
Object.assign(instance, body);
56+
Object.assign(instance, sanitizeUntrustedInput(body));
4257
}
4358

4459
Object.assign(ref, body);

src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,6 @@ export class BaileysStartupService extends ChannelStartupService {
528528
instanceName: this.instance.name,
529529
});
530530

531-
532531
if (shouldReconnect) {
533532
// Add 3 second delay before reconnection to prevent rapid reconnection loops
534533
this.logger.info('Reconnecting in 3 seconds...');

0 commit comments

Comments
 (0)