141 lines
6.9 KiB
Markdown
141 lines
6.9 KiB
Markdown
# 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
|