the-custodian/tests/test_secret_hook_activation.py
codex 355b6b9720
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Reject generated-only commits / generated-commit-guard (push) Successful in 7s
Python Tests / pytest (push) Successful in 25s
security: verify staged-secret enforcement across 22 workstation checkouts
2026-10-12 17:48:43 +02:00

85 lines
4.1 KiB
Python

"""Exercise reviewed hook activation/rollback in disposable repositories only."""
import hashlib
import json
import os
from pathlib import Path
import shutil
import subprocess
import sys
import pytest
ROOT = Path(__file__).resolve().parents[1]
SCRIPT = ROOT / 'docs/changes/CUST-WP-0080/activate_secret_hooks.py'
def sha(path): return hashlib.sha256(path.read_bytes()).hexdigest()
@pytest.fixture
def proposal(tmp_path):
binary_source = Path('/tmp/netkingdom-secret-detection/gitleaks')
if not binary_source.exists(): pytest.skip('reviewed disposable scanner not installed')
repo = tmp_path / 'scoped'; repo.mkdir()
def git(*args):
return subprocess.check_output(['git', '-C', str(repo), *args], stderr=subprocess.DEVNULL)
git('init', '-q')
hooks = repo / '.git/hooks'
git('config', 'core.hooksPath', str(hooks))
git('config', 'user.name', 'Synthetic Probe'); git('config', 'user.email', 'probe@example.test')
(repo / 'source.py').write_text('print("clean")\n')
git('add', 'source.py'); git('commit', '-qm', 'clean baseline')
before = hooks / 'pre-commit'; before.write_text('#!/bin/sh\nexit 0\n'); before.chmod(0o755)
backup = tmp_path / 'backup'; shutil.copyfile(before, backup)
binary = tmp_path / 'tools/gitleaks'
candidate = tmp_path / 'candidate'
candidate.write_text('#!/bin/sh\nset -eu\npython3 '+str(ROOT/'scripts/check_generated_only_commit.py')+' --staged\n'
'case "$(git rev-parse --show-toplevel)" in\n'+str(repo)+')\n'
'exec python3 /home/worsch/repo-manager/src/repo_manager/secret_scan.py --path . --binary '
+str(binary)+'\n;;\nesac\n')
data = {'proposal_id':'disposable-activation-probe','scope_roots':[str(repo)],
'binary':{'source':str(binary_source),'target':str(binary),'sha256':sha(binary_source)},
'hook_source':str(candidate),'hooks':[{'path':str(before),'before_sha256':sha(before),
'before_mode':0o755,'backup':str(backup),'after_sha256':sha(candidate)}],
'original_routes':[{'repo':repo.name,'effective_pre_commit':str(before)}],
'protected_files':[], 'executor_sha256':sha(SCRIPT)}
path = tmp_path / 'proposal.json'; path.write_text(json.dumps(data))
return path, data, tmp_path / 'receipt.json', repo, git('rev-parse','HEAD')
def invoke(proposal, *args):
path, _, receipt, _, _ = proposal
return subprocess.run([sys.executable, str(SCRIPT), '--proposal', str(path), '--receipt', str(receipt), *args],
capture_output=True, text=True, check=False, timeout=60)
def test_activation_native_probes_restore_tool_and_preserve_repository(proposal):
_, data, receipt, repo, head = proposal
before_index = (repo/'.git/index').read_bytes()
assert invoke(proposal, '--apply').returncode == 0
observed = json.loads(receipt.read_text())
assert observed['result'] == 'verified'
assert observed['verification']['repositories'][0]['scanner_failure_exit'] == 2
assert observed['verification']['scanner_mode_restored']
assert (repo/'.git/index').read_bytes() == before_index
assert subprocess.check_output(['git','-C',str(repo),'rev-parse','HEAD']) == head
assert invoke(proposal, '--rollback').returncode == 0
assert sha(Path(data['hooks'][0]['path'])) == data['hooks'][0]['before_sha256']
def test_unknown_hook_is_refused_before_install(proposal):
_, data, _, _, _ = proposal
Path(data['hooks'][0]['path']).write_text('unreviewed hook\n')
assert invoke(proposal, '--apply').returncode != 0
assert not Path(data['binary']['target']).exists()
def test_failed_probe_rolls_back_exact_hook(proposal):
path, data, receipt, _, _ = proposal
candidate = Path(data['hook_source']); candidate.write_text('#!/bin/sh\nexit 0\n')
data['hooks'][0]['after_sha256'] = sha(candidate); path.write_text(json.dumps(data))
assert invoke(proposal, '--apply').returncode != 0
observed = json.loads(receipt.read_text())
assert observed['result'] == 'unverified'
assert observed['rollback'] == 'prior hooks restored'
assert sha(Path(data['hooks'][0]['path'])) == data['hooks'][0]['before_sha256']