Date: 2026-03-24
Scope: Backend experimental interaction patterns
Branch: feat/security-audit-106
A full review of the backend's interaction patterns was performed covering authentication, LLM prompt handling, webhook ingestion, real-time socket communication, configuration management, and data storage. Four critical issues, four high-severity issues, and five medium-severity issues were identified. Critical and high-priority code fixes have been applied in this branch.
- File:
src/Agents/agent.ts - Description: User-controlled input was concatenated directly into the LLM prompt string with no delimiter or escaping, allowing an attacker to override system instructions.
- Fix applied: User input is now HTML-entity-escaped and wrapped in
<user_input>XML delimiters before interpolation, clearly separating data from instructions.
- File:
src/Auth/accounts.json - Description: Private keys are stored in plaintext on disk. Any file-system read (path traversal, misconfigured permissions, backup leak) exposes all keys.
- Fix required (manual): Encrypt keys at rest using the existing
ENCRYPTION_KEYconfig value before writing to storage. Consider migrating to a secrets manager (e.g., AWS Secrets Manager, HashiCorp Vault).
- File:
src/Gateway/routes.ts - Description: The signup endpoint accepts
pk(private key) in the HTTP request body. This transmits the key over the wire and logs it in access logs. - Fix required (manual): Private keys should never transit the API. Generate key pairs server-side or accept only the public address and have the client sign a challenge.
- File:
src/Gateway/routes.ts - Description: All three webhook endpoints (Stellar funding, Telegram, Discord) were publicly accessible with no signature verification, allowing spoofed events.
- Fix applied: HMAC-SHA256 signature verification middleware (
verifyWebhookSignature) added to all three endpoints. SetWEBHOOK_SECRETin the environment to enforce it in production.
- File:
src/config/config.ts - Description: The default JWT secret was
"secret-token"(12 chars). Short secrets are trivially brute-forceable. - Fix applied: Startup validation now throws if
JWT_SECRETis shorter than 32 characters.
- File:
src/Gateway/socketManager.ts - Description: Any unauthenticated socket could subscribe to
transaction:<id>orbot:<id>rooms and receive sensitive real-time events. - Fix applied:
subscribe:transactionsandsubscribe:bot-alertshandlers now reject unauthenticated clients with an error event and a warning log.
- File:
src/Auth/user.entity/ database layer - Description: The
pkcolumn is stored as plaintext in PostgreSQL. A database dump exposes all keys. - Fix required (manual): Encrypt the
pkfield usingENCRYPTION_KEYbefore persisting (TypeORM transformer or service layer).
- File:
src/Agents/memory/memory.ts - Description: Entries added to the memory store are joined with
\nand prepended verbatim to future prompts, creating a persistent prompt injection vector across sessions. - Fix required (manual): Sanitize or delimit entries when reading from the store before injecting into prompts (the
<user_input>delimiter fix inagent.tspartially mitigates this for the final user input, but stored memory entries should also be treated as untrusted data).
- File:
src/Gateway/socketManager.ts - Description:
origin: process.env.ALLOWED_ORIGINS || "*"defaults to allowing all origins. - Recommendation: Set
ALLOWED_ORIGINSto an explicit allowlist in all environments.
- File:
src/Gateway/routes.ts - Description: Rate limiting is per-IP only. Authenticated users behind a shared IP (NAT, proxy) share the same bucket, and a single user can exhaust the limit for others.
- Recommendation: Add a secondary rate limiter keyed on
req.user.idafterauthenticateToken.
- File:
src/config/config.ts,src/Gateway/middleware/rbac.middleware - Description:
admin.allowedIpsis populated from the environment but therequireAdminmiddleware does not check it. - Recommendation: Add an IP check inside
requireAdminusingconfig.admin.allowedIps.
- File:
src/config/config.ts - Description: Redis connection has no
tlsoption, transmitting session/cache data in plaintext. - Recommendation: Add
tls: {}to the Redis config whenNODE_ENV === "production".
- File:
src/Agents/agent.ts,src/Gateway/socketManager.ts - Description: LLM calls and socket event handlers have no per-user throttle, enabling cost amplification and denial-of-service.
- Recommendation: Add a per-agent/per-user token bucket before
callLLM, and throttle socket event handlers.
| ID | File | Change |
|---|---|---|
| C1 | src/Agents/agent.ts |
Escape and delimit user input with <user_input> tags |
| C4 | src/Gateway/routes.ts |
HMAC-SHA256 webhook signature verification middleware |
| H1 | src/config/config.ts |
JWT secret minimum length validation at startup |
| H2 | src/Gateway/socketManager.ts |
Auth check before room subscription |
| ID | Priority | Action |
|---|---|---|
| C2 | Critical | Encrypt private keys at rest in accounts.json |
| C3 | Critical | Remove pk from signup request body; generate server-side or use challenge-response |
| H3 | High | Encrypt pk column in database via TypeORM transformer |
| H4 | High | Delimit/sanitize memory store entries before LLM injection |
| M1 | Medium | Set explicit ALLOWED_ORIGINS in all environments |
| M2 | Medium | Add per-user rate limiter after authenticateToken |
| M3 | Medium | Enforce admin.allowedIps inside requireAdmin middleware |
| M4 | Medium | Enable Redis TLS in production config |
| M5 | Medium | Add per-agent throttle on LLM calls and socket events |