Skip to content

Fix quarantine layout symlink escapes - #47

Closed
cursor[bot] wants to merge 1 commit into
mainfrom
cursor/critical-bug-investigation-a0e1
Closed

cursor[bot] wants to merge 1 commit into
mainfrom
cursor/critical-bug-investigation-a0e1

Conversation

@cursor

@cursor cursor Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Bug and impact

A recent quarantine layout change trusted $SCP_QUARANTINE_DIR/registry_fetch with Path.is_dir(), which follows symlinks. If that layout path was a symlink, registry-fetch quarantine writes, list/purge, quota eviction, and the registry merge gate could operate on files outside the real quarantine layout boundary. Concrete impacts included outside-root writes/deletes and forged root-layout quarantine files becoming merge-eligible when registry_fetch pointed back at the quarantine root.

Root cause

Allowlisted layout directories were added to trusted quarantine scopes without rejecting symlinks or verifying that the real directory is strictly below the resolved quarantine root. The merge gate also resolved registry_fetch directly, so a symlinked layout could collapse the trusted fetch root to the quarantine root.

Fix

  • Added shared quarantine_limits.safe_layout_subdir() / ensure_layout_subdir() helpers that accept only real, non-symlink layout directories strictly under the resolved root.
  • Reused the helper for quarantine list/purge/quota enumeration, registry-fetch writes, and registry_ssot.apply_merge() path gating.
  • Added regressions for outside-root list/purge protection, write fail-closed behavior, and forged merge rejection with registry_fetch -> quarantine root.

Validation

  • RED before fix: new symlink regressions failed as expected.
  • PYTHONPATH=src python3 -m pytest tests/test_security_regressions.py::test_symlinked_registry_fetch_layout_is_not_listed_or_purged_outside_root tests/test_security_regressions.py::test_registry_fetch_quarantine_rejects_symlinked_layout_dir tests/test_registry_ssot.py::test_apply_merge_rejects_registry_fetch_symlink_to_quarantine_root -q -> 3 passed.
  • PYTHONPATH=src python3 -m pytest tests/test_security_regressions.py tests/test_registry_ssot.py tests/test_registry_fetch_r4.py tests/test_registry_load_order_r5.py -q -> 54 passed.
  • PYTHONPATH=src python3 -m compileall -q src/scp && git diff --check -> passed.
  • Full suite after installing validation-only deps (pytest, mcp>=1.2.0,<2.0.0, coincurve, websocket-client) reached 357 passed / 3 skipped / 3 failed. Remaining failures appear pre-existing: vendored scp_mcp_v1 and scp_mcp_v1.1 contract hash mismatches, plus test_rt11_authority_only_is_reversal.
Open in Web View Automation 

@ManintheCrowds

Copy link
Copy Markdown
Owner

Audit: symlink layout + apply_merge reject of registry_fetch->root symlink covered by #60. Closing as superseded 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