phase: 123_chat_image_questions
All gates green. Verification complete. **Phase 123 — final verification pass (all 4 tasks already in `complete/`)** - Verified the full implementation is in the working tree: `app/api/chat_images.py` (upload/serve pair), `ChatRequest.image`/`ChatMessage.image` (path-validated, omitted-when-None), toggle-off + stale-file hinted error frames, `build_user_content` multimodal build at both sites (chat.py deflected branch + `run_agent`), config-gated composer attach/preview/upload-then-send, restore + shared rendering, CSP `img-src 'self' data:` carve-out, mock-LLM capture buffer. - `uv run pytest` → **2796 passed**, exit 0 (unit + integration). - `uv run pytest --cov=app --cov-report=term-missing` → **TOTAL 99%** (29/4615 missed; phase-123 modules 99–100%). - `uv run pytest tests/e2e/test_chat_image_questions.py -v --no-cov` → **5 passed** in isolation. - `uv run ruff check . && uv run pyright` → clean (0 errors). **Completion criteria:** (1) attach→send→multimodal text+image to the model, bubble/reload/shared all render it, saved chat stores the PATH with `"base64" not in json.dumps(stored)` — **verified** (E2E tests 1–4 + integration round-trip); (2) `BOR_IMAGES=false` — control hidden, exact hinted error frame, zero model calls / no query_log row — **verified** (E2E test 5 + integration); (3) text-only byte-identical (`content` stays a plain `str`) — **verified** (unit + integration); (4) all gates green — **verified**; (5) commit + phase move — left to the harness per pipeline rules (no `git add`/`commit` run). No defects found; no live-infrastructure changes (repo + local dev DB only). **Next pending phase: none** — 123 is the last phase in `todo/`.
This commit is contained in:
@@ -38,7 +38,8 @@ from __future__ import annotations
|
||||
from starlette.datastructures import MutableHeaders
|
||||
from starlette.types import ASGIApp, Message, Receive, Scope, Send
|
||||
|
||||
#: The exact owner-approved policy (phase 82, decision A1).
|
||||
#: The exact owner-approved policy (phase 82, decision A1), extended
|
||||
#: by phase 123's ``img-src`` carve-out (see below).
|
||||
#: ``default-src 'self'`` is inherited by every sub-policy that has no
|
||||
#: explicit entry (``script-src``, ``style-src``, ``connect-src``, …),
|
||||
#: ``base-uri 'none'`` blocks base-tag hijacking, and
|
||||
@@ -46,7 +47,26 @@ from starlette.types import ASGIApp, Message, Receive, Scope, Send
|
||||
#: (verified unnecessary — see module docstring), no ``report-uri`` /
|
||||
#: ``report-to`` (no collector in the homelab — a report would just
|
||||
#: vanish).
|
||||
CSP = "default-src 'self'; base-uri 'none'; frame-ancestors 'none'"
|
||||
#
|
||||
#: Phase 123 (chat image questions, owner-confirmed 2026-09-24) added
|
||||
#: the one scoped relaxation the design requires: ``img-src 'self'
|
||||
#: data:``. The question-image composer renders the picked file as a
|
||||
#: ``data:`` URL — the PREVIEW thumbnail (before the send-time upload
|
||||
#: there is no served path yet) and the LIVE user bubble (the data URL
|
||||
#: needs no fetch) — and ``default-src 'self'`` alone blocks ``data:``
|
||||
#: images in every real browser (the phase-123 E2E caught it: the
|
||||
#: bubble degraded to the "image unavailable" line). The carve-out is
|
||||
#: ``img-src`` ONLY: ``data:`` never becomes a source for scripts,
|
||||
#: styles, or fetches (those keep the strict ``default-src 'self'``
|
||||
#: inheritance), and the bytes are the user's OWN locally-picked file
|
||||
#: (no exfiltration vector — an ``<img>`` cannot read them back).
|
||||
#: Restored / shared bubbles render from the served path (``'self'``),
|
||||
#: so the ``data:`` allowance exists for the two pre-upload/first-paint
|
||||
#: surfaces only.
|
||||
CSP = (
|
||||
"default-src 'self'; base-uri 'none'; frame-ancestors 'none'; "
|
||||
"img-src 'self' data:"
|
||||
)
|
||||
|
||||
|
||||
class SecurityHeadersMiddleware:
|
||||
|
||||
Reference in New Issue
Block a user