# 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 += \`
${line.slice(3)}
\``) 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`) 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)`.
- **Root Cause:** The webhook handler at `web/src/server/services/billing.service.ts:173, 196, 207` casts event data to `Record`, 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.1–2.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*