Commits the 5 product paths and 7 gate helpers of the row 227 R4 source overlay on top of HEAD 5a0c801cb. scripts/case2/console.mjs is already committed at HEAD and is not part of this commit. The R4 independent review is sha256 6667baaa6af9d6d78a3ec7c64d9f3163ecbc350b94050f62899d9c01deabe002, verdict APPROVE source-only. Packet hashes: PACKET.sha256 4eb60915b2144137df9b5a42afd0c5b266718d71de051fb8be53a27430a2cad4, CURRENT-SOURCE.sha256 0cfd47c2073bc18a3ba7bff1ce6fdf39fe33a64b7fca209be71517b9feb89020. The 12 committed files match the review hash table, the applied overlay JSON (size, mode, hash) and the saved overlay tar. No scripts/test-case2.sh run exists on these bytes.
76 lines
4.0 KiB
Python
76 lines
4.0 KiB
Python
# Owned gate evidence only; raw bytes are retained before any control/registry parse.
|
|
import os,json,time,hashlib,stat
|
|
from pathlib import Path
|
|
class Evidence:
|
|
def __init__(self,root):
|
|
self.root=Path(root);self.root.mkdir(parents=True,exist_ok=False)
|
|
(self.root/'raw-inputs').mkdir();self.failures=[];self.unknown=False;self.inventory=[];self.saved=set()
|
|
self.journal=(self.root/'failure-journal.jsonl').open('x',buffering=1)
|
|
self.final=(self.root/'result.json').open('x');self.cleanup=(self.root/'cleanup.json').open('x')
|
|
def fail(self,code,unknown=True):
|
|
if code not in self.failures:self.failures.append(code)
|
|
self.unknown=self.unknown or unknown
|
|
try:self.journal.write(json.dumps(dict(at=time.time(),code=code))+'\n');self.journal.flush();os.fsync(self.journal.fileno())
|
|
except Exception:
|
|
try:os.write(2,b'gate-evidence-journal-failed\n')
|
|
except Exception:pass
|
|
def write(self,name,data):
|
|
try:
|
|
p=self.root/name;p.parent.mkdir(parents=True,exist_ok=True)
|
|
if not isinstance(data,bytes):data=(json.dumps(data,indent=2)+'\n').encode()
|
|
with p.open('wb') as f:f.write(data);f.flush();os.fsync(f.fileno())
|
|
return True
|
|
except Exception as exc:self.fail('evidence-write-'+type(exc).__name__);return False
|
|
def raw(self,path,category,missing_ok=False):
|
|
path=Path(path);fd=None
|
|
# Private capture limits, not product/API limits. The full admitted dependency manifest
|
|
# gets128MiB; admission8MiB and changing run logs64MiB; native/control records1MiB.
|
|
limit={'frozen-manifest':128*1024*1024,'admission':8*1024*1024,'run-control':64*1024*1024}.get(category,1024*1024)
|
|
def refuse(reason,info=None):
|
|
row=dict(path=str(path),category=category,disposition=reason,limit=limit,
|
|
mode=info.st_mode if info else None,size=info.st_size if info else None)
|
|
name=hashlib.sha256(json.dumps(row,sort_keys=True).encode()).hexdigest()
|
|
self.write('raw-refusals/'+name+'.json',row);self.inventory.append(row);self.fail('raw-'+reason)
|
|
try:
|
|
# Nonblocking open cannot wait for a FIFO writer; descriptor validation precedes READ.
|
|
fd=os.open(path,os.O_RDONLY|os.O_NOFOLLOW|os.O_NONBLOCK|os.O_CLOEXEC)
|
|
info=os.fstat(fd)
|
|
if not stat.S_ISREG(info.st_mode):refuse('nonregular-refused',info);return None
|
|
if info.st_size<0 or info.st_size>limit:refuse('size-refused',info);return None
|
|
# Read only the descriptor's initial finite size, never a growing stream through EOF.
|
|
chunks=[];remaining=info.st_size
|
|
while remaining:
|
|
block=os.read(fd,min(remaining,65536))
|
|
if not block:break
|
|
chunks.append(block);remaining-=len(block)
|
|
data=b''.join(chunks);after=os.fstat(fd)
|
|
sha=hashlib.sha256(data).hexdigest();key=(str(path),sha)
|
|
if key not in self.saved:
|
|
if self.write('raw-inputs/'+category+'/'+sha+'.raw',data):
|
|
self.saved.add(key);self.inventory.append(dict(path=str(path),category=category,sha256=sha,bytes=len(data),
|
|
initialSize=info.st_size,observedSizeAfter=after.st_size,
|
|
disposition='snapshot' if len(data)==info.st_size==after.st_size else 'finite-prefix-snapshot'))
|
|
return data
|
|
except FileNotFoundError:
|
|
if not missing_ok:self.fail('required-raw-input-missing')
|
|
except Exception as exc:refuse('capture-'+type(exc).__name__)
|
|
finally:
|
|
if fd is not None:
|
|
try:os.close(fd)
|
|
except Exception as exc:self.fail('raw-close-'+type(exc).__name__)
|
|
return None
|
|
def parse(self,path,category,missing_ok=False):
|
|
raw=self.raw(path,category,missing_ok)
|
|
if raw is None:return None
|
|
try:return json.loads(raw)
|
|
except Exception as exc:self.fail('parse-'+category+'-'+type(exc).__name__);return None
|
|
def inventory_write(self):return self.write('raw-inventory.json',self.inventory)
|
|
def reserved(self,handle,data):
|
|
try:
|
|
handle.seek(0);handle.truncate();handle.write(json.dumps(data,indent=2)+'\n');handle.flush();os.fsync(handle.fileno());return True
|
|
except Exception as exc:
|
|
self.fail('final-receipt-'+type(exc).__name__)
|
|
try:os.write(2,b'gate-final-receipt-failed; nonacceptance\n')
|
|
except Exception:pass
|
|
return False
|