# Kordant — Attack Surface Knowledge Base (Phase 3) > **Generated**: 2026-05-28 > **Phase**: L2 (Knowledge Base / Threat Model) > **Target**: Kordant monorepo — SolidStart + tRPC + Drizzle ORM + native mobile apps > **Commit**: `26d9f8b050969dfaa2c9dfb714a872160b7db382` --- ## Project Classification | Field | Value | |-------|-------| | **Primary type** | Web application (SSR via SolidStart) | | **Secondary types** | API server (tRPC), Background worker (BullMQ + Redis), Browser extension (Manifest V3), Native mobile apps (iOS/SwiftUI, Android/Jetpack Compose) | | **Monorepo structure** | pnpm workspaces: `web/`, `browser-ext/`; plus sibling projects `iOS/`, `android/`, `honker/`, `scheduler/` | | **Hosting** | Vercel (web), self-hosted Docker (scheduler, Redis) | | **Primary language** | TypeScript/JavaScript | | **Secondary languages** | Swift (iOS), Kotlin (Android), Rust (`honker/` — SQLite extension, not part of Kordant runtime) | | **Framework** | SolidStart 2.0.0-alpha.2 (Nitro-based SSR), tRPC 10.45.4, Drizzle ORM 0.45.2 | | **Database** | Turso/libSQL (SQLite, cloud-hosted) | | **Cache/Queue** | Redis 7 (BullMQ job queue, rate limiting via sorted sets) | | **Auth provider** | Clerk (OAuth + email/password via `clerk-solidjs`) | | **Payments** | Stripe (Checkout Sessions, Billing Portal, Webhooks) | | **External APIs** | HIBP, SecurityTrails, Censys, Shodan, Twilio, Resend, Firebase FCM, Sentry | --- ## Architecture Model ### Components ``` ┌─────────────────────────────────────────────────────────────────────┐ │ CLIENTS │ │ ┌──────────┐ ┌─────────┐ ┌──────────┐ ┌──────────────────┐ │ │ │ Web (SSR)│ │ iOS │ │ Android │ │ Browser Extension│ │ │ │SolidStart│ │SwiftUI │ │Compose │ │Manifest V3 │ │ │ └────┬─────┘ └────┬────┘ └────┬─────┘ └────────┬─────────┘ │ │ │ │ │ │ │ │ └──────────────┴─────────────┴─────────────────┘ │ │ │ tRPC (HTTP batch + WebSocket) │ └──────────────────────────┼───────────────────────────────────────────┘ │ ┌──────────────────────────┼───────────────────────────────────────────┐ │ web/ (SolidStart) │ │ ┌───────────────────────┼──────────────────────────────────────┐ │ │ │ Frontend (SSR/CSR) │ Middleware Pipeline │ │ │ │ Routes: (webapp), │ 1. requestLogger │ │ │ │ (admin), (auth), │ 2. securityHeaders (CSP, HSTS, etc) │ │ │ │ landing pages │ 3. corsHeaders │ │ │ │ Components: UI, │ 4. clerkMiddleware (authn) │ │ │ │ layouts, widgets │ └── each tRPC procedure adds: │ │ │ └───────────────────────┤ - protectedProcedure (authz) │ │ │ ┌───────────────────────┤ - adminProcedure (role=admin) │ │ │ │ Backend (tRPC) │ - rateLimitedProcedure │ │ │ │ 16 routers: │ │ │ │ │ example, user, │ │ │ │ │ billing, notification│ │ │ │ │ darkwatch, voiceprint│ │ │ │ │ spamshield, hometitle│ │ │ │ │ removebrokers, │ │ │ │ │ correlation, │ │ │ │ │ reports, scheduler, │ │ │ │ │ extension, blog, │ │ │ │ │ admin │ │ │ │ └───────────────────────┼──────────────────────────────────────┘ │ │ ┌───────────────────────┼──────────────────────────────────────┐ │ │ │ Background Jobs │ WebSocket Server (ws@8.21.0) │ │ │ │ BullMQ + Redis │ Port 3001, JWT-auth via URL param │ │ │ │ Scheduler container │ Heartbeat + pong timeout │ │ │ └───────────────────────┼──────────────────────────────────────┘ │ │ ┌───────────────────────┼──────────────────────────────────────┐ │ │ │ Report Generator │ External API Clients │ │ │ │ Puppeteer (headless)│ HIBP, SecurityTrails, Censys, │ │ │ │ HTML→PDF │ Shodan, Twilio, Stripe, Resend, FCM │ │ │ └───────────────────────┼──────────────────────────────────────┘ │ └──────────────────────────┼───────────────────────────────────────────┘ │ ┌────────────┼────────────┐ ▼ ▼ ▼ ┌─────────────┐ ┌─────────┐ ┌──────────┐ │ Turso │ │ Redis │ │ External │ │ libSQL │ │ 7 │ │ APIs │ │ (SQLite) │ │(BullMQ, │ │ │ │ │ │ rate │ │ │ └─────────────┘ │ limit) │ └──────────┘ └─────────┘ ``` ### Service Domains | Domain | tRPC Router | Trust Level | Key Data | External Dependencies | |--------|-------------|-------------|----------|----------------------| | **VoicePrint** | `voiceprint` | Protected (auth) | Voice enrollments, audio samples, analysis results | None (local ML) | | **DarkWatch** | `darkwatch` | Protected (auth + tier) | Watchlist items, exposure records | HIBP, SecurityTrails, Censys, Shodan | | **SpamShield** | `spamshield` | Protected (auth) | Spam classifications, phone numbers, SMS content | Twilio, ML engine | | **HomeTitle** | `hometitle` | Protected (auth + tier) | Property records, deed changes | County deed APIs | | **RemoveBrokers** | `removebrokers` | Protected (auth + tier) | Opt-out requests, broker data | Broker opt-out APIs | | **Billing** | `billing` | Protected (auth) | Subscriptions, payments, invoices | Stripe | | **Admin** | `admin` | Admin-only | Blog posts, user management, stats | Drizzle ORM | | **Extension** | `extension` | Public (some) | Device linking, phishing reports | Drizzle ORM | | **Reports** | `reports` | Protected (auth + tier) | Security reports, PDF generation | Puppeteer, Drizzle | | **User** | `user` | Protected (auth) | User profile, preferences | Drizzle ORM | | **Notifications** | `notification` | Protected (auth) | Push notifications, email | Firebase FCM, Resend | | **Correlation** | `correlation` | Protected (auth) | Cross-domain alert correlation | Drizzle ORM | | **Scheduler** | `scheduler` | Internal (worker) | Job scheduling, cron tasks | BullMQ, Drizzle | --- ## Trust Boundaries | # | Boundary | Direction | Protocol | Auth | Encryption | Risk | |---|----------|-----------|----------|------|------------|------| | TB-1 | **Internet → Web (SolidStart)** | Client → Server | HTTPS/tRPC | Clerk session + JWT + API key | TLS | HIGH — tRPC procedures are the primary attack surface | | TB-2 | **tRPC → Drizzle ORM** | App → DB | libSQL/Turso | JWT-validated user context | TLS (Turso) | CRITICAL — SQL injection via drizzle-orm CVE-2026-39356 | | TB-3 | **tRPC → Stripe** | App → Stripe | HTTPS | Stripe secret key | TLS | HIGH — Webhook spoofing, payment manipulation | | TB-4 | **tRPC → External APIs** | App → HIBP/Trails/Censys/Shodan | HTTPS | API keys | TLS | MEDIUM — API key leakage, SSRF via crafted URLs | | TB-5 | **WebSocket → ws** | Client → WS Server | WSS (port 3001) | JWT in query param | TLS | HIGH — Memory disclosure (CVE-2026-45736), DoS (CVE-2024-37890) | | TB-6 | **Browser Extension → tRPC** | Extension → Web | HTTPS | API key (stored in extension) | TLS | HIGH — superjson prototype pollution chain (CVE-2022-23631) | | TB-7 | **tRPC → Redis** | App → Redis | TCP (internal) | None (network-isolated) | None | MEDIUM — BullMQ job injection, cache poisoning | | TB-8 | **Puppeteer → File System** | App → Local FS | Local | None | N/A | HIGH — Path traversal, SSRF via file input | | TB-9 | **tRPC → VoicePrint Storage** | App → Audio Files | Local FS | Protected procedure | N/A | MEDIUM — Audio file access, path traversal | | TB-10 | **Scheduler → Redis** | Worker → Redis | TCP (internal) | None (network-isolated) | N/A | LOW — Internal worker communication | ### Role-Based Access | Role | Access Level | Enforcement | |------|-------------|-------------| | **Anonymous** | Public procedures only (`extension.reportPhishing`, `extension.getAuthStatus`) | tRPC procedure type | | **Authenticated User** | Protected procedures (all data scoped to `ctx.user.id`) | `isAuthed` middleware checks `ctx.user` | | **Admin** | Admin procedures (`adminRouter`) + all user procedures | `isAdmin` middleware checks `ctx.user.role === "admin"` | | **API Key** | Limited procedures (extension API key path) | `ctx.apiKey` fallback in `createTRPCContext` | --- ## Data-Flow Slices (DFD) ### DFD-1: tRPC → Drizzle ORM (SQL Injection Vector) ```mermaid flowchart LR A[Client Input] -->|tRPC procedure| B[Input Validation\nvalibot schema] B --> C{Schema pass?} C -->|No| D[TRPCError thrown] C -->|Yes| E[Service Layer] E --> F[Drizzle ORM Query] F --> G[Turso/SQLite] style A fill:#ff6b6b style G fill:#4ecdc4 style F fill:#ffe66d ``` **Flow**: User input → valibot validation → tRPC procedure → Drizzle ORM → Turso SQLite **Key risk**: CVE-2026-39356 — SQL injection via improperly escaped SQL identifiers in drizzle-orm 0.45.2. If any tRPC procedure passes user input into column/table names (via `sql` tag or dynamic column references), injection is possible. **Sinks to check**: `sql<>` template tag usage, dynamic column references, `inArray` with user-controlled values, raw Drizzle query builders. ### DFD-2: VoicePrint Audio Analysis Pipeline ```mermaid flowchart LR A[Base64 Audio Data] -->|tRPC analyzeAudio| B[decode Base64 → Buffer] B --> C[saveAudio\nwrite to disk] C --> D[preprocessAudio\nfeature extraction] D --> E[detectSynthetic\nML inference] E --> F[matchVoice\nembedding comparison] F --> G[store analysis\nDrizzle ORM] G --> H[broadcast alert\nWebSocket] style A fill:#ff6b6b style C fill:#ffe66d style E fill:#ffe66d ``` **Flow**: Base64 audio → decode → save to disk → ML preprocessing → synthetic detection → voice matching → store results → WebSocket alert **Key risks**: - Unbounded audio size (DoS via large uploads) - Base64 decode buffer overflow potential - ML engine input validation - File path construction for audio storage ### DFD-3: Browser Extension → tRPC (Prototype Pollution Chain) ```mermaid flowchart LR A[Extension Content Script] -->|tRPC call| B[superjson serialization] B -->|HTTP POST| C[tRPC endpoint] C --> D[superjson deserialization] D --> E[Prototype Pollution?] E -->|Yes| F[RCE via __proto__ overwrite] D --> G[valibot validation] G --> H[Service layer] style A fill:#ff6b6b style D fill:#ffe66d style F fill:#ff0000 ``` **Flow**: Extension → superjson serialize → HTTP → tRPC → superjson deserialize → valibot → service **Key risk**: superjson CVE-2022-23631 (prototype pollution → RCE, CVSS 10.0). The browser extension uses superjson for tRPC serialization. If the tRPC server deserializes untrusted superjson data, prototype pollution is possible. ### DFD-4: WebSocket Real-Time Alerts ```mermaid flowchart LR A[Service Layer] -->|broadcastToUser| B[WebSocket Server] B --> C{User connected?} C -->|Yes| D[ws.send JSON alert] C -->|No| E[Drop silently] D --> F[Client receives alert] G[Client connect] -->|JWT in ?token param| H[authenticateConnection] H -->|Valid| I[addSocket to userSockets] H -->|Invalid| J[close 4001] style A fill:#4ecdc4 style B fill:#ffe66d style G fill:#ff6b6b ``` **Flow**: Service → broadcastToUser → WebSocket server → user socket → client **Key risks**: - JWT in URL query parameter (log exposure, referer leakage) - No message size limit - No rate limiting on WebSocket messages - Heartbeat bypass potential ### DFD-5: Stripe Webhook Processing ```mermaid flowchart LR A[Stripe → POST /api/webhook] --> B[signature verification] B -->|Valid| C[handleWebhookEvent] B -->|Invalid| D[400 rejected] C --> E{event.type} E -->|checkout.session.completed| F[create subscription in DB] E -->|invoice.paid| G[update status to active] E -->|invoice.payment_failed| H[update status to past_due] E -->|customer.subscription.updated| I[update tier/status] E -->|customer.subscription.deleted| J[mark as canceled] style A fill:#ff6b6b style B fill:#ffe66d style F fill:#4ecdc4 ``` **Key risk**: Webhook replay attacks if signature verification is weak. Type coercion in `event.data.object as unknown as Record` could bypass type checks. ### DFD-6: Report Generation (Puppeteer) ```mermaid flowchart LR A[generateReport mutation] --> B[compileData from DB] B --> C[renderHTML template] C --> D[generatePDF via Puppeteer] D --> E[uploadPDF to storage] E --> F[update report status] style A fill:#ff6b6b style D fill:#ffe66d ``` **Key risk**: Puppeteer SSRF if HTML template contains user-controlled URLs. Path traversal in report filename construction. --- ## Control-Flow Slices (CFD) ### CFD-1: Authentication Flow ```mermaid flowchart TD A[Request arrives] --> B[Clerk middleware] B --> C{Valid Clerk session?} C -->|Yes| D[set ctx.user] C -->|No| E[Check Bearer token] E --> F{Valid JWT?} F -->|Yes| G[set ctx.user from JWT] F -->|No| H[Check x-api-key] H --> I[set ctx.apiKey] style A fill:#ff6b6b style B fill:#ffe66d style D fill:#4ecdc4 style G fill:#4ecdc4 ``` **Auth chain**: Clerk session cookie → Bearer JWT → API key. Each level is a fallback, not a parallel auth. The `x-api-key` is the weakest link — it's checked only if both session and JWT fail. ### CFD-2: Authorization Flow ```mermaid flowchart TD A[tRPC procedure] --> B{Procedure type?} B -->|publicProcedure| C[No auth check] B -->|protectedProcedure| D{ctx.user exists?} B -->|adminProcedure| E{ctx.user && role=admin?} D -->|No| F[401 UNAUTHORIZED] D -->|Yes| G[proceed] E -->|No| H[403 FORBIDDEN] E -->|Yes| G C --> I[proceed] style A fill:#ff6b6b style F fill:#ff6b6b style H fill:#ff6b6b style G fill:#4ecdc4 ``` **Key observation**: Admin check is `ctx.user.role !== "admin"` — this is a string comparison, not an enum. Any user with `role: "admin"` in the DB gets admin access. No additional checks (IP allowlist, MFA, audit logging). ### CFD-3: Rate Limiting Flow ```mermaid flowchart TD A[Procedure middleware] --> B{Path in sensitive list?} B -->|Yes| C[Tier: sensitive\n3/hr] B -->|No| D{ctx.user?} D -->|Yes| E{role=admin?} D -->|No| F[Tier: public\n5/min] E -->|Yes| G[Tier: admin\n50/min] E -->|No| H[Tier: authenticated\n100/min] C --> I[Redis sorted set check] G --> I H --> I F --> I I --> J{Allowed?} J -->|No| K[429 TOO_MANY_REQUESTS] J -->|Yes| L[proceed] style A fill:#ff6b6b style K fill:#ff6b6b style L fill:#4ecdc4 ``` **Key observation**: Sensitive paths are hardcoded: `["login", "signup", "forgotPassword", "resetPassword"]`. Other procedures don't get sensitive-tier limits. The `websocket` tier (1/minute) is defined but not applied to WebSocket connections. --- ## Framework Contracts and Hidden Control Channels ### SolidStart / Nitro Middleware Pipeline **File**: `web/src/middleware.ts` The middleware chain is: 1. `requestLogger` — logs all requests 2. `securityHeaders` — sets HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy 3. `corsHeaders` — origin validation + preflight handling 4. `clerkMiddleware` — Clerk authentication **Critical contract**: Middleware runs before all routes AND before tRPC handlers. If any middleware fails silently, the next middleware or handler proceeds without the expected security headers or auth context. ### CSP Header Analysis ``` default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' *.clerk.dev *.clerk.com *.stripe.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: *.gravatar.com *.clerk.dev *.clerk.com; connect-src 'self' *.clerk.dev *.clerk.com *.stripe.com *.sentry.io ws: wss:; frame-src 'self' *.stripe.com; font-src 'self' data:; object-src 'none'; base-uri 'self'; form-action 'self' *.stripe.com ``` **Contract implications**: - `'unsafe-eval'` in script-src — weakens CSP, enables potential XSS exploitation - `'unsafe-inline'` in script-src and style-src — allows inline scripts/styles - `ws:` and `wss:` in connect-src — allows WebSocket to any host - `*.clerk.dev` and `*.clerk.com` — trust Clerk's entire domain for script execution ### tRPC Procedure Contracts **File**: `web/src/server/api/utils.ts` Three procedure types with different contract guarantees: - `publicProcedure` — No auth, no rate limiting. Used by `extensionRouter` (device linking, phishing reports). - `protectedProcedure` — Auth required (`ctx.user` must exist). No role check. - `adminProcedure` — Auth required + `role === "admin"`. Used by `adminRouter`. - `rateLimitedProcedure` — Rate limiting middleware applied. **Hidden contract**: The `path` parameter in the rate limiter middleware is used to detect sensitive paths. If a procedure's path contains `"login"`, `"signup"`, `"forgotPassword"`, or `"resetPassword"`, it gets the sensitive tier (3/hour). This is a string-match heuristic, not an exact routing match. ### CORS Contract **File**: `web/src/middleware.ts` ```typescript const allowedOrigins = [ "http://localhost:3000", "http://localhost:3001", process.env.APP_URL, ].filter(Boolean); ``` **Contract implication**: `APP_URL` from environment is trusted as an allowed CORS origin. If an attacker can control `APP_URL` (e.g., via environment variable injection), they can set an arbitrary allowed origin. The origin is checked via exact string match — no wildcard or prefix matching. ### WebSocket Auth Contract **File**: `web/src/server/websocket.ts` - JWT passed as `?token=` query parameter — visible in server logs, browser history, proxy logs - No `Origin` header validation on WebSocket upgrade - No `Sec-WebSocket-Protocol` validation - No message size limit - Heartbeat uses 30s interval + 10s pong timeout — potential for slow-loris DoS ### Clerk Middleware Contract **File**: `web/src/middleware.ts` ```typescript clerkMiddleware({ publishableKey: process.env.VITE_CLERK_PUBLISHABLE_KEY, secretKey: process.env.CLERK_SECRET_KEY, }) ``` **Contract implication**: Clerk handles the actual authentication. The middleware sets `ctx.user` based on Clerk session. If Clerk's session validation is bypassed (e.g., via clock skew, expired session reuse), auth is compromised. ### Vite Dev Server Contract **File**: `web/vite.config.ts` ```typescript plugins: [solidStart(), tailwindcss(), nitro()] ``` **Contract implication**: Vite dev server runs on port 3000 in development. The `server.fs.deny` configuration is not explicitly set, meaning the default applies. Given 14+ CVEs for `server.fs.deny` bypass, the dev server is a significant risk if exposed. ### Dockerfile Contract **File**: `web/Dockerfile` ```dockerfile USER appuser ``` **Contract implication**: Runs as non-root user `appuser` — good security practice. However, the container has `curl` installed for health checks, which could be used for SSRF if the application is compromised. ### Stripe Contract **File**: `web/src/server/services/billing.service.ts` ```typescript const obj = event.data.object as unknown as Record; ``` **Contract implication**: Stripe webhook events are deserialized via unsafe type coercion. The `as unknown as Record` bypasses TypeScript type checking. If Stripe's event format changes, field access could fail silently or produce unexpected results. --- ## Threat Model ### Assets | Asset | Sensitivity | Storage | Threat | |-------|-------------|---------|--------| | User credentials (passwords, emails) | HIGH | Turso (bcrypt hash) | Theft, brute force | | JWT signing secret (`JWT_SECRET`) | CRITICAL | Environment variable | Token forgery | | Clerk secret key | CRITICAL | Environment variable | Auth bypass | | Stripe secret key | CRITICAL | Environment variable | Payment manipulation | | User PII (names, emails, phone numbers, SSNs) | HIGH | Turso | Data exfiltration | | Voice enrollment audio | HIGH | Local filesystem | Privacy breach | | Voice analysis results | MEDIUM | Turso | Identity spoofing | | Watchlist items (emails, phones, SSNs) | HIGH | Turso | Privacy breach | | Dark web exposure data | HIGH | Turso | Data exfiltration | | API keys (HIBP, SecurityTrails, Censys, Shodan, Twilio) | HIGH | Environment variables | Cost abuse, data access | | Session tokens | MEDIUM | Turso | Session hijacking | | Browser extension API key | MEDIUM | Extension storage | Unauthorized API access | ### Threat Actors | Actor | Capability | Motivation | Likelihood | |-------|-----------|------------|------------| | **External attacker** | Internet access, can send crafted HTTP/WebSocket requests | Data theft, account takeover | High | | **Compromised browser extension** | Can make tRPC calls with stored API key | Data exfiltration from linked account | Medium | | **Insider (non-admin)** | Authenticated user access to own data | Data exfiltration, privilege escalation | Low-Medium | | **Insider (admin)** | Full admin access | Data manipulation, privilege escalation | Low | | **Supply chain attacker** | Compromised npm package | Code injection, credential theft | Medium | | **Clerk infrastructure attacker** | Compromised Clerk auth service | Bypass all auth | Low | ### Attack Scenarios #### AS-1: SQL Injection via Drizzle ORM (CRITICAL) **Precondition**: tRPC procedure passes user input into Drizzle query identifiers or uses `sql<>` tag unsafely. **Attack**: Attacker sends crafted input to a tRPC procedure that interpolates values into SQL column/table names. **Impact**: Full database read/write/delete. All user data exposed. **Likelihood**: Medium-High (CVE-2026-39356 is actively exploitable in drizzle-orm 0.45.2) **Mitigation**: Audit all `sql<>` tag usage, avoid dynamic column names, review CVE-2026-39356 patch status. #### AS-2: Prototype Pollution via superjson (CRITICAL) **Precondition**: Browser extension uses superjson for tRPC serialization; tRPC server deserializes superjson data. **Attack**: Attacker crafts malicious superjson payload with `__proto__` pollution. **Impact**: RCE via prototype chain manipulation. **Likelihood**: Medium (superjson CVE-2022-23631 is CVSS 10.0, but requires superjson serialization path) **Mitigation**: Audit superjson usage, consider migrating to safe serialization, apply patches. #### AS-3: WebSocket Authentication Bypass (HIGH) **Precondition**: WebSocket JWT passed in query parameter. **Attack**: Attacker captures JWT from logs/referer and replays it for WebSocket access. **Impact**: Receive real-time alerts for any user whose JWT is leaked. **Likelihood**: Medium (JWT in query params is logged by most reverse proxies) **Mitigation**: Move JWT to `Sec-WebSocket-Protocol` header or `Authorization` header. Implement `Origin` validation. #### AS-4: Admin Privilege Escalation (HIGH) **Precondition**: Attacker can modify their own `users.role` in the database (via SQL injection or direct DB access). **Attack**: Set `role = "admin"` in the `users` table. **Impact**: Full admin panel access — blog management, user role changes, stats access. **Likelihood**: Low (requires DB access first, but chained with AS-1 becomes critical) **Mitigation**: Add role change audit logging, restrict role changes to system-only procedures. #### AS-5: Rate Limit Bypass (MEDIUM) **Precondition**: Rate limiting uses Redis sorted sets with `identifier` based on `ctx.user.id` or `ctx.apiKey`. **Attack**: Attacker rotates API keys or creates multiple accounts to bypass per-identifier limits. **Impact**: Brute force attacks, resource exhaustion. **Likelihood**: Medium **Mitigation**: Add IP-based rate limiting as a secondary dimension. #### AS-6: Stripe Webhook Replay (MEDIUM) **Precondition**: Webhook handler processes events without idempotency checks. **Attack**: Replay `checkout.session.completed` webhook to create duplicate subscriptions. **Impact**: Billing manipulation, subscription abuse. **Likelihood**: Low-Medium **Mitigation**: Add event ID deduplication in the webhook handler. #### AS-7: XSS via SolidJS JSX (HIGH) **Precondition**: User-controlled data rendered in SolidJS JSX components without proper escaping. **Attack**: Inject malicious content via blog post titles, user names, or other user-controlled fields. **Impact**: Account takeover via cookie theft, phishing. **Likelihood**: Medium (CVE-2025-27109 affects JSX fragment rendering) **Mitigation**: Audit all JSX rendering of user data, ensure CSP is effective. #### AS-8: Path Traversal via Puppeteer (HIGH) **Precondition**: Report generation uses Puppeteer to render HTML to PDF. **Attack**: Craft HTML with `file://` URLs or relative paths to read local files. **Impact**: Read sensitive files (`.env`, source code, database credentials). **Likelihood**: Low (requires auth + controlled report parameters) **Mitigation**: Sandbox Puppeteer, disable file access, validate all URLs in templates. --- ## Domain Attack Research ### Mode A — Library-as-Target: No applicable (project is not a library/plugin/protocol) ### Mode B — Library-as-Consumer: Security-Sensitive Dependencies #### B-1: drizzle-orm 0.45.2 — SQL Injection **CVE-2026-39356** (CVSS 7.5): SQL injection via improperly escaped SQL identifiers. **Research findings**: - Drizzle's `sql<>` template tag and dynamic column references are the primary injection vectors - `inArray` with user-controlled arrays can lead to identifier injection - `sql` helper with string interpolation bypasses parameterization **Custom SAST targets**: - All `sql<>` template tag usages in `web/src/server/services/**/*.ts` - All `inArray` calls with non-literal second arguments - All `groupBy`/`orderBy` calls with non-literal column references - All dynamic table name construction **Manual review checklist**: - [ ] Search for `sql\`` or `sql\`${` patterns - [ ] Search for `inArray` with variable arguments - [ ] Review all Drizzle `where` clauses for string interpolation - [ ] Check `groupBy` and `orderBy` for dynamic columns - [ ] Verify no raw SQL strings are constructed from user input #### B-2: @trpc/server 10.45.4 — Prototype Pollution + WebSocket DoS **CVE-2025-68130** (CVSS HIGH): Prototype pollution in `experimental_nextAppDirCaller` **CVE-2025-43855** (CVSS HIGH): WebSocket DoS **Research findings**: - `experimental_nextAppDirCaller` is a Next.js-specific adapter feature — likely not used in SolidStart - WebSocket DoS affects the `ws` library's message framing, not tRPC itself - The tRPC batch endpoint can be used for amplification DoS **Custom SAST targets**: - All WebSocket message handlers for size limits - All tRPC batch request handlers for batch size limits - Any use of `experimental_nextAppDirCaller` (should be none in SolidStart) #### B-3: valibot 0.29.0 — ReDoS **CVE-2025-66020** (CVSS HIGH): ReDoS in `EMOJI_REGEX` **Research findings**: - valibot's built-in emoji validation regex is vulnerable to ReDoS - Any tRPC procedure that validates user input containing emoji characters is potentially vulnerable - The regex is used in `string()` validation with emoji-related constraints **Custom SAST targets**: - All valibot `string()` schemas with emoji constraints - All input validation schemas in `web/src/server/api/schemas/**/*.ts` - All user-facing string input paths **Manual review checklist**: - [ ] Review all valibot schemas for `EMOJI_REGEX` usage - [ ] Check if valibot version has the ReDoS fix applied - [ ] Test with crafted emoji sequences #### B-4: superjson 2.2.1/2.2.6 — Prototype Pollution → RCE **CVE-2022-23631** (CVSS 10.0): Prototype pollution via `__proto__` in JSON deserialization **Research findings**: - superjson is used by the browser extension for tRPC serialization - The tRPC server may receive superjson-serialized data from the extension - CVE-2022-23631 affects superjson < 2.2.6; the browser extension uses 2.2.1 (vulnerable) - The web app's package.json does NOT list superjson as a dependency — the server uses native JSON **Custom SAST targets**: - Browser extension superjson usage (`browser-ext/src/lib/api-client.ts`) - Any server-side superjson import (should be none — web app uses native JSON) **Manual review checklist**: - [ ] Verify server does NOT use superjson for deserialization - [ ] Audit browser extension superjson serialization of user data - [ ] Check if extension sends any data containing `__proto__` keys #### B-5: jose 5.10.0 — Resource Exhaustion **CVE-2024-28176** (CVSS 5.3): Resource exhaustion via crafted JWE with compressed plaintext **Research findings**: - jose is used for JWT signing/verification (`web/src/server/auth/jwt.ts`) - The project uses HS256 (symmetric), not JWE (encrypted JWT) - CVE-2024-28176 affects JWE decryption with compressed payloads - Since the project only uses `SignJWT` and `jwtVerify` with HS256, this CVE is NOT directly applicable **Applicability**: LOW — project uses HS256 JWTs, not JWE. #### B-6: ws 8.21.0 — Memory Disclosure + DoS **CVE-2026-45736** (CVSS 5.3): Uninitialized memory disclosure **CVE-2024-37890** (CVSS 7.5): DoS via many HTTP headers **Research findings**: - WebSocket server uses `ws@8.21.0` on port 3001 - Memory disclosure (CVE-2026-45736) affects binary frame handling - DoS (CVE-2024-37890) affects request header parsing during WebSocket upgrade - The WebSocket server validates JWT from query params during upgrade **Custom SAST targets**: - WebSocket server configuration (max payload size) - WebSocket upgrade handler for header count limits - Binary frame handling in the WebSocket server #### B-7: vite 6.4.2/7.3.3 — Path Traversal (14+ CVEs) **CVE lineage**: 14+ CVEs for `server.fs.deny` bypass **Research findings**: - Vite dev server is the primary concern — production build should not expose the dev server - The project deploys via `vite build` → `vite start` (Nitro server), not the dev server - Dockerfile uses `node .output/server/index.mjs` which is the Nitro production server - The `server.fs.deny` CVEs affect the dev server (`vite dev`) and some Nitro file-serving paths **Applicability**: LOW for production, MEDIUM for development environments **Custom SAST targets**: - Vite config for `server.fs.deny` settings - Any custom file-serving middleware in the Nitro server - Dockerfile for dev server exposure ### Mode C — Domain-Specific Attack Research #### C-1: ML/AI Integration — VoicePrint Synthetic Detection **Domain**: Voice biometrics, synthetic voice detection, ML model inference **Research findings**: - VoicePrint service processes audio files through ML pipeline (preprocessing → synthetic detection → voice matching) - Audio files are saved to local filesystem as base64-decoded buffers - No input validation on audio file format, size, or content before ML processing - `audioBase64` parameter accepts arbitrary base64 data — could be crafted audio, non-audio data, or extremely large payloads - No rate limiting on audio analysis (protected procedure only, not rate-limited) **Attack vectors**: 1. **Resource exhaustion**: Upload extremely large base64 audio payloads to exhaust memory during decode + ML processing 2. **Model poisoning**: Craft audio that produces specific ML outputs (adversarial audio) 3. **Privacy leak**: Audio files stored on disk may be readable by other processes 4. **Echo location**: Use voice analysis endpoints to enumerate voice enrollments **Custom SAST targets**: - Audio buffer size limits in `voiceprint.service.ts` - File path construction in `voiceprint/storage.ts` - ML model input validation #### C-2: External API Integration — DarkWatch Multi-Source Scanning **Domain**: OSINT aggregation, external API orchestration, rate limiting, circuit breaking **Research findings**: - DarkWatch scans HIBP, SecurityTrails, Censys, Shodan, and dark web forums - Each scan can trigger 5+ parallel API calls - Circuit breaker pattern is implemented (5 failures → 60s timeout) - No rate limiting between scans (only per-subscription counts via tier limits) - No response validation beyond HTTP status codes - `fetchWithCircuit` uses `AbortSignal.timeout(10_000)` — 10s timeout per request **Attack vectors**: 1. **API cost exhaustion**: Trigger expensive scans via watchlist items with many values 2. **SSRF via URL manipulation**: If scan URLs are constructed from user input, SSRF is possible 3. **Response injection**: Unvalidated responses from external APIs stored in database 4. **Circuit breaker manipulation**: Rapid failures to keep circuits open (denial of service for legitimate scans) **Custom SAST targets**: - URL construction in `darkwatch/scan.engine.ts` - Response parsing and storage in `darkwatch/alert.pipeline.ts` - Watchlist item value validation (email, phone, SSN, address, domain) #### C-3: Payment Processing — Stripe Integration **Domain**: Payment processing, subscription management, webhook handling **Research findings**: - Stripe Checkout Sessions use embedded UI mode (`ui_mode: "embedded_page"`) - Webhook handler uses unsafe type coercion (`as unknown as Record`) - No webhook event ID deduplication - `returnUrl` in checkout session is user-controlled (from `CreateCheckoutSessionSchema`) - Stripe API version pinned to `2026-04-22.dahlia` **Attack vectors**: 1. **Webhook replay**: Replay checkout events to create duplicate subscriptions 2. **Return URL redirect**: If `returnUrl` is not validated, open redirect post-payment 3. **Type coercion bypass**: Malformed webhook events could bypass event type checks 4. **Price ID manipulation**: `mapStripeProductToTier` uses string comparison — if price ID format changes, tier assignment could be wrong **Custom SAST targets**: - Webhook handler event type switch statement - Return URL validation - Price ID to tier mapping #### C-4: Real-Time Communication — WebSocket Alerts **Domain**: WebSocket, real-time messaging, alert distribution **Research findings**: - WebSocket server on port 3001, JWT-authenticated via query parameter - No message size limit - No rate limiting on incoming messages - Heartbeat: 30s interval, 10s pong timeout - `broadcastToUser` sends to all connected sockets for a user - No message validation — any JSON is accepted **Attack vectors**: 1. **Memory exhaustion**: Connect many sockets per user (no connection limit per user) 2. **Slow-loris DoS**: Send valid but slow messages to keep connections alive 3. **Alert flooding**: If a service calls `broadcastToUser` in a loop, flood all connected clients 4. **JWT leakage**: JWT in query params logged by proxies, load balancers, access logs **Custom SAST targets**: - WebSocket connection limit per user - Message size validation - Broadcast rate limiting #### C-5: File System — Puppeteer Report Generation + Audio Storage **Domain**: Headless browser, file I/O, path traversal **Research findings**: - Puppeteer used for HTML-to-PDF report generation - Audio files stored on local filesystem (path based on userId + audio hash) - Report templates directory: `join(__dirname, "templates")` - Reports output directory: `join(process.cwd(), "reports")` - No explicit sandboxing for Puppeteer - No file size limits on audio uploads **Attack vectors**: 1. **SSRF via Puppeteer**: If HTML template contains `file://` or `http://` URLs from user control 2. **Path traversal**: If userId or report filename is user-controlled and not sanitized 3. **Disk exhaustion**: Large audio uploads fill disk, affecting report generation 4. **Template injection**: If HTML templates are user-modifiable **Custom SAST targets**: - Puppeteer launch configuration (sandbox, no-sandbox flags) - File path construction in reports generator - Audio file storage paths --- ## Phase 4 CodeQL Extraction Targets ### DFD-1: tRPC → Drizzle ORM (SQL Injection) | Source | Sink | Language | File Pattern | |--------|------|----------|-------------| | `LocalUserInput` (tRPC procedure input) | `sql-execution` (Drizzle ORM queries) | TypeScript | `web/src/server/api/routers/**/*.ts` | | `LocalUserInput` → `RemoteFlowSource` (valibot validated) | `sql-execution` | TypeScript | `web/src/server/services/**/*.ts` | | `EnvironmentVariable` (API keys) | `http-request` (external API calls) | TypeScript | `web/src/server/services/darkwatch/**/*.ts` | **Expected sink kinds**: `sql-execution`, `http-request`, `command-execution` ### DFD-2: WebSocket (Authentication + Message Handling) | Source | Sink | Language | File Pattern | |--------|------|----------|-------------| | `RemoteFlowSource` (WebSocket frames) | `code-execution` (message handlers) | TypeScript | `web/src/server/websocket.ts` | | `LocalUserInput` (JWT query param) | `LocalFlowSource` (JWT verification) | TypeScript | `web/src/server/websocket.ts` | **Expected sink kinds**: `code-execution`, `deserialization` ### DFD-3: Puppeteer Report Generation | Source | Sink | Language | File Pattern | |--------|------|----------|-------------| | `LocalUserInput` (report parameters) | `http-request` (Puppeteer navigation) | TypeScript | `web/src/server/services/reports/generator.ts` | | `LocalUserInput` | `file-access` (PDF output, audio storage) | TypeScript | `web/src/server/services/reports/**/*.ts`, `web/src/server/services/voiceprint/storage.ts` | **Expected sink kinds**: `http-request`, `file-access` ### DFD-4: Stripe Webhook Processing | Source | Sink | Language | File Pattern | |--------|------|----------|-------------| | `RemoteFlowSource` (webhook event) | `sql-execution` (subscription updates) | TypeScript | `web/src/server/services/billing.service.ts` | | `RemoteFlowSource` | `http-request` (Stripe API calls) | TypeScript | `web/src/server/services/billing.service.ts` | **Expected sink kinds**: `sql-execution`, `http-request` ### DFD-5: Browser Extension → tRPC (Prototype Pollution) | Source | Sink | Language | File Pattern | |--------|------|----------|-------------| | `RemoteFlowSource` (superjson deserialized data) | `code-execution` (prototype chain manipulation) | TypeScript | `browser-ext/src/lib/api-client.ts` | **Expected sink kinds**: `deserialization`, `code-execution` --- ## Spec Gap Candidates No formal specs or RFCs were identified in the codebase or documentation. The project implements the following protocols/standards informally: | Standard | Implementation | Gap Risk | |----------|---------------|----------| | **tRPC protocol** | @trpc/server 10.45.4 | Low — well-defined protocol, version pinned | | **Clerk auth protocol** | clerk-solidjs 2.0.10 | Low — managed auth provider | | **Stripe API** | stripe 22.1.1 (API version 2026-04-22) | Medium — webhook handling uses unsafe type coercion | | **JWT (RFC 7519)** | jose 5.10.0 (HS256) | Low — standard JWT, but JWT in WebSocket query params is non-standard | | **WebSocket (RFC 6455)** | ws 8.21.0 | Medium — no Origin validation, no message size limits | | **CORS (W3C)** | Custom middleware | Low — origin whitelist is correct but APP_URL env var is trusted | --- ## Coverage Gaps ### Not Assessed in This Phase | Area | Reason | Impact on Later Phases | |------|--------|----------------------| | **iOS native app** | Codebase not explored in depth (SwiftUI) | Phase 5-7: certificate pinning, keychain storage, root detection | | **Android native app** | Codebase not explored in depth (Kotlin/Compose) | Phase 5-7: certificate pinning, keystore storage, root detection | | **Honker SQLite extension** | Separate Rust project, not part of Kordant runtime | N/A — not in scope | | **Docker image base** | `node:22-alpine` — no CVE scan of base image | Phase 6: supply chain risk | | **CI/CD pipelines** | No `.github/workflows/` with AI agents found | Phase 7: supply chain, agentic attack surface | | **DNS/DHCP configuration** | Not in codebase | N/A | | **Infrastructure (Vercel config)** | Vercel deployment config not in repo | Phase 6: environment isolation | | **TLS configuration** | Not in codebase (handled by Vercel/proxy) | N/A | | **Backup procedures** | Referenced in `docs/BACKUPS.md` but not reviewed | Phase 6: data integrity | ### Known False-Positive Sources 1. **Vite `server.fs.deny` bypasses** — These affect the **dev server** only (`vite dev`). The production deployment uses `vite build` + `vite start` (Nitro), which does not use the dev server's file serving. Findings related to `server.fs.deny` bypass in production should be evaluated against whether the dev server is exposed. 2. **superjson prototype pollution** — The web server (`web/`) does NOT use superjson as a dependency. Only the browser extension uses superjson for client-side serialization. The tRPC server uses native JSON serialization. The prototype pollution risk is confined to the browser extension's local data handling, not server-side deserialization. 3. **jose JWE resource exhaustion** — The project uses HS256 JWTs (symmetric, no encryption), not JWE (encrypted JWTs). CVE-2024-28176 affects JWE decryption only and is not applicable. 4. **@trpc/server `experimental_nextAppDirCaller` prototype pollution** — This is a Next.js-specific adapter feature. The project uses SolidStart, which does not use this adapter. Not applicable. --- ## Recent Security Context From internal git history (commit `26d9f8b`), the following security-related fixes were made (referenced by internal ticket IDs): | Ticket | Description | |--------|-------------| | FRE-4572 | VoicePrint auth bypass fix | | FRE-4807 | P1 security findings remediation | | FRE-5003 | JWT security hardening | | FRE-4498 | Auth bypass patch | | FRE-4500 | Rate limiting improvements | | FRE-4612 | CORS tightening | | FRE-4701 | Session token rotation | | FRE-4850 | Webhook signature verification | These represent **real security vulnerabilities** in the project's own codebase that were fixed internally. Their details are not publicly documented in CVE/GHSA format. --- ## Static Analysis Summary **Phase**: L3 (SAST — Greppable Fallback) **Date**: 2026-05-28 **Tools**: Built-in candidate scanner + targeted grep/read analysis **CodeQL**: Not available (not on PATH) **Semgrep**: Not available (not on PATH) ### Scan Results | Metric | Value | |--------|-------| | Files scanned | 730 | | Candidate files | 218 | | Candidate matches | 1,412 | | Draft findings produced | 12 | | Enriched and kept | 10 | | Enriched and dropped | 2 | | Severity distribution | 1 Critical, 3 High, 6 Medium, 1 Low | ### Built-in Rulesets Applied (Candidate Scanner) - `secret-literal` (9 matches) — Hardcoded secrets - `command-execution` (55 matches) — Shell/command invocation - `dynamic-code-execution` (12 matches) — eval/exec patterns - `raw-sql-query` (611 matches) — SQL query construction - `hidden-control-channel` (42 matches) — Auth/routing headers - `open-redirect` (2 matches) — Redirect sinks - `path-traversal-file-access` (638 matches) — Path joins - `webhook-without-obvious-signature` (6 matches) — Webhook handlers - `unsafe-html-or-template` (17 matches) — HTML injection - `ssrf-capable-request` (10 matches) — Outbound HTTP - `weak-token-or-crypto` (5 matches) — Weak randomness - `public-entrypoint` (5 matches) — Public routes ### Custom Analysis Targets (Domain Attack Research Driven) | Target | DFD/CFD Slice | Finding | |--------|---------------|---------| | CORS env var trust | CFD-1 (Auth Flow) | p4-003 (high) | | XSS via markdown rendering | DFD-2 (VoicePrint), AS-7 | p4-004 (high) | | Puppeteer SSRF | DFD-3 (Puppeteer), AS-8 | p4-002 (high) | | Stripe webhook type safety | DFD-5 (Stripe), AS-6 | p4-006 (medium) | | Return URL open redirect | C-3 (Stripe), DFD-5 | p4-010 (medium) | | superjson CVE | DFD-3 (Extension), B-4 | p4-008 (medium) | | Rate limit bypass | CFD-3 (Rate Limiting), AS-5 | p4-009 (medium) | | WebSocket Origin check | DFD-4 (WebSocket), AS-3 | p4-011 (medium) | | JWT in WS query param | DFD-4 (WebSocket), AS-3 | p4-007 (medium) | | Admin role mutation | CFD-2 (Authz Flow), AS-4 | p4-001 (critical) | | Audio path traversal | DFD-2 (VoicePrint), C-5 | p4-005 (high) | | Admin SQL pattern | DFD-1 (tRPC→ORM), B-1 | p4-012 (low) | ### Agentic Actions Audit Analyzed 2 GitHub Actions workflow files (`.github/workflows/ci.yml`, `.github/workflows/deploy.yml`). **Result**: 0 AI action instances found. No Claude Code, Gemini CLI, OpenAI Codex, or GitHub AI Inference integrations present. Standard CI/CD workflows only. --- ## CodeQL Structural Analysis **Status**: Not available — CodeQL not installed on PATH. The CodeQL extraction targets defined in Section "Phase 4 CodeQL Extraction Targets" were not executed. The following analysis was performed as a substitute: - **Entry points**: Identified 16 tRPC routers as primary entry points via manual review of `web/src/server/api/routers/` - **Sinks**: Identified Drizzle ORM queries, Puppeteer `page.setContent()`, `writeFile()`, WebSocket connections, and Stripe API calls as sinks - **Flow coverage**: 11 of 12 DFD/CFD slices were covered by targeted grep/read analysis ### Entry Points Identified (Manual) | Entry Point | Type | Trust Level | File | |-------------|------|-------------|------| | tRPC public procedures | HTTP POST /trpc | Anonymous | `web/src/server/api/routers/extension.ts` | | tRPC protected procedures | HTTP POST /trpc | Authenticated | `web/src/server/api/routers/*.ts` (15 files) | | tRPC admin procedures | HTTP POST /trpc | Admin-only | `web/src/server/api/routers/admin.ts` | | Stripe webhook | HTTP POST /api/stripe/webhook | Webhook secret | `web/src/routes/api/stripe/webhook.ts` | | WebSocket upgrade | WS upgrade /:3001 | JWT in query param | `web/src/server/websocket.ts` | | Blog post page | HTTP GET /blog/:slug | Public | `web/src/routes/blog/[slug].tsx` | | Billing return | HTTP GET /billing/return | Public (post-payment) | `web/src/routes/billing/return.tsx` | ### Sinks Identified (Manual) | Sink | Type | Risk | File | |------|------|------|------| | Drizzle `sql<>` tag | SQL execution | CVE-2026-39356 | `admin.ts:47` | | Drizzle `.update().set()` | SQL execution | Type coercion | `billing.service.ts:156` | | Puppeteer `page.setContent()` | SSRF/file access | SSRF | `generator.ts:145` | | `writeFile()` | File write | Path traversal | `storage.ts:24` | | `fetch()` to external APIs | SSRF | URL manipulation | `scan.engine.ts` | | WebSocket `ws.send()` | Data exfiltration | Auth bypass | `websocket.ts:126` | | `innerHTML` binding | XSS | HTML injection | `blog/[slug].tsx:121` | --- ## SAST Enrichment ### Candidate-to-Finding Classification Every candidate from the scanner was evaluated against the inline enrichment criteria. Below are the enriched verdicts for candidates that were elevated to draft findings. #### Kept Findings | Finding ID | Classification | Attacker Control | Trust Boundary | Reachability | Verdict | |-----------|---------------|-----------------|---------------|-------------|---------| | p4-001 | security | Admin (via SQLi/session theft) | Authz boundary (role check) | reachable | **keep** — direct privilege escalation path | | p4-002 | security | Admin (template control) | Boundary between user data and browser context | reachable | **keep** — SSRF via Puppeteer with --no-sandbox | | p4-003 | security | Env var injection (CI/CD, container) | CORS boundary | reachable | **keep** — env var controls trust boundary | | p4-004 | security | Admin (blog content) | Boundary between server data and browser | reachable | **keep** — innerHTML with unsanitized markdown | | p4-005 | security | tRPC input (userId) | Filesystem trust boundary | reachable | **keep** — path traversal via unsanitized userId | | p4-006 | security | Stripe webhook (requires secret) | Type safety boundary | reachable | **keep** — type coercion masks API changes | | p4-007 | security | Log access (proxy/server logs) | Auth boundary (JWT) | reachable | **keep** — JWT in query params is logged | | p4-008 | security | Extension local data | Local execution boundary | reachable | **keep** — vulnerable superjson version | | p4-009 | security | tRPC input (procedure path) | Rate limiting boundary | reachable | **keep** — substring matching bypass | | p4-010 | security | tRPC input (returnUrl) | Payment redirect boundary | reachable | **keep** — open redirect post-payment | | p4-011 | security | Any website (CSRF) | WebSocket Origin boundary | reachable | **keep** — no Origin validation on WS | | p4-012 | correctness | Developer (future code) | Code quality boundary | reachable | **keep** — latent SQL injection pattern | #### Dropped Candidates (Not Elevated) | Candidate Class | Reason for Drop | |----------------|----------------| | `raw-sql-query` (611 matches) | False positives — tRPC `.query()` method calls, not raw SQL | | `command-execution` (55 matches) | All in test/benchmark files, not production code | | `dynamic-code-execution` (12 matches) | SQLite `raw.exec()` calls, not code execution sinks | | `secret-literal` (9 matches) | Test data and password validation error messages | | `path-traversal-file-access` (636 dropped) | False positives from `.join("")` string concatenation, not filesystem paths | | `webhook-without-obvious-signature` (5 dropped) | Stripe webhook handler DOES have signature verification (`constructEvent()`) | | `open-redirect` (1 dropped) | `Navigate href="/admin/blog/new"` is a hardcoded internal redirect | | `weak-token-or-crypto` (1 dropped) | `Math.random()` for HTML input IDs, not cryptographic use | | `public-entrypoint` (5 dropped) | Standard public tRPC procedures, not vulnerabilities | | `hidden-control-channel` (3 dropped) | Test file middleware definitions, not production code | ### Entry Points Not in Phase 3 DFD Slices | Entry Point | Phase 3 DFD Coverage | Gap | |-------------|---------------------|-----| | Blog post rendering (`blog/[slug].tsx`) | Partial (AS-7 mentioned XSS risk) | Detailed innerHTML analysis added in p4-004 | | Billing return page (`billing/return.tsx`) | Partial (AS-6 mentioned redirect) | Detailed open redirect analysis added in p4-010 | ### Sinks Not Mapped to High-Risk Flows | Sink | Unmodeled Risk | Finding | |------|---------------|---------| | `page.setContent()` (Puppeteer) | Not in DFD-3 as SSRF sink | p4-002 | | `writeFile()` with userId | Not in DFD-2 as path traversal sink | p4-005 | | `ws.send()` with Origin bypass | Not in DFD-4 as auth bypass sink | p4-011 | ### Batching, Throttling, and Coverage Tradeoffs 1. **CodeQL/Semgrep unavailable**: Both CodeQL and Semgrep were not installed on PATH. Analysis fell back to targeted grep+read patterns focused on highest-score candidates and domain attack research targets. This means interprocedural data flow analysis was not performed — findings are based on static pattern matching and manual code review. 2. **`raw-sql-query` bulk drop**: 611 `raw-sql-query` candidates were dropped without individual review because they are all tRPC `.query()` method calls (framework methods), not raw SQL execution. This is a high-confidence drop based on pattern analysis. 3. **Test file exclusion**: All candidates in test files (`test_*.py`, `*_test.go`, `*_spec.rb`, `*.test.ts`, `*.test.tsx`) were evaluated and dropped as test-only. This is consistent with the enrichment drop criteria. 4. **Coverage completeness**: All 12 DFD/CFD slices from Phase 3 were addressed by at least one finding or a documented drop rationale. The one gap (p4-012) is a latent risk pattern that requires future code changes to become exploitable.