Harden image ingress: magic bytes, re-encode, limits, metadata stripping, and rate control #16

Closed
opened 2026-08-19 01:58:59 +00:00 by timmy · 3 comments
Owner

Parent epic: #2

Outcome

Treat every uploaded medical image as hostile and sensitive.

Acceptance criteria

  • Decode verifies content independent of filename/MIME.
  • Dimension/body/time/rate limits fail before provider work.
  • Re-encoding strips metadata and malformed/polyglot fixtures are rejected.
  • Request and error logs contain no image bytes or base64.

Dependencies

  • None

Required evidence

  • Test or executable receipt attached to the issue/PR
  • Privacy/safety boundary checked where applicable
  • Clean checkout and relevant full suite pass

Delivery

Open a focused branch and PR with Closes #ISSUE. Do not include real medical images in issue comments or Git history.

Parent epic: #2 ## Outcome Treat every uploaded medical image as hostile and sensitive. ## Acceptance criteria - [ ] Decode verifies content independent of filename/MIME. - [ ] Dimension/body/time/rate limits fail before provider work. - [ ] Re-encoding strips metadata and malformed/polyglot fixtures are rejected. - [ ] Request and error logs contain no image bytes or base64. ## Dependencies - None ## Required evidence - Test or executable receipt attached to the issue/PR - Privacy/safety boundary checked where applicable - Clean checkout and relevant full suite pass ## Delivery Open a focused branch and PR with `Closes #ISSUE`. Do not include real medical images in issue comments or Git history.
timmy added this to the M1 — Sovereign Photo Intelligence milestone 2026-08-19 01:58:59 +00:00
timmy added the type/securitypriority/P0area/backendarea/privacy labels 2026-08-19 01:58:59 +00:00
Member

[CLAIM] agent=timmy

Working in dedicated worktree /tmp/hy3-timmy-16 at origin/main 47294a9 on branch free/hy3-timmy-16. Scope: magic-byte validation, safe re-encode with metadata stripping, strict body/dimension/time limits, and bounded rate control on the /api/analyze image ingress. Strict RED-GREEN TDD with hostile synthetic fixtures (spoofed MIME, polyglots, truncation, decompression bombs, EXIF/GPS, oversized dimensions, bursts). Fail closed with sanitized errors and manual fallback; no real medical images, no live deploy.

[CLAIM] agent=timmy Working in dedicated worktree /tmp/hy3-timmy-16 at origin/main 47294a9 on branch free/hy3-timmy-16. Scope: magic-byte validation, safe re-encode with metadata stripping, strict body/dimension/time limits, and bounded rate control on the /api/analyze image ingress. Strict RED-GREEN TDD with hostile synthetic fixtures (spoofed MIME, polyglots, truncation, decompression bombs, EXIF/GPS, oversized dimensions, bursts). Fail closed with sanitized errors and manual fallback; no real medical images, no live deploy.
rockachopa added the type/test label 2026-08-22 21:23:07 +00:00
Member

[EVIDENCE] agent=timmy

PR: #63 (branch free/hy3-timmy-16 at ccb2279, base 47294a9). Not merged; awaiting human review.

  • Strict RED-GREEN TDD: tests/image-ingress.test.js written first against hostile synthetic fixtures and observed failing (module missing), then implemented to green.
  • Hostile inputs covered: spoofed MIME (HTML as JPEG), GIF-header polyglot with script payload, ZIP-in-JPEG polyglot, truncated/garbage/empty images, 12000x12000 decompression bomb, 6000x6000 oversized dimensions, EXIF+GPS JPEG, PNG tEXt metadata, SVG with embedded script.
  • Verified post-ingress output carries zero EXIF/GPS/text metadata and errors are sanitized fixed strings (no bytes, no base64, no stacks); provider fetch provably never reached on rejection.
  • Rate control: bounded per-client window limiter on /api/analyze, 429 + Retry-After, no payload retention.
  • Clean checkout of pushed branch: 81/81 node tests pass, 20/20 staging-deploy tests pass, check:syntax clean, check:diff clean, npm audit --omit=dev 0 vulnerabilities, secrets grep clean. All fixtures synthetic; no real medical images in issue comments or git history.
[EVIDENCE] agent=timmy PR: https://forge.alexanderwhitestone.com/git/stackchain/timmy-talking-turd/pulls/63 (branch free/hy3-timmy-16 at ccb2279, base 47294a9). Not merged; awaiting human review. - Strict RED-GREEN TDD: tests/image-ingress.test.js written first against hostile synthetic fixtures and observed failing (module missing), then implemented to green. - Hostile inputs covered: spoofed MIME (HTML as JPEG), GIF-header polyglot with script payload, ZIP-in-JPEG polyglot, truncated/garbage/empty images, 12000x12000 decompression bomb, 6000x6000 oversized dimensions, EXIF+GPS JPEG, PNG tEXt metadata, SVG with embedded script. - Verified post-ingress output carries zero EXIF/GPS/text metadata and errors are sanitized fixed strings (no bytes, no base64, no stacks); provider fetch provably never reached on rejection. - Rate control: bounded per-client window limiter on /api/analyze, 429 + Retry-After, no payload retention. - Clean checkout of pushed branch: 81/81 node tests pass, 20/20 staging-deploy tests pass, check:syntax clean, check:diff clean, npm audit --omit=dev 0 vulnerabilities, secrets grep clean. All fixtures synthetic; no real medical images in issue comments or git history.
rockachopa added the state:review label 2026-08-22 21:49:09 +00:00
Author
Owner

PR #82 is open against main with Closes #16 — a fresh implementation of these acceptance criteria on today’s tree. It supersedes PR #63 (branch free/hy3-timmy-16), which has six conflicts and 31 commits of drift since it was written.

Verified locally: 225 unit/security tests pass; the new acceptance suite runs the shipping server against synthetic bytes with a counting provider stub and shows non-image, polyglot, oversized-dimension, flood (429 + Retry-After) and stalled-body (408) refusals all landing before any provider call, with a clean re-encoded photo still reaching it exactly once; the browser suite shows the new upload-free refusal state and proves nothing was posted. Release demo re-recorded: 63/63 captions measured as painted, including the refusal scene, and the builder now requires it.

Not merged — review and RC approval stay with the release job. The threat model now names each control and its enforcing test.

PR #82 is open against main with `Closes #16` — a fresh implementation of these acceptance criteria on today’s tree. It supersedes PR #63 (branch `free/hy3-timmy-16`), which has six conflicts and 31 commits of drift since it was written. Verified locally: 225 unit/security tests pass; the new acceptance suite runs the shipping server against synthetic bytes with a counting provider stub and shows non-image, polyglot, oversized-dimension, flood (429 + Retry-After) and stalled-body (408) refusals all landing before any provider call, with a clean re-encoded photo still reaching it exactly once; the browser suite shows the new upload-free refusal state and proves nothing was posted. Release demo re-recorded: 63/63 captions measured as painted, including the refusal scene, and the builder now requires it. Not merged — review and RC approval stay with the release job. The threat model now names each control and its enforcing test.
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: stackchain/timmy-talking-turd#16