#!/usr/bin/env python3 """Exact-binary disposable probe in the admitted live Forgejo container. No production repository, Git hook, credential, account, config or policy writes. Default is read-only preflight; --apply creates only the exact new /tmp directory and removes it after the probe, guarded by its unique owner marker. """ import argparse import hashlib import io import json import re import shlex import subprocess import tarfile from pathlib import Path def sha(path): return hashlib.sha256(Path(path).read_bytes()).hexdigest() def command(args, payload=None, timeout=45): result = subprocess.run(['ssh', '-o', 'BatchMode=yes', '-o', 'ConnectTimeout=10', 'railiance01', shlex.join(['kubectl', '--request-timeout=15s', *args])], input=payload, capture_output=True, timeout=timeout) if result.returncode: raise ValueError('native-command-refused') return result.stdout def execute(proposal, *args, payload=None, timeout=45): return command(['-n', 'forgejo', 'exec', '-i', proposal['pod'], '-c', 'gitea', '--', *args], payload, timeout) def preflight(proposal): if not re.fullmatch(r'custodian-receive-native-[a-z0-9-]+', proposal['directory_name']): raise ValueError('unsafe-probe-directory') if proposal['executor_sha256'] != sha(__file__): raise ValueError('executor-revision-changed') nodes = json.loads(command(['get', 'nodes', '-o', 'json']))['items'] if len(nodes) != 1 or not any(a['address'] == '92.205.62.239' for a in nodes[0]['status']['addresses']): raise ValueError('target-host-changed') pod = json.loads(command(['-n', 'forgejo', 'get', 'pod', proposal['pod'], '-o', 'json'])) if pod['metadata']['uid'] != proposal['pod_uid']: raise ValueError('target-pod-changed') if not any(c['type'] == 'Ready' and c['status'] == 'True' for c in pod['status']['conditions']): raise ValueError('target-pod-unready') image = next(c['image'] for c in pod['spec']['containers'] if c['name'] == 'gitea') if image != proposal['image']: raise ValueError('target-image-changed') image_id = next(c['imageID'] for c in pod['status']['containerStatuses'] if c['name'] == 'gitea') if image_id != proposal['image_id']: raise ValueError('target-image-digest-changed') for item in proposal['artifacts']: path = Path(item['source']) if path.is_symlink() or not path.is_file() or sha(path) != item['sha256']: raise ValueError('artifact-changed') if not re.fullmatch(r'[a-zA-Z0-9._-]+', item['name']): raise ValueError('artifact-name-refused') df = execute(proposal, 'df', '-Pk', '/tmp').decode().splitlines()[-1].split() if int(df[3]) < 256 * 1024: raise ValueError('temporary-space-insufficient') execute(proposal, 'wget', '-qO', '/dev/null', 'http://127.0.0.1:3000/api/healthz') dispatcher = execute(proposal, 'sha256sum', proposal['existing_dispatcher']).decode().split()[0] if dispatcher != proposal['dispatcher_sha256']: raise ValueError('existing-dispatcher-changed') return {'pod_uid': pod['metadata']['uid'], 'image_id': image_id, 'git_version': execute(proposal, '/usr/bin/git', '--version').decode().strip(), 'free_tmp_kib': int(df[3]), 'health_before': True} def archive(proposal): data = io.BytesIO() checksums = [] with tarfile.open(fileobj=data, mode='w') as output: for item in proposal['artifacts']: content = Path(item['source']).read_bytes() info = tarfile.TarInfo(item['name']); info.size = len(content); info.mode = item['mode'] info.uid = info.gid = 1000 output.addfile(info, io.BytesIO(content)) checksums.append(item['sha256'] + ' ' + item['name'] + '\n') content = ''.join(checksums).encode() info = tarfile.TarInfo('SHA256SUMS'); info.size = len(content); info.mode = 0o600 info.uid = info.gid = 1000 output.addfile(info, io.BytesIO(content)) return data.getvalue() def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('--proposal', type=Path, required=True) parser.add_argument('--receipt', type=Path, required=True) parser.add_argument('--apply', action='store_true') args = parser.parse_args() proposal = json.loads(args.proposal.read_text()) observation = preflight(proposal) if not args.apply: print(json.dumps({'status': 'preflight-verified', **observation})) return directory = '/tmp/' + proposal['directory_name'] marker = proposal['proposal_id'] if not re.fullmatch(r'[A-Za-z0-9-]+', marker): raise ValueError('unsafe-marker') receipt = {'proposal_id': marker, 'proposal_sha256': sha(args.proposal), 'executed_revision': proposal['revision'], 'result': 'unverified', 'observation': observation, 'scope': 'exact binaries in disposable bare repositories inside existing Forgejo image; builtin auth stubbed', 'production_repositories_changed': False, 'live_credentials_used': False} created = False try: execute(proposal, 'sh', '-c', 'umask 077; test ! -e "$1" && mkdir "$1" && printf "%s" "$2" > "$1/.probe-owner"', 'sh', directory, marker) created = True execute(proposal, 'tar', '-xmf', '-', '-C', directory, payload=archive(proposal), timeout=90) raw = execute(proposal, 'timeout', '-k', '5', '150', 'sh', directory + '/probe.sh', directory, timeout=180) result = json.loads(raw) keys = ['clean_push', 'intermediate_secret_refused', 'quarantine_discarded', 'scanner_unavailable_refused', 'clean_after_recovery', 'dispatcher_preserved'] if not all(result.get(k) is True for k in keys) or result.get('other_hook_invocations') != 4: raise ValueError('incomplete-native-proof') receipt['verification'] = result execute(proposal, 'wget', '-qO', '/dev/null', 'http://127.0.0.1:3000/api/healthz') receipt['health_after'] = True receipt['result'] = 'verified' finally: if created: try: execute(proposal, 'sh', '-c', 'test "$(cat "$1/.probe-owner")" = "$2" && rm -rf -- "$1" && test ! -e "$1"', 'sh', directory, marker) receipt['temporary_directory_removed'] = True except (ValueError, OSError, subprocess.SubprocessError): receipt['temporary_directory_removed'] = False receipt['result'] = 'unverified' args.receipt.write_text(json.dumps(receipt, indent=2) + '\n') if receipt['result'] != 'verified': raise ValueError('verification-or-cleanup-incomplete') print('Exact-binary native probe verified; temporary area removed; production hooks unchanged.') if __name__ == '__main__': try: main() except (ValueError, OSError, KeyError, StopIteration, subprocess.SubprocessError): raise SystemExit('Native probe refused or incomplete; inspect value-free receipt. Raw diagnostics suppressed.')