77 lines
3.8 KiB
Python
77 lines
3.8 KiB
Python
"""Founder-run read-only cached-artifact and exec-stream comparison.
|
|
|
|
Only reads known public scanner artifacts and target identities. The payload is
|
|
synthetic zero bytes, never a credential or repository data. No rollout, file
|
|
creation, chmod, patch, stage, rollback or receipt reset is performed.
|
|
"""
|
|
from pathlib import Path
|
|
import importlib.util
|
|
import json
|
|
import os
|
|
import subprocess
|
|
|
|
HERE=Path(__file__).resolve().parent
|
|
R4=HERE.parent/'native-r4'
|
|
EXPECTED='64c73100f2347470fda1b8b3194dbf6a8009291d9d80223af463b3c5ecab52fd'
|
|
OUTPUT=Path('/tmp/forge-scanner-stream-diagnostic-receipt.json')
|
|
|
|
|
|
def marker(raw):
|
|
for line in (raw or b'').splitlines():
|
|
try:
|
|
d=json.loads(line)
|
|
if d=={'cached_artifacts_verified':True}:return True
|
|
except (ValueError,TypeError):pass
|
|
return False
|
|
|
|
|
|
def check_script(c):
|
|
return '''set +vx
|
|
set -eu
|
|
export PATH=/usr/bin:/bin
|
|
test "$(id -u)" = 1000
|
|
for parent in /data /data/security /data/security/receive-secret-scan "$1" "$2"; do
|
|
test -d "$parent" && test ! -L "$parent"
|
|
done
|
|
verify() {
|
|
test -f "$1" && test ! -L "$1"
|
|
test "$(stat -c %u "$1")" = 1000
|
|
test "$(sha256sum "$1" | cut -d ' ' -f 1)" = "$2"
|
|
test "$(stat -c %a "$1")" = 755
|
|
}
|
|
verify "$1/gitleaks" "$4"
|
|
verify "$2/receive-secret-scan" "$3"
|
|
verify "$2/gitleaks" "$4"
|
|
test -f "$2/.migration-owner" && test ! -L "$2/.migration-owner"
|
|
test "$(cat "$2/.migration-owner")" = SYNC-SCANNER-JSON-DIGEST-20261101-R2
|
|
printf '{"cached_artifacts_verified":true}\\n'
|
|
'''
|
|
|
|
|
|
def main():
|
|
spec=importlib.util.spec_from_file_location('approved_r4',R4/'rollout.py');m=importlib.util.module_from_spec(spec);spec.loader.exec_module(m)
|
|
if m.verify_packet()!=EXPECTED:raise RuntimeError('approved-packet-drift')
|
|
c=json.loads((R4.parent/'native-r2/inputs.json').read_bytes())
|
|
env=dict(os.environ,KUBECONFIG='/home/worsch/.kube/config-railiance01')
|
|
for resource,name,uid in [('deployment',c['deployment_name'],c['deployment_uid']),('pod',c['pod'],c['pod_uid'])]:
|
|
q=subprocess.run(['kubectl','--request-timeout=8s','-n','forgejo','get',resource,name,'-o','jsonpath={.metadata.uid}'],capture_output=True,timeout=12,env=env)
|
|
if q.returncode or q.stdout.decode()!=uid:raise RuntimeError('target-read-or-identity-failed; raw diagnostics withheld')
|
|
payload=bytes(Path('/tmp/sync-scanner-candidate/receive-secret-scan-candidate').stat().st_size)
|
|
result={'read_only':True,'native_mutation':False,'rollout_packet_sha256':EXPECTED,'synthetic_payload_bytes':len(payload),'checks':[]}
|
|
for label,protocol,data in [('empty-default','true',b''),('payload-websocket','true',payload),('payload-spdy','false',payload)]:
|
|
probe_env=dict(env,KUBECTL_REMOTE_COMMAND_WEBSOCKETS=protocol)
|
|
argv=['kubectl','--request-timeout=8s','-n','forgejo','exec','-i',c['pod'],'-c',c['container'],'--','sh','-c',check_script(c),'sh',c['old_dir'],c['new_dir'],c['new_adapter_sha256'],c['scanner_sha256']]
|
|
try:
|
|
q=subprocess.run(argv,input=data,capture_output=True,timeout=12,env=probe_env)
|
|
item={'probe':label,'protocol':'WebSocket' if protocol=='true' else 'SPDY','returncode':q.returncode,'category':'completed' if q.returncode==0 else 'command-failed','remote_cache_marker_received':marker(q.stdout)}
|
|
except subprocess.TimeoutExpired as e:
|
|
item={'probe':label,'protocol':'WebSocket' if protocol=='true' else 'SPDY','category':'client-timeout','remote_cache_marker_received':marker(e.stdout)}
|
|
result['checks'].append(item);OUTPUT.write_text(json.dumps(result,indent=2)+'\n');print(json.dumps(item),flush=True)
|
|
print('receipt:',str(OUTPUT))
|
|
|
|
|
|
if __name__=='__main__':
|
|
try:main()
|
|
except Exception as e:
|
|
print(json.dumps({'result':'read-only-diagnostic-refused','reason':str(e) if isinstance(e,RuntimeError) else type(e).__name__}))
|
|
raise SystemExit(1)
|