119 lines
5.3 KiB
Python
119 lines
5.3 KiB
Python
"""Founder-run read-only replay of sealed r2 preflight, with value-free failures.
|
|
|
|
Never invokes apply, staging, patch, diff, server dry-run or rollback. Never
|
|
prints command arguments, shell diagnostics, Kubernetes objects or credentials.
|
|
The original failed execution receipt is not modified.
|
|
"""
|
|
from pathlib import Path
|
|
import importlib.util
|
|
import json
|
|
import re
|
|
import subprocess
|
|
|
|
HERE = Path(__file__).resolve().parent
|
|
ORIGINAL = HERE.parent / 'native-r2'
|
|
EXPECTED_PACKET = 'd22b1f555af96c203b7d68efd25e80815ce18ca4e3bf0dd3ab96b6f699d50237'
|
|
OUTPUT = Path('/tmp/forge-scanner-diagnostic-receipt.json')
|
|
|
|
|
|
def failure_category(returncode, stderr):
|
|
# Fixed labels only. Raw stderr stays in memory and is discarded.
|
|
if returncode == 0:
|
|
return 'passed'
|
|
for needle, label in [(b'Forbidden', 'forbidden'), (b'Unauthorized', 'unauthorized'),
|
|
(b'certificate has expired', 'certificate-expired'),
|
|
(b'certificate signed by unknown authority', 'certificate-authority'),
|
|
(b'connection refused', 'connection-refused'),
|
|
(b'no such host', 'name-resolution'),
|
|
(b'context deadline exceeded', 'deadline-exceeded'),
|
|
(b'NotFound', 'not-found')]:
|
|
if needle in stderr:
|
|
return label
|
|
if re.search(rb'command terminated with exit code [0-9]+', stderr):
|
|
return 'remote-check-nonzero'
|
|
return 'unclassified-command-failure'
|
|
|
|
|
|
def step_label(args, cfg, inventory):
|
|
if 'exec' in args:
|
|
# Exact known shell only, including the literal check argument.
|
|
ensure(args[-1:] in [('check',), ('sh',)], 'unexpected-exec-tail')
|
|
if cfg['guard_command'][2] in args and args[-1] == 'check':
|
|
return 'installed-artifact-guard'
|
|
if inventory in args:
|
|
return 'existing-repository-hook-inventory'
|
|
raise RuntimeError('unexpected-exec-script')
|
|
if args[:2] == ('get', 'nodes'):
|
|
return 'node-identity-read'
|
|
for resource, label in [('deployment', 'deployment-read'), ('replicasets', 'replicaset-read'),
|
|
('pods', 'pod-list-read'), ('pod', 'initial-pod-uid-read'),
|
|
('configmap', 'old-public-hook-read')]:
|
|
if args[:4] == ('-n', 'forgejo', 'get', resource):
|
|
return label
|
|
raise RuntimeError('unexpected-read-command')
|
|
|
|
|
|
def ensure(condition, reason):
|
|
if not condition:
|
|
raise RuntimeError(reason)
|
|
|
|
|
|
def main():
|
|
spec = importlib.util.spec_from_file_location('sealed_r2_rollout', ORIGINAL / 'rollout.py')
|
|
module = importlib.util.module_from_spec(spec); spec.loader.exec_module(module)
|
|
ensure(module.verify_packet() == EXPECTED_PACKET, 'reviewed-packet-drift')
|
|
cfg = json.loads((ORIGINAL / 'inputs.json').read_bytes())
|
|
receipt = {'read_only': True, 'replayed_packet_sha256': EXPECTED_PACKET, 'steps': [],
|
|
'native_mutation': False, 'original_failed_receipt_preserved': True}
|
|
|
|
def save():
|
|
OUTPUT.write_text(json.dumps(receipt, indent=2) + '\n')
|
|
|
|
class Diagnostic(module.Rollout):
|
|
def k(self, *args, data=None, timeout=60):
|
|
ensure(data is None, 'unexpected-stdin')
|
|
label = step_label(args, cfg, module.inventory_script())
|
|
try:
|
|
q = subprocess.run(['kubectl', *args], capture_output=True, timeout=min(timeout, 60))
|
|
except subprocess.TimeoutExpired:
|
|
receipt['steps'].append({'step': label, 'category': 'timeout'})
|
|
save(); print(json.dumps(receipt['steps'][-1]), flush=True)
|
|
raise RuntimeError('read-only-command-timeout')
|
|
item = {'step': label, 'returncode': q.returncode,
|
|
'category': failure_category(q.returncode, q.stderr)}
|
|
if q.returncode:
|
|
match = re.search(rb'command terminated with exit code ([0-9]+)', q.stderr)
|
|
if match:
|
|
item['remote_exit_code'] = int(match[1])
|
|
receipt['steps'].append(item); save(); print(json.dumps(item), flush=True)
|
|
ensure(q.returncode == 0, 'read-only-step-failed:' + label)
|
|
return q.stdout
|
|
|
|
def event(self, name, **details):
|
|
# No writes to the native r2 receipt; details contain public counters only.
|
|
receipt['checks'] = {'event': name, **details}; save()
|
|
|
|
def dry_run(self, d):
|
|
receipt['server_dry_run'] = 'intentionally-not-replayed'
|
|
|
|
def ready_pod(self, d, seconds=180):
|
|
return super().ready_pod(d, seconds=10)
|
|
|
|
try:
|
|
Diagnostic(cfg).preflight()
|
|
receipt['result'] = 'read-only-preflight-passed'
|
|
except Exception as exc:
|
|
receipt['result'] = 'read-only-preflight-refused'
|
|
receipt['reason'] = str(exc) if isinstance(exc, RuntimeError) else type(exc).__name__
|
|
save()
|
|
print(json.dumps({'result': receipt['result'], 'reason': receipt.get('reason'),
|
|
'receipt': str(OUTPUT), 'native_mutation': False}), flush=True)
|
|
return 0 if receipt['result'] == 'read-only-preflight-passed' else 1
|
|
|
|
|
|
if __name__ == '__main__':
|
|
try:
|
|
raise SystemExit(main())
|
|
except Exception as error:
|
|
print(json.dumps({'result': 'diagnostic-refused', 'reason': str(error) if isinstance(error, RuntimeError) else type(error).__name__}))
|
|
raise SystemExit(1)
|