T05 · Unauthorized Access and Privilege Escalation
- Location
index.ts:43- Finding
Unauthenticated Webhook Bypasses Pairing and Allowlist Controls
- Content
View full analysis
{ if (req.method === "OPTIONS") { res.setHeader("Access-Control-Allow-Origin", "*"); res.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS"); res.setHeader("Access-Control-Allow-Headers", "Content-Type"); res.statusCode = 200; res.end(); return; } if (req.url !== account.webhookPath || req.method !== "POST") { res.statusCode = 404; res.end("Not Found"); return; } let body = ""; req.on("data", (chunk: any) => { body += chunk.toString(); }); req.on("end", async () => { try { const payload = JSON.parse(body); await handleIncomingMessage(account, payload, pluginApi); res.statusCode = 200; res.setHeader("Content-Type", "application/json"); res.end(JSON.stringify({ success: true })); } catch (error) { console.error("[quantum-im] Webhook error:", error); res.statusCode = 400; res.end(JSON.stringify({ error: "Invalid payload" })); } }); }); ``` The plugin declares direct-message security controls: ```ts security: { dm: { channelKey: "quantum-im", resolvePolicy: (account) => account.dmSecurity, resolveAllowFrom: (account) => account.allowFrom, defaultPolicy: "pairing", }, }, ``` However, the custom webhook directly processes the supplied identity and dispatches the content: ```ts const { type, phone, groupId, callBackUrl, textMsg, imageMsg, fileMsg, news } = payload; const isGroup = Boolean(groupId); const chatType = isGroup ? "group" : "direct"; const peerId = isGroup ? groupId! : phone; ``` ### Technical Analysis The webhook verifies only the request method and URL path. It does not verify a cryptographic signatu ...[truncated 1999 chars]- Remediation
View remediation
