Skip to content

Security review — PR #22 (retroactive): 3 Medium / 3 Low / 1 Info #26

@pulkitpareek18

Description

@pulkitpareek18

Retroactive security review of PR #22 (Central API developer console — merged as 0c325fb, live at 0d1741d).

Discipline-debt clearance — PR #22 touched all four security-reviewer trigger surfaces (auth, crypto, audit, tenant boundaries) and merged without the subagent running, against the standing instruction in CLAUDE.md.

Full report (with reproduction steps + remediation for each finding) lives at qa-log/security-review-pr22.md.

Net assessment

Medium risk overall. No Critical findings. No tenant API key rotation required. Tenant scoping (the load-bearing security property) is correctly enforced; the tests/console-proxy.test.ts suite already covers A-01 + A-10 scoping. The actual issues are documentation drift, an enumeration channel, and an attribution gap on the audit log.

Findings (action items)

Medium — land this week

  • F-1: Console JWT stored in localStorage; docs/threat_model.md A-09 claims "client memory" → either move to HttpOnly cookies OR update A-09 to match implementation. Run threat-model-update, file an ADR.
  • F-2: Email enumeration on /api/console/signup → return uniform 202 regardless of email existence; emit verification email out-of-band.
  • F-3: Console-initiated audit rows show actor_type='api_key' with actor_id=NULL → plumb actorType: 'console' + actorEmail through platform.ts and recordAuditEvent.

Low — land next week or fold into related work

  • F-4: Add per-tenant write rate-limit (60/15min by req.console.tenantId) on POST/PATCH/DELETE in src/routes/console.ts.
  • F-5: Add jti + aud='zeroauth-console' to console JWT; verify aud in verifyConsoleToken; Redis-backed jti revocation list.
  • F-6: Validate ?limit= query param in 5 console handlers; reject with 400 invalid_limit instead of relying on sanitizeLimit downstream.

Informational

  • F-7: Two console handlers use the error: field for human strings; convention is { error: '<machine_code>', message: '<human>' }.

Things checked + clean (8 items)

See report. Highlights: every SQL query in platform.ts is pg-parameterised + tenant-scoped; no dangerouslySetInnerHTML anywhere in dashboard; no JWT or password in log lines; Helmet CSP + trust proxy 1 correctly set behind Caddy.

Recommendations beyond findings

  • Run threat-model-update skill to reconcile A-09 with shipped reality.
  • Adopt zod via dep-add for the 8+ repeated if (!field) checks in console.ts.
  • Add CSP report-uri (open item in threat model).

Subagent

Continuation reference: agentId acdae2de12c322caa — use SendMessage to resume the same security-reviewer session with follow-up questions.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    securitySecurity finding or hardening

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions