# Balanced Probe Summary: Kordant `web/` **Status**: complete **Phase**: L4 (Lite Probe) **Date**: 2026-05-28 **Commit**: 26d9f8b050969dfaa2c9dfb714a872160b7db382 --- ## Probe Execution ### Scope The L4 probe focused on the `web/` directory of the Kordant monorepo — the primary web application built on SolidStart + tRPC + Drizzle ORM. The probe examined 16 tRPC routers, the Stripe webhook handler, WebSocket server, voiceprint audio pipeline, darkwatch external API scanner, report generator (Puppeteer), and middleware pipeline. ### Knowledge Base Integration The Phase 3 knowledge base was used to identify highest-impact attack surface slices: 1. **Stripe Webhook Processing** (DFD-5, TB-3) — CRITICAL in KB, verified as MEDIUM in this probe 2. **tRPC → Drizzle ORM** (DFD-1, TB-2) — CVE-2026-39356 in KB, verified as currently safe but with latent risks 3. **VoicePrint Audio Pipeline** (DFD-2, TB-9) — CRITICAL in KB, verified as MEDIUM 4. **WebSocket** (DFD-4, TB-5) — HIGH in KB, verified as MEDIUM 5. **Browser Extension** (DFD-3, TB-6) — CRITICAL in KB, but KB correctly identified server does NOT use superjson ### Files Analyzed - 25+ source files read in full or partial - 4 router files (admin, billing, extension, voiceprint) - 3 service files (billing, voiceprint, darkwatch) - 3 core infrastructure files (middleware, websocket, ratelimit) - 6 schema files (billing, voiceprint, extension, darkwatch, reports, user) - 1 generator file (reports/generator.ts) - 1 JWT auth file ### Hypothesis Generation 12 hypotheses were generated covering: - Stripe webhook replay and type coercion - Return URL open redirect - VoicePrint resource exhaustion - WebSocket JWT leakage - Admin role unrestricted values - Extension no-op endpoints - SQL injection vectors (blog router `sql<>` tags) - Rate limit bypass - Puppeteer SSRF - Watchlist item type injection ### Hypothesis Verification Each hypothesis was verified against actual code with file:line evidence. 7 findings were written as drafts; 5 were rejected as SAFE or LOW-IMPACT. --- ## Findings Summary | Draft ID | Title | Severity | Status | |----------|-------|----------|--------| | L4-001 | Stripe Webhook Replay → Partial Duplicate Subscription Protection | MEDIUM | VALIDATED | | L4-002 | Return URL Open Redirect via Stripe Checkout | MEDIUM | VALIDATED | | L4-003 | VoicePrint Resource Exhaustion via Unbounded Audio Upload | MEDIUM | VALIDATED | | L4-004 | WebSocket JWT Leakage via Query Parameter | MEDIUM | VALIDATED | | L4-005 | Webhook Type Coercion → Data Corruption | MEDIUM | VALIDATED | | L4-006 | Admin `userUpdateRole` — Unrestricted Role Value | LOW | VALIDATED | | L4-007 | Public Extension Endpoints — No-Op and Unbounded Input | LOW | VALIDATED | ### Rejected Hypotheses (SAFE or LOW-IMPACT) - **SQL injection via blog router `sql<>` tag**: SAFE — Drizzle parameterizes values in `sql<>` template tags - **Puppeteer SSRF via `page.setContent`**: LOW — `setContent` loads HTML as document, no external navigation; `--no-sandbox` weakens isolation but HTML is generated server-side - **Admin role escalation via SQL injection**: NOT FOUND — no SQL injection vector exists in admin procedures - **Rate limit bypass via path heuristic**: NOT A VULNERABILITY — the heuristic is over-protective (flags legitimate sensitive paths) - **Watchlist item type injection**: SAFE — `picklist()` validates type, and scan engine uses `encodeURIComponent` --- ## Coverage Summary ### Entry Points Covered | Entry Point | Covered? | Finding IDs | |-------------|----------|-------------| | Stripe webhook (`/api/stripe/webhook`) | YES | L4-001, L4-005 | | Billing tRPC procedures | YES | L4-002 | | VoicePrint tRPC procedures | YES | L4-003 | | WebSocket (`ws://:3001`) | YES | L4-004 | | Extension public procedures | YES | L4-007 | | Admin procedures | YES | L4-006 | | Blog public procedures | YES (verified safe) | — | | DarkWatch tRPC procedures | PARTIAL | — | | Reports tRPC procedures | PARTIAL | — | | User procedures | PARTIAL | — | | Middleware (CORS, CSP, Clerk) | PARTIAL | — | | Rate limiting | YES | — | ### Trust Boundary Crossings Analyzed | Boundary | File | Findings | |----------|------|----------| | TB-1: Internet → Web (tRPC) | `middleware.ts`, `utils.ts` | L4-002, L4-006, L4-007 | | TB-3: tRPC → Stripe | `billing.service.ts` | L4-001, L4-005 | | TB-5: WebSocket → ws | `websocket.ts` | L4-004 | | TB-9: tRPC → VoicePrint Storage | `voiceprint.service.ts` | L4-003 | ### Uncovered Areas - **DarkWatch scan engine**: External API calls (HIBP, SecurityTrails, Censys, Shodan) were read but no vulnerabilities were found — all use `encodeURIComponent` and circuit breakers - **Report generation (Puppeteer)**: `page.setContent` was analyzed — HTML is server-generated, not user-controlled - **Middleware pipeline**: CORS and CSP were analyzed — CSP has `'unsafe-inline'` and `'unsafe-eval'` which are known weaknesses but not exploitable without XSS - **Drizzle ORM SQL injection**: All `sql<>` usages were verified as safe (Drizzle parameterizes values) - **Clerk auth integration**: Clerk handles auth — no vulnerabilities in the integration layer --- ## Risk Assessment ### Overall Risk: MEDIUM The Kordant web application has a well-structured security model with Clerk-based authentication, tRPC procedure type enforcement, and rate limiting. However, several areas need attention: **High-priority fixes (MEDIUM severity)**: 1. Add event ID deduplication to Stripe webhook handler 2. Validate `returnUrl` against an allowlist 3. Add maximum audio size limits to VoicePrint endpoints 4. Move WebSocket JWT from query parameter to header 5. Replace type coercion in webhook handler with proper type guards **Low-priority fixes (LOW severity)**: 1. Add role validation to `userUpdateRole` 2. Add length limits to extension public endpoints 3. Implement or remove the no-op `reportPhishing` endpoint ### Dependencies with Known CVEs - **drizzle-orm 0.45.2**: CVE-2026-39356 (SQL injection) — not currently exploitable in this codebase (all `sql<>` tags use parameterized values) - **ws 8.21.0**: CVE-2026-45736, CVE-2024-37890 — WebSocket server uses `ws`, but the vulnerabilities require specific attack conditions - **valibot 0.29.0**: CVE-2025-66020 (ReDoS) — emoji validation regex vulnerable, but no emoji-specific schemas found - **superjson 2.2.1** (browser extension): CVE-2022-23631 — prototype pollution, but server does NOT use superjson --- ## Next Steps for Deeper Phases 1. **L5/L6**: Test Stripe webhook replay with actual Stripe test API 2. **L5/L6**: Verify WebSocket JWT leakage by checking actual log configurations 3. **L5/L6**: Load test VoicePrint endpoints with large payloads 4. **L6**: Audit CSP effectiveness — `'unsafe-inline'` and `'unsafe-eval'` weaken XSS protection 5. **L7**: Supply chain analysis of npm dependencies 6. **L8**: Native app security (iOS/Android) — separate codebases 7. **L9**: Infrastructure and deployment security