ADR-013 Phase 2: fix four bypasses found by adversarial review
An adversarial security review of the Phase 2 trust model found four real bypasses (two remote-triggerable to in-process code execution). Root cause for three: the set of bytes verification covered was smaller than the set that determined execution. Fixes: 1. Bytecode-cache blind spot (CRITICAL). verify_dir excluded __pycache__/.pyc, so a planted cache ran while escaping the hash map. verify_dir now flags any bytecode as an unexpected file; the loader strips bytecode before verify and imports under sys.dont_write_bytecode, so only verified source executes. 2. Unauthenticated verify-at-load bypass (CRITICAL). load_plugin_class imported plugin.py with no gate, reachable via discover_available / an anonymous GET /api/plugins. The verify+strip gate moved INTO load_plugin_class - the single import choke point every path flows through - so an unsigned/tampered plugin is never imported. discover_available skips a refused plugin instead of 500. 3. Ungated migration entrypoints (HIGH). downgrade_plugin and get_current_head (ScriptDirectory imports version modules) ran plugin code with no check. All alembic-invoking methods now pass through _verify_ok (strip + verify) first and run under no-bytecode. 4. Revocation/content bypass (HIGH). The signed index bound a filename, not content; adopt did not bind the delivered bytes to the resolved version, so revoked bytes could be served under a live filename. The index now records a per-artifact SHA-256; adopt verifies the on-disk digest and requires the artifact's own signed manifest version to equal the resolved version. Enforcement stays default-off; strip/no-bytecode run only under enforcement, so the unsigned path is unchanged. 6 regression tests (planted bytecode, the discover import path, downgrade gate, version-swap). 1054 pass, naming green.
This commit is contained in:
@@ -23,9 +23,14 @@ from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
|
||||
PROVENANCE_NAME = 'PROVENANCE.json'
|
||||
PROVENANCE_SIG = 'PROVENANCE.sig'
|
||||
|
||||
# Never packed, hashed, or counted as an unexpected artifact member.
|
||||
# Never packed or hashed (a signed artifact carries source, never bytecode).
|
||||
_EXCLUDE_DIRS = {'__pycache__', '.pytest_cache', '.mypy_cache'}
|
||||
_EXCLUDE_SUFFIXES = {'.pyc', '.pyo'}
|
||||
# Verification ignores only non-executable dev noise. It deliberately does NOT
|
||||
# ignore __pycache__/.pyc: a planted bytecode cache would otherwise run while
|
||||
# escaping the hash check (the set of verified bytes must not be smaller than
|
||||
# the set of executed bytes). So verify flags any bytecode as an extra file.
|
||||
_VERIFY_EXCLUDE_DIRS = {'.pytest_cache', '.mypy_cache', '.git'}
|
||||
_CHUNK = 65536
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user