85 lines
4.1 KiB
Python
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']
|