fix(security): reject unknown MIME types in macOS image reader (#24)#47
Conversation
_macos_read_image previously fell back to interpolating raw caller-supplied MIME types into an AppleScript string literal. A crafted MIME containing a double-quote could escape the literal. Now rejects any MIME type not in the UTI reverse map with a ClipboardError, eliminating the injection vector. Closes #24 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
c431007 to
f27bbef
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
cmeans
left a comment
There was a problem hiding this comment.
QA Review — PR #47
Verdict: One observation. QA Failed pending a PR body edit.
Code review
Fix correctness: The old code uti = mime_to_uti.get(mime_type, mime_type) fell back to raw mime_type as the UTI, which was then interpolated into an AppleScript string literal. A crafted MIME type containing " could break out of the string and inject AppleScript commands. The new code rejects unknown types at the boundary with ClipboardError. Defense in depth — correct fix. ✓
Supported types: Only image/png, image/tiff, image/jpeg (the three types in _UTI_TO_MIME). Narrowing to known types is the right approach for a security fix. ✓
Error message: f"Unsupported image type: {mime_type}" — user input in an exception string, no injection risk in Python. ✓
Tests
Targeted (4/4 pass):
test_macos_read_image_unknown_mime_rejected—image/webp(valid MIME, no UTI mapping) → ClipboardError ✓test_macos_read_image_injection_rejected—image/png"; --(injection payload) → ClipboardError ✓test_macos_read_image— known type still works ✓test_macos_read_image_jpeg_uti— UTI mapping still works ✓
Full suite: 429 passed, 11 xfailed, 0 failed, 0 skipped ✓
The old test (test_macos_read_image_unknown_mime_passthrough) that asserted the insecure passthrough behavior is properly replaced — not just removed, but replaced with the rejection + injection tests. Good test evolution. ✓
Other checks
- CHANGELOG:
### Fixedentry, correct category (security fix), accurate description,Closes #24. ✓ - CI:
test (3.11/3.12/3.13),codecov/patchSUCCESS,QA Gatepending ✓ - Diff scope: 3 files (clipboard.py, test_server.py, CHANGELOG.md), +19/-13, single commit. Clean. ✓
Finding
F1 (observation) — PR body test count is stale.
PR body says: Full suite: uv run pytest -q -- 240 passed
Actual result: 429 passed, 11 xfailed
The branch includes #44, #45, and #46 content (test_escaping.py is present, all 189+11 escaping tests run). The expected count needs updating.
Fix: Edit PR body checkbox 5 from 240 passed to 429 passed, 11 xfailed. No commit needed.
Merge coordination note
This branch includes #44, #45, #46 content. Adding merge-order: 4 — merge after all three land. The CHANGELOG diff should auto-merge cleanly since the #24 entry appends to the Fixed section whose context lines (#16, #17) are stable.
|
Applying |
cmeans
left a comment
There was a problem hiding this comment.
QA Re-review — PR #47 (round 2)
Verdict: F1 resolved. Zero findings. Ready for maintainer signoff.
F1: fixed ✓
PR body checkbox 5 now says 429 passed, 11 xfailed — matches actual output on head f27bbef.
Re-verification (all 5 checkboxes on new head)
CHECKBOX 1: test_macos_read_image_unknown_mime_rejected PASSED
CHECKBOX 2: test_macos_read_image_injection_rejected PASSED
CHECKBOX 3: test_macos_read_image PASSED
CHECKBOX 4: test_macos_read_image_jpeg_uti PASSED
CHECKBOX 5: Full suite — 429 passed, 11 xfailed PASSED (matches PR body)
Code, tests, CHANGELOG, CI all unchanged from round 1 review (which had zero code findings). Applying Ready for QA Signoff.
|
Applying |
Summary
_macos_read_imagenow rejects MIME types not in the UTI reverse map withClipboardErrorinstead of interpolating them raw into an AppleScript string literal"image/png,image/tiff,image/jpeg(all mapped via_UTI_TO_MIME)Test plan
uv run pytest tests/test_server.py::test_macos_read_image_unknown_mime_rejected -vpassesuv run pytest tests/test_server.py::test_macos_read_image_injection_rejected -vpassesuv run pytest tests/test_server.py::test_macos_read_image -vstill passes (known types work)uv run pytest tests/test_server.py::test_macos_read_image_jpeg_uti -vstill passes (UTI mapping works)uv run pytest -q-- 429 passed, 11 xfailedCloses #24