T05 · Unauthorized Access and Privilege Escalation
Error
- Location
- index.ts:1069
- Finding
- Unauthenticated Webhooks Are Forwarded as Authorized Agent Commands<![CDATA[ ## Vulnerability Details **File Location**: `index.ts:1069-1078`, with related authorization behavior at `index.ts:845-872` and route registration at `index.ts:1734-1745` **Vulnerability Type**: Authentication bypass and improper authorization **Risk Level**: High ### Vulnerable Code Authentication is performed only when a webhook secret has been configured: ```ts // Verify webhook secret if configured if (acct?.webhookSecret) { const authHeader = req.headers?.authorization ?? ""; const token = authHeader.startsWith("Bearer ") ? authHeader.slice(7) : ""; if (token !== acct.webhookSecret) { res.writeHead(401, { "Content-Type": "application/json" }); res.end(JSON.stringify({ error: "Unauthorized" })); return; } } ``` Inbound content is subsequently forwarded to the agent and explicitly marked as command-authorized: ```ts const ctxPayload = core.channel.reply.finalizeInboundContext({ Body: formattedBody, BodyForAgent: content, RawBody: content, CommandBody: content, From: from, To: to, SessionKey: route.sessionKey, AccountId: accountId, ChatType: chatType, GroupSubject: chatType === "group" ? (groupSubject || replyTarget) : undefined, SenderName: senderName, SenderId: senderId, Provider: "hxa-connect" as const, Surface: "hxa-connect" as const, MessageSid: messageId || `hxa-connect-${Date.now()}`, Timestamp: Date.now(), WasMentioned: true, CommandAuthorized: true, OriginatingChannel: "hxa-connect" as const, OriginatingTo: to, ConversationLabel: chatType === "group" ? (groupSubject || senderName) : senderName, ...(params.replyToMessageId ? { ReplyToId: params.replyToMessageId } : {}), ...(params.replyToBody ? { ReplyToBody: params.replyToBody } : {}), ...(params.replyToSender ? { ReplyToSender: params.replyToSender } : {}), }); ``` The webhook route is registered regardless of whether a secret exists: ```ts for (const [id, acct] of Object.entries(accounts)) { const webhookPath = a ...[truncated 4028 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Fail closed when no webhook secret is configured** - Do not register the webhook route unless a strong secret or signature-verification key is present. - Alternatively, return a configuration error and refuse all webhook requests until authentication is configured. 2. **Do not unconditionally authorize inbound commands** - Set `CommandAuthorized` only after transport authentication and sender authorization succeed. - Unauthenticated or merely informational events must not be assigned command privileges. 3. **Use robust request authentication** - Prefer an HMAC signature over the raw request body, timestamp, HTTP method, and path. - Compare signatures or bearer secrets using a timing-safe comparison. - Reject stale timestamps and previously used event identifiers to prevent replay attacks. 4. **Adopt restrictive defaults** - Default `dmPolicy` and `groupPolicy` to `allowlist` or disabled. - Require operators to explicitly opt into open access. - Update `SKILL.md` examples to include a webhook secret and secure allowlist policies. 5. **Harden the HTTP boundary** - Apply request body size limits. - Validate the payload against a strict schema. - Reject unknown event types and malformed sender or channel identifiers. - Rate-limit requests and record authentication failures without logging secrets. 6. **Separate messaging and administrative privileges** - Do not make administrative HXA operations available to sessions initiated by untrusted messaging channels. - Require explicit confirmation or a separate privileged tool for role changes, ticket creation, secret rotation, and configuration writes. ]]>
