67 lines
3.1 KiB
Python
67 lines
3.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Real disposable receive hooks outlasting both historical client deadlines."""
|
|
from concurrent.futures import ThreadPoolExecutor
|
|
import hashlib
|
|
import importlib.util
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import subprocess
|
|
import tempfile
|
|
import time
|
|
|
|
SOURCES = {
|
|
'state-hub': Path('/home/worsch/state-hub/scripts/repo_sync.py'),
|
|
'repo-manager': Path('/home/worsch/repo-manager/src/repo_manager/gitops.py'),
|
|
}
|
|
|
|
|
|
def probe(item):
|
|
name, source = item
|
|
spec = importlib.util.spec_from_file_location(name, source)
|
|
module = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(module)
|
|
with tempfile.TemporaryDirectory(prefix='push-deadline-') as temporary:
|
|
base = Path(temporary)
|
|
remote, local = base / 'remote.git', base / 'client'
|
|
|
|
def git(*args):
|
|
p = subprocess.run(['git', *map(str, args)], capture_output=True, timeout=15)
|
|
if p.returncode:
|
|
raise ValueError('probe-git-failed')
|
|
return p.stdout.decode().strip()
|
|
|
|
git('init', '--bare', '-q', remote)
|
|
git('clone', '-q', remote, local)
|
|
git('-C', local, 'config', 'user.name', 'Deadline probe')
|
|
git('-C', local, 'config', 'user.email', 'probe@example.test')
|
|
git('-C', local, 'config', 'core.hooksPath', base / 'no-client-hooks')
|
|
git('-C', local, 'commit', '--allow-empty', '-qm', 'isolated deadline fixture')
|
|
hook = remote / 'hooks/pre-receive'
|
|
hook.write_text('#!/bin/sh\nsleep 65\nexit 0\n')
|
|
hook.chmod(0o755)
|
|
start = time.monotonic()
|
|
ok, _ = module.push_ff(local)
|
|
elapsed = time.monotonic() - start
|
|
head = git('-C', local, 'rev-parse', 'HEAD')
|
|
remote_head = git('--git-dir', remote, 'rev-parse', 'HEAD')
|
|
accepted = ok and head == remote_head and elapsed >= 65
|
|
hook.write_text('#!/bin/sh\nexit 1\n')
|
|
git('-C', local, 'commit', '--allow-empty', '-qm', 'isolated rejection fixture')
|
|
rejected, _ = module.push_ff(local)
|
|
refusal = not rejected and git('--git-dir', remote, 'rev-parse', 'HEAD') == head
|
|
return {'repo': name, 'source_sha256': hashlib.sha256(source.read_bytes()).hexdigest(),
|
|
'configured_seconds': module.PUSH_TIMEOUT_SECONDS, 'elapsed_seconds': round(elapsed, 2),
|
|
'receive_hook_65_seconds_accepted': accepted, 'refusal_preserved_ref': refusal}
|
|
|
|
|
|
if __name__ == '__main__':
|
|
os.environ.update(GIT_CONFIG_GLOBAL='/dev/null', GIT_CONFIG_NOSYSTEM='1', GIT_TERMINAL_PROMPT='0')
|
|
with ThreadPoolExecutor(max_workers=2) as pool:
|
|
results = list(pool.map(probe, SOURCES.items()))
|
|
passed = all(x['receive_hook_65_seconds_accepted'] and x['refusal_preserved_ref'] for x in results)
|
|
receipt = {'result': 'verified' if passed else 'failed', 'checks': results,
|
|
'scope': 'Disposable local Git repositories; synthetic delayed/refusing receive hooks, no production delay injected.'}
|
|
Path('docs/evidence/2026-10-13-netkingdom-security/push-deadline.json').write_text(json.dumps(receipt, indent=2) + '\n')
|
|
print(json.dumps(receipt))
|
|
assert passed
|