# Local prototype security review — 9 October 2026

Scope: source review and focused regression checks. This is not an independent penetration test or a guarantee of no vulnerabilities.

## Present safeguards

- Listener bound to 127.0.0.1; exact Host allowlist for localhost/127.0.0.1 with the actual server port.
- Origin must match the request Host when provided. Cross-site fetches rejected. No permissive CORS.
- Random process-level request token required for POST; constant-time comparison. This is CSRF defense, not user authentication.
- Fixed static file allowlist; SQLite, Git history and project documents are not served.
- Text rendered with textContent; no report-controlled HTML insertion. CSP and frame/resource restrictions.
- Parameterized SQLite queries. A server-owned draft, explicit review and operator name required for confirmation.
- Report schema checks, exact asset-linked quotes, unknown/duplicate asset rejection; no shell execution of reports.
- 80 KiB JSON body cap, 8 MiB audio cap, 12,000-character report cap; client read timeout. Unsupported transfer encoding / duplicate Content-Length rejected.
- Up to two AI requests concurrently, ten per minute per server process. These limits do not cap the total spending of a long-running session or all HTTP threads.
- OpenAI key read from server environment, never returned in config or errors. Upstream response bodies and credentials are not exposed.
- DNS/TLS/timeout/connectivity errors distinguished using sanitized error codes. Provider failures produce 502/504, not input-validation 400. TLS verification remains enabled.

## Boundaries and remaining work

Local processes and users on the same computer are trusted by this prototype. There is no login, session-bound ownership, role separation, tenant isolation or verified identity. The operator name is self-declared. SQLite is not application-level encrypted. There is no cryptographically signed audit trail, backup policy or data retention management. A global request token does not replace these features.

HTTP connection/thread resource exhaustion is not fully addressed. Audio MIME type is declared by the client; this is not complete file-format validation. AI extraction can misclassify meaning even with a genuine quote; prompt instructions are not a guarantee against prompt injection. Every handover requires human review, and physical inspection is not proved.

Python's http.server is not a production server. Do not change the binding to 0.0.0.0 or directly publish this local server. A public service needs a production web stack, HTTPS, authenticated sessions, authorization for each record, per-user limits and operational logging/backup controls. Those are not implemented by these localhost safeguards.

## Architecture for a public service

Browser → HTTPS application with authenticated session → ownership/role checks → report service → AI adapter with budgets → database with operator/organization ownership. Keep keys in the server secret store. Queue long AI work and save structured audit events. The current UI and evidence validation can be reused; the HTTP/security/session layer needs a separate implementation and review.

References: https://docs.python.org/3/library/http.server.html and https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html.

## Connectivity diagnosis without a key

On the user's laptop, not on another machine:

```bash
curl --connect-timeout 10 --max-time 20 -sS -o /dev/null -w 'HTTP %{http_code}\n' https://api.openai.com/v1/models
```

No Authorization header is sent. HTTP 401 typically confirms DNS/TLS and an HTTP response from the API, not validity of a key or success of the analysis request. A curl error / HTTP 000 helps identify network failure. Do not disable certificate verification.

## Output validation update (v4)

Invalid AI asset/status/evidence/schema/oversized-explanation errors are distinguished and mapped to sanitized provider failures. A missing explanation is marked explicitly in the draft and export using a localized fallback and warnings; it does not bypass quote checks or alter the model status. No automatic repair of invalid statuses or fabricated evidence is performed. Regression checks use mocked provider responses, not a live model.

## Source code variants (v5)

Known code aliases are matched with complete Unicode word boundaries in the original source, including boundaries outside the quoted substring. Matching allows case, omitted/spaced hyphens and a small explicit set of dash characters. It does not use approximate text matching or translate/repair evidence. Generated evidence must still occur verbatim in the original transcript. Canonical code mapping does not prove the correctness of model status or transcription; human review remains required.

## Server-owned source evidence (v6)

Live model output cannot supply an evidence field: additional model fields are rejected. Quotes are constructed from original source spans for known explicit asset references, with exported Unicode code-point offsets. An unmentioned or duplicated ID is rejected; omission of a mentioned ID fails explicitly. Existing exact-source and full-code-boundary validation still runs after quote construction. Repeated asset references retain the continuous source range covering all their blocks, including other assets if present. This is deterministic context retrieval, not verification of semantic attribution, status accuracy or prompt-injection resistance. Operator review remains mandatory. No extra automatic API retries were introduced.
