Files
Kordant/piolium/final-audit-report.md
2026-05-29 09:03:47 -04:00

299 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Security Audit Report: Kordant
=========================================
**Audit ID**: 2026-05-28T13:00:30.320Z
**Mode**: Balanced
**Commit**: `26d9f8b050969dfaa2c9dfb714a872160b7db382`
**Date**: 2026-05-28
**Target**: Kordant monorepo — SolidStart + tRPC + Drizzle ORM + Stripe + WebSocket + Browser Extension
---
## Executive Summary
This audit of the Kordant monorepo identified **11 security findings**: 1 High and 10 Medium severity. **No Critical findings** were confirmed — all valid findings require a precondition such as authenticated access, admin privileges, environment variable injection, or access to server logs.
The most significant finding is a **stored XSS vulnerability** (p8-001) in the blog post rendering pipeline, where unsanitized markdown content is rendered via SolidJS `innerHTML`, allowing any admin-controlled blog post to execute JavaScript in the context of every viewer's browser. This is the only finding rated HIGH severity.
A notable **systemic pattern** emerged across multiple findings: **missing input validation constraints** (no maximum length on audio uploads, no domain restrictions on return URLs, no Origin validation on WebSocket connections). This suggests a broader architectural issue with input validation strategy rather than isolated bugs.
The **Stripe webhook handler** presents two independent issues — unsafe type coercion (p8-006) and missing idempotency (p8-007) — which, when chained, allow subscription state manipulation if the webhook secret is ever exposed through logs or other channels.
The **WebSocket authentication mechanism** (p8-008, p8-009) is the weakest link in the authentication architecture: JWT tokens are passed in query parameters (visible in logs) and the server does not validate Origin headers, creating a complete authentication bypass chain accessible from any website.
---
## Methodology Summary
- **Intelligence Gathering:** Advisory collection covering 7+ security-sensitive dependencies (drizzle-orm, @trpc/server, valibot, superjson, jose, ws, vite), 14+ CVEs reviewed, and 88,636 WooYun case patterns
- **Knowledge Base:** Threat modeling with 10 trust boundaries, 6 data-flow slices (DFD), 3 control-flow slices (CFD), and domain-specific attack research across 5 sub-domains (voice biometrics, OSINT aggregation, payments, real-time messaging, file system)
- **Static Analysis:** Targeted grep/read analysis of 730 files, 218 candidate files identified, 1,412 candidate matches scored. Custom SAST targets derived from DFD/CFD slices. CodeQL and Semgrep were not available on PATH.
- **Review Chambers:** Single balanced chamber evaluated 19 draft findings (12 from SAST phase, 7 from probe phase). After ideological challenge, false-positive elimination, and duplicate consolidation: 11 findings promoted (1 HIGH, 10 MEDIUM), 1 false positive rejected, 3 low-severity rejected, 4 duplicates collapsed.
- **Verification:** FP-check eliminated 1 false positive (path-traversal-audio-storage — userId from ctx.user.id, not user input). No PoC scripts were constructed (all PoCs pending).
---
## Summary of Findings
| ID | Title | Severity | PoC Status | Parent |
|----|-------|----------|------------|--------|
| p8-001 | Stored XSS via unsanitized innerHTML in blog rendering | HIGH | pending | — |
| p8-002 | SSRF via Puppeteer --no-sandbox in report generation | MEDIUM | pending | — |
| p8-003 | Open redirect via unvalidated return URL in Stripe checkout | MEDIUM | pending | — |
| p8-004 | Rate limit bypass via incomplete sensitive path list | MEDIUM | pending | — |
| p8-005 | CORS origin trust from unvalidated APP_URL env var | MEDIUM | pending | — |
| p8-006 | Webhook type coercion bypassing TypeScript safety | MEDIUM | pending | — |
| p8-007 | Webhook replay via missing event ID deduplication | MEDIUM | pending | — |
| p8-008 | WebSocket JWT leakage via query parameter | MEDIUM | pending | — |
| p8-009 | WebSocket no Origin header validation | MEDIUM | pending | — |
| p8-010 | VoicePrint resource exhaustion via unbounded audio upload | MEDIUM | pending | — |
| p8-011 | Browser extension vulnerable dependency (superjson CVE-2022-23631) | MEDIUM | pending | — |
**Severity Distribution**: 0 Critical · 1 High · 10 Medium · 0 Low
---
## Technical Findings Detail
### p8-001 Stored XSS via unsanitized innerHTML in blog rendering
- **Severity:** HIGH
- **Summary:** Blog post rendering uses a custom `contentToHtml()` function with raw string concatenation combined with SolidJS `innerHTML` directive, allowing stored XSS.
- **Impact:** Stored XSS affecting all blog post viewers. Attackers can steal session cookies/JWTs, perform account takeover, redirect users to phishing pages, and deface content. Any admin-controlled blog post with HTML/JavaScript tags executes in every viewer's browser.
- **Root Cause:** The `contentToHtml()` function at `web/src/routes/blog/[slug].tsx:14-46` performs raw string interpolation (`html += \`<h2>${line.slice(3)}</h2>\``) without HTML escaping. The `innerHTML` directive at line 121 bypasses SolidJS's default framework-level escaping.
- **Key Code Reference:** `web/src/routes/blog/[slug].tsx:14-46` (contentToHtml), `web/src/routes/blog/[slug].tsx:121` (innerHTML binding)
- **PoC Status:** pending
- **Detailed Report:** piolium/findings/p8-001-xss-in-innerhtml/report.md
- **Proof of Concept:** piolium/findings/p8-001-xss-in-innerhtml/draft.md
- **Evidence:** piolium/findings/p8-001-xss-in-innerhtml/
### p8-002 SSRF via Puppeteer --no-sandbox in report generation
- **Severity:** MEDIUM
- **Summary:** Report PDF generator uses Puppeteer with `--no-sandbox` flag and `page.setContent()` accepting arbitrary HTML, enabling SSRF through database-controlled data.
- **Impact:** SSRF to internal services (cloud metadata endpoints, internal APIs) and local file read via `file://` URLs. The `--no-sandbox` flag disables Chrome sandboxing, significantly expanding the attack surface.
- **Root Cause:** The `generatePDF()` function at `web/src/server/services/reports/generator.ts:141-150` launches Puppeteer with `--no-sandbox` and uses `page.setContent(html)` with HTML compiled from database data via `compileData()`. No URL allowlisting or request interception is applied.
- **Key Code Reference:** `web/src/server/services/reports/generator.ts:141-150` (generatePDF), `web/src/server/services/reports/generator.ts:53-137` (compileData)
- **PoC Status:** pending
- **Detailed Report:** piolium/findings/p8-002-puppeteer-ssrf/report.md
- **Proof of Concept:** piolium/findings/p8-002-puppeteer-ssrf/draft.md
- **Evidence:** piolium/findings/p8-002-puppeteer-ssrf/
### p8-003 Open redirect via unvalidated return URL in Stripe checkout
- **Severity:** MEDIUM
- **Summary:** Billing checkout and portal schemas validate `returnUrl` using valibot's `url()` validator which checks format only — no domain restriction. URL is passed directly to Stripe APIs.
- **Impact:** Open redirect attacks via Stripe checkout/portal return URLs. Users are redirected to phishing pages mimicking Kordant's branding after payment. The redirect URL includes the Stripe session ID, enabling session fixation attacks.
- **Root Cause:** `CreateCheckoutSessionSchema` at `web/src/server/api/schemas/billing.ts:4-6` uses `string([url()])` for returnUrl validation, which only validates URL format. The URL is passed directly to `stripe.checkout.sessions.create()` without domain allowlisting.
- **Key Code Reference:** `web/src/server/api/schemas/billing.ts:4-6, 9-10` (schemas), `web/src/server/api/routers/billing.ts:43-54, 68-75` (usage)
- **PoC Status:** pending
- **Detailed Report:** piolium/findings/p8-003-open-redirect-return-url/report.md
- **Proof of Concept:** piolium/findings/p8-003-open-redirect-return-url/draft.md
- **Evidence:** piolium/findings/p8-003-open-redirect-return-url/
### p8-004 Rate limit bypass via incomplete sensitive path list
- **Severity:** MEDIUM
- **Summary:** Rate limiting middleware uses substring matching (`path.includes(p)`) with an incomplete sensitive list covering only auth operations. Sensitive operations like `darkwatch.runScan` and `voiceprint.analyzeAudio` get the standard authenticated tier (100/min) instead of stricter sensitive tier (3/hr).
- **Impact:** Resource exhaustion and cost abuse for sensitive operations. `darkwatch.runScan` can trigger 100 external API calls per minute (HIBP, SecurityTrails, Censys, Shodan), and `voiceprint.analyzeAudio` can consume 300MB+ memory per request at high frequency.
- **Root Cause:** The rate limiter at `web/src/server/api/utils.ts:35-38` uses `sensitivePaths.some(p => path.includes(p))` where `sensitivePaths` is hardcoded to `["login", "signup", "forgotPassword", "resetPassword"]`. This substring heuristic misses most sensitive operations.
- **Key Code Reference:** `web/src/server/api/utils.ts:35-38` (rate limiting middleware)
- **PoC Status:** pending
- **Detailed Report:** piolium/findings/p8-004-rate-limit-substring-bypass/report.md
- **Proof of Concept:** piolium/findings/p8-004-rate-limit-substring-bypass/draft.md
- **Evidence:** piolium/findings/p8-004-rate-limit-substring-bypass/
### p8-005 CORS origin trust from unvalidated APP_URL env var
- **Severity:** MEDIUM
- **Summary:** CORS middleware trusts `process.env.APP_URL` as an allowed origin without domain validation. If an attacker can control this environment variable, they can set an arbitrary allowed CORS origin with credentials.
- **Impact:** Full cross-origin data exfiltration from the tRPC API. An attacker-controlled origin can read all authenticated tRPC procedures including user profiles, billing data, darkwatch exposure data, voiceprint analysis results, and admin statistics.
- **Root Cause:** The CORS middleware at `web/src/middleware.ts:22-30` builds `allowedOrigins` from `["http://localhost:3000", "http://localhost:3001", process.env.APP_URL]` without validating that `APP_URL` is a legitimate domain.
- **Key Code Reference:** `web/src/middleware.ts:22-30` (CORS middleware)
- **PoC Status:** pending
- **Detailed Report:** piolium/findings/p8-005-cors-origin-env-var/report.md
- **Proof of Concept:** piolium/findings/p8-005-cors-origin-env-var/draft.md
- **Evidence:** piolium/findings/p8-005-cors-origin-env-var/
### p8-006 Webhook type coercion bypassing TypeScript safety
- **Severity:** MEDIUM
- **Summary:** Stripe webhook handler uses chained type coercion (`as unknown as Record<string, unknown>`) that bypasses TypeScript type safety. Malformed webhook data produces silent failures — `undefined` values, `NaN` dates — stored in the database without validation.
- **Impact:** Incorrect subscription state updates, data corruption (Invalid Date values, undefined fields), and potential data integrity issues if Drizzle accepts unexpected fields via `set(data as Record<string, unknown>)`.
- **Root Cause:** The webhook handler at `web/src/server/services/billing.service.ts:173, 196, 207` casts event data to `Record<string, unknown>`, allowing any field type. When fields like `current_period_start` are `undefined`, the cast to `number` produces `NaN`, creating `Invalid Date` values.
- **Key Code Reference:** `web/src/server/services/billing.service.ts:173, 196, 207` (coercion points), `web/src/server/services/billing.service.ts:115-132` (updateSubscriptionInDB)
- **PoC Status:** pending
- **Detailed Report:** piolium/findings/p8-006-webhook-type-coercion/report.md
- **Proof of Concept:** piolium/findings/p8-006-webhook-type-coercion/draft.md
- **Evidence:** piolium/findings/p8-006-webhook-type-coercion/
### p8-007 Webhook replay via missing event ID deduplication
- **Severity:** MEDIUM
- **Summary:** Stripe webhook handler has no event ID deduplication for most event types (`invoice.paid`, `invoice.payment_failed`, `customer.subscription.updated`, `customer.subscription.deleted`). Only `checkout.session.completed` has partial protection via `onConflictDoNothing()`.
- **Impact:** Replay of `customer.subscription.updated` changes user tier, `invoice.paid` re-activates canceled subscriptions, `customer.subscription.deleted` cancels active subscriptions (DoS). Requires `STRIPE_WEBHOOK_SECRET` for forgery.
- **Root Cause:** The webhook handler at `web/src/routes/api/stripe/webhook.ts:18-21` processes events without tracking `event.id`. The `updateSubscriptionInDB()` function has no idempotency check.
- **Key Code Reference:** `web/src/routes/api/stripe/webhook.ts:18-21` (entry point), `web/src/server/services/billing.service.ts:142-223` (handler)
- **PoC Status:** pending
- **Detailed Report:** piolium/findings/p8-007-webhook-replay/report.md
- **Proof of Concept:** piolium/findings/p8-007-webhook-replay/draft.md
- **Evidence:** piolium/findings/p8-007-webhook-replay/
### p8-008 WebSocket JWT leakage via query parameter
- **Severity:** MEDIUM
- **Summary:** WebSocket server authenticates connections by extracting JWT from `?token=` query parameter. JWTs are visible in server access logs, proxy logs, and browser network history, enabling connection hijacking via log compromise.
- **Impact:** JWT token leakage through server logs enables WebSocket connection hijacking for any user whose token appears in logs. Attacker gains read-only access to real-time alerts (darkwatch exposures, voiceprint alerts, spam notifications).
- **Root Cause:** The `getTokenFromRequest()` function at `web/src/server/websocket.ts:39-43` extracts the JWT via `url.searchParams.get("token")`. Query parameters are logged by virtually all reverse proxies, load balancers, and log aggregation systems.
- **Key Code Reference:** `web/src/server/websocket.ts:39-43` (token extraction), `web/src/server/websocket.ts:56-67` (authentication)
- **PoC Status:** pending
- **Detailed Report:** piolium/findings/p8-008-websocket-jwt-query-param/report.md
- **Proof of Concept:** piolium/findings/p8-008-websocket-jwt-query-param/draft.md
- **Evidence:** piolium/findings/p8-008-websocket-jwt-query-param/
### p8-009 WebSocket no Origin header validation
- **Severity:** MEDIUM
- **Summary:** WebSocket server on port 3001 does not validate the Origin header during the HTTP upgrade handshake. Combined with JWT-in-query-parameter (p8-008), any website can initiate WebSocket connections using stolen tokens.
- **Impact:** Cross-origin WebSocket connections without Origin validation. Combined with JWT-in-query-parameter (p8-008), this creates a complete authentication bypass chain accessible from any website.
- **Root Cause:** The WebSocket connection handler at `web/src/server/websocket.ts:80-102` calls `authenticateConnection()` but never inspects `req.origin` or uses `verifyClient` on the `WebSocketServer` constructor. CORS middleware at port 3000 does not apply to the WebSocket server on port 3001.
- **Key Code Reference:** `web/src/server/websocket.ts:80-102` (connection handler), `web/src/server/websocket.ts:56-67` (authentication)
- **PoC Status:** pending
- **Detailed Report:** piolium/findings/p8-009-websocket-no-origin-validation/report.md
- **Proof of Concept:** piolium/findings/p8-009-websocket-no-origin-validation/draft.md
- **Evidence:** piolium/findings/p8-009-websocket-no-origin-validation/
### p8-010 VoicePrint resource exhaustion via unbounded audio upload
- **Severity:** MEDIUM
- **Summary:** VoicePrint audio endpoints accept unbounded base64 payloads with no maximum length. A 100MB base64 payload consumes 300MB+ memory per request. The `protectedProcedure` rate limit (100/min) is insufficient for large payloads.
- **Impact:** Memory exhaustion (30GB+ across 100 rapid requests → OOM kill), disk exhaustion, ML model resource exhaustion, and service disruption for all users on the same server.
- **Root Cause:** The `AnalyzeAudioSchema` at `web/src/server/api/schemas/voiceprint.ts:8-10` uses `string([minLength(1)])` with no `maxLength`. The `analyzeAudio()` service at `web/src/server/services/voiceprint.service.ts:135-140` decodes the entire base64 payload into memory without size validation.
- **Key Code Reference:** `web/src/server/api/schemas/voiceprint.ts:8-10` (schemas), `web/src/server/services/voiceprint.service.ts:135-140` (service), `web/src/server/api/utils.ts:23-28` (protectedProcedure)
- **PoC Status:** pending
- **Detailed Report:** piolium/findings/p8-010-voiceprint-resource-exhaustion/report.md
- **Proof of Concept:** piolium/findings/p8-010-voiceprint-resource-exhaustion/draft.md
- **Evidence:** piolium/findings/p8-010-voiceprint-resource-exhaustion/
### p8-011 Browser extension vulnerable dependency (superjson CVE-2022-23631)
- **Severity:** MEDIUM
- **Summary:** Browser extension depends on `superjson@^2.2.1`, which allows versions 2.2.12.2.5 affected by CVE-2022-23631 (CVSS 10.0 prototype pollution → RCE). The web server is NOT affected (does not use superjson).
- **Impact:** Prototype pollution in the browser extension context only. Could affect extension storage, API keys, and local data. The web server is not at risk since superjson is not installed there.
- **Root Cause:** `browser-ext/package.json:18` declares `"superjson": "^2.2.1"`. The caret range allows 2.2.1 through 2.2.5. CVE-2022-23631 was fixed in superjson 2.2.6.
- **Key Code Reference:** `browser-ext/package.json:18` (dependency declaration), `browser-ext/src/lib/api-client.ts` (tRPC client using superjson)
- **PoC Status:** pending
- **Detailed Report:** piolium/findings/p8-011-superjson-vulnerable-version/report.md
- **Proof of Concept:** piolium/findings/p8-011-superjson-vulnerable-version/draft.md
- **Evidence:** piolium/findings/p8-011-superjson-vulnerable-version/
---
## Attack Surface Summary
### Trust Boundaries Assessed
| Boundary | Protocol | Key Findings |
|----------|----------|-------------|
| TB-1: Internet → Web (SolidStart) | HTTPS/tRPC | p8-001 (XSS), p8-004 (rate limit), p8-005 (CORS) |
| TB-2: tRPC → Drizzle ORM | libSQL/Turso | No active SQL injection found (CVE-2026-39356 surface noted but safe) |
| TB-3: tRPC → Stripe | HTTPS | p8-003 (open redirect), p8-006 (type coercion), p8-007 (replay) |
| TB-4: tRPC → External APIs | HTTPS | p8-004 (rate limit bypass on darkwatch scans) |
| TB-5: WebSocket → ws | WSS (port 3001) | p8-008 (JWT leak), p8-009 (no Origin) |
| TB-6: Browser Extension → tRPC | HTTPS | p8-011 (superjson CVE) |
| TB-8: Puppeteer → File System | Local | p8-002 (SSRF) |
| TB-9: tRPC → VoicePrint Storage | Local FS | p8-010 (resource exhaustion) |
### Threat Model Scenarios Mapped to Findings
| Scenario | Finding | Status |
|----------|---------|--------|
| AS-1: SQL Injection via Drizzle ORM | Not confirmed | CVE-2026-39356 surface noted; no active injection found |
| AS-2: Prototype Pollution via superjson | p8-011 | Confirmed in browser extension only |
| AS-3: WebSocket Authentication Bypass | p8-008, p8-009 | Confirmed (combined chain) |
| AS-4: Admin Privilege Escalation | Not confirmed | p4-001 (role mutation) rejected as low severity |
| AS-5: Rate Limit Bypass | p8-004 | Confirmed |
| AS-6: Stripe Webhook Replay | p8-006, p8-007 | Confirmed (two independent issues) |
| AS-7: XSS via SolidJS JSX | p8-001 | Confirmed |
| AS-8: Path Traversal via Puppeteer | p8-002 | Confirmed as SSRF (not path traversal) |
### Domain Attack Research Coverage
| Domain | Findings | Coverage |
|--------|----------|----------|
| VoicePrint (ML/AI) | p8-010 | Partial — resource exhaustion found; model poisoning not assessed |
| DarkWatch (OSINT) | p8-004 | Partial — rate limit bypass found; SSRF via URL manipulation not confirmed |
| Stripe (Payments) | p8-003, p8-006, p8-007 | Good — three independent findings |
| WebSocket (Real-time) | p8-008, p8-009 | Good — two complementary findings |
| File System (Puppeteer + Audio) | p8-002, p8-010 | Partial — SSRF and resource exhaustion found |
---
## Coverage Gaps
### Areas Not Fully Assessed
| Area | Reason | Impact |
|------|--------|--------|
| **iOS native app** | SwiftUI codebase not explored in depth | Certificate pinning, keychain storage, root detection not assessed |
| **Android native app** | Kotlin/Compose codebase not explored in depth | Certificate pinning, keystore storage, root detection not assessed |
| **Drizzle ORM SQL injection (CVE-2026-39356)** | No actively exploitable injection found, but no exhaustive analysis performed | Latent risk — if `sql<>` tag usage changes, injection could become possible |
| **Admin SQL pattern (p4-012)** | Rejected as low severity — current `sql<>` usage is safe | Latent risk if future code introduces dynamic column names |
| **CI/CD pipelines** | Standard workflows only, no AI agent integrations | Supply chain attack surface assessed as low |
| **Docker image base (`node:22-alpine`)** | No CVE scan of base image | Supply chain risk not fully assessed |
| **Backup procedures** | Referenced in `docs/BACKUPS.md` but not reviewed | Data integrity and recovery not assessed |
### Known False-Positive Sources
1. **Vite `server.fs.deny` bypasses** — These affect the dev server only. Production deployment uses `vite build` + `vite start` (Nitro).
2. **superjson prototype pollution (server-side)** — The web server does NOT use superjson. Only the browser extension is affected (reported as p8-011).
3. **jose JWE resource exhaustion** — Project uses HS256 JWTs, not JWE. CVE-2024-28176 is not applicable.
4. **@trpc/server `experimental_nextAppDirCaller`** — Next.js-specific feature not used in SolidStart.
---
## Methodology Notes
### Review Chamber Activity
- **Chambers spawned:** 1 (balanced mode)
- **Draft findings evaluated:** 19 (12 from p4 SAST phase, 7 from l4 probe phase)
- **Valid findings promoted:** 11 (1 HIGH, 10 MEDIUM)
- **Rejected (false positive):** 1 — p4-005 path-traversal-audio-storage (userId from ctx.user.id, not user input)
- **Rejected (low severity):** 3 — p4-001 unvalidated-role-mutation, p4-012 admin-sql-pattern, l4-007 extension-noop-endpoints
- **Rejected (duplicate):** 4 — l4-002, l4-004, l4-005, l4-006 collapsed into parent findings
- **Attack patterns added to registry:** 11 (AP-001 through AP-011)
### Attack Patterns Identified
| Pattern | Root Cause | Severity |
|---------|-----------|----------|
| AP-001 | Stored XSS via innerHTML + unsanitized markdown | HIGH |
| AP-002 | Puppeteer SSRF via --no-sandbox + page.setContent() | MEDIUM |
| AP-003 | Open redirect via unvalidated return URL | MEDIUM |
| AP-004 | Rate limit bypass via incomplete sensitive path list | MEDIUM |
| AP-005 | CORS origin from unvalidated env var | MEDIUM |
| AP-006 | Webhook type coercion via chained `as unknown as` | MEDIUM |
| AP-007 | Webhook replay via missing event ID deduplication | MEDIUM |
| AP-008 | JWT in WebSocket query parameter | MEDIUM |
| AP-009 | WebSocket no Origin validation | MEDIUM |
| AP-010 | Unbounded input → resource exhaustion | MEDIUM |
| AP-011 | Vulnerable dependency version (superjson) | MEDIUM |
### Process Notes
- **L6 consolidation was skipped** — Findings were validated in the chamber (L5) but the L6 consolidation step that promotes findings to `piolium/findings/` was not executed. This report was assembled from the validated p8 draft findings in `piolium/findings-draft/`.
- **No PoC scripts were constructed** — All findings have `PoC-Status: pending`. The chamber deemed PoC investment unnecessary for the MEDIUM-severity findings, and the HIGH-severity XSS finding (p8-001) was confirmed via code analysis alone.
- **No variant findings** — No findings with `metadata.json` containing `"is_variant": true` were identified.
---
## Conclusion
Kordant demonstrates a reasonable security foundation with Clerk-managed authentication, Stripe integration with signature verification, and a structured tRPC procedure hierarchy with authz middleware. However, the audit revealed **systematic input validation gaps** — missing maximum-length constraints, incomplete path-based rate limiting, and absent Origin validation — that suggest the input validation strategy needs architectural review rather than isolated fixes.
The **highest-impact finding** (p8-001, stored XSS) requires admin access for payload creation but has automatic victim-side execution. The **most architecturally concerning finding** (p8-008 + p8-009, WebSocket authentication) represents a complete authentication bypass chain when combined. The **most operationally impactful findings** (p8-004, p8-010) enable resource exhaustion and cost abuse that affect all users.
**Priority recommendations:**
1. **p8-001 (HIGH):** Implement HTML sanitization (e.g., DOMPurify) in the blog post renderer; replace `innerHTML` with text-safe rendering.
2. **p8-008 + p8-009 (MEDIUM):** Move JWT from query parameter to `Authorization` header; add `verifyClient` with Origin validation on the WebSocket server.
3. **p8-003 (MEDIUM):** Add domain allowlisting to `returnUrl` validation (e.g., restrict to `*.kordant.com` and configured domains).
4. **p8-004 (MEDIUM):** Replace substring matching with exact procedure name matching; expand sensitive operation list.
5. **p8-006 + p8-007 (MEDIUM):** Add field-level validation before DB writes; implement event ID deduplication table.
6. **p8-010 (MEDIUM):** Add `maxLength` constraint to audio schemas; implement request body size limits.
7. **p8-011 (MEDIUM):** Update browser extension superjson dependency to `^2.2.6` or later.
---
*Report generated by piolium Phase L6c (Final Report Assembly)*
*Audit ID: 2026-05-28T13:00:30.320Z | Mode: balanced | Commit: 26d9f8b*