Skip to content

Fail closed on malformed streamed fetch bodies - #48

Closed
cursor[bot] wants to merge 3 commits into
mainfrom
cursor/critical-bug-investigation-44a5
Closed

cursor[bot] wants to merge 3 commits into
mainfrom
cursor/critical-bug-investigation-44a5

Conversation

@cursor

@cursor cursor Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Bug and impact

Malformed streamed fetch bodies from an allowlisted registry/Antigen host could escape the existing fail-closed error contract. Invalid UTF-8 in registry fetch raised a raw UnicodeDecodeError; scalar JSON in Antigen fetch raised a raw TypeError; streamed non-200 and L402 responses were not closed before raising. In CLI/library paths this can crash fetch workflows, and repeated streamed error/challenge responses can leak HTTP resources.

Root cause

http_body.read_response_json() decoded bytes outside the JSON error boundary, while Antigen payload extraction assumed decoded JSON was always a dict. The Antigen early-error branches also raised before closing streamed responses.

Fix

  • Map invalid UTF-8 to json.JSONDecodeError so existing callers return invalid_json / FetchError("invalid_json").
  • Reject non-object or non-dict bundle payload JSON with FetchError("unrecognized_payload_shape").
  • Close streamed non-200 and L402 challenge responses before raising.
  • Added RED/GREEN AppSec regressions for each failure mode.

Validation

  • RED before fix: the new tests failed with raw UnicodeDecodeError, raw TypeError, and missing close() calls.
  • PYTHONPATH=src python3 -m pytest tests/test_appsec_fetch_body_caps.py tests/test_antigen_p1_nostr.py tests/test_registry_fetch_r4.py tests/test_registry_ssot.py tests/test_registry_load_order_r5.py tests/test_appsec_antigen_mcp_gates.py tests/test_security_regressions.py -q -> 100 passed, 1 skipped.
  • PYTHONPATH=src python3 -m compileall -q src/scp && git diff --check -> passed.
  • Focused review subagent: no critical/important issues; it also ran PYTHONPATH=src python3 -m pytest tests/test_appsec_fetch_body_caps.py -q -> 12 passed.
  • PYTHONPATH=src python3 -m pytest -q -> 358 passed, 3 skipped, 3 known baseline failures (test_vendored_scp_mcp_v1_contract_sha256, test_vendored_scp_mcp_v1_1_contract_sha256, test_rt11_authority_only_is_reversal).
Open in Web View Automation 

@ManintheCrowds

Copy link
Copy Markdown
Owner

Audit: write-failure preserve / temp-commit integrity covered by #60. Streamed-body and base64/leetspeak sanitizer extras were unique to this draft; not required to close the provenance/symlink gap and left for a later pass if still failing on main. Closing as superseded for the quarantine pile by #60.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants