Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a126e1-76f8-7643-8cf3-e93336539ade
266 lines
12 KiB
Python
266 lines
12 KiB
Python
#!/usr/bin/env python3
|
|
"""Owner-held verified KeyCape CLI files -> inherited pipe; never normal output.
|
|
No login, secret retrieval, network, executor launch or authenticated admission.
|
|
Decoded claims below are shape/lifetime checks, not signature verification.
|
|
"""
|
|
import argparse
|
|
import base64
|
|
import contextlib
|
|
import fcntl
|
|
import json
|
|
import math
|
|
import os
|
|
from pathlib import Path
|
|
import signal
|
|
import stat
|
|
import threading
|
|
import time
|
|
|
|
MAX_FILE = 32768
|
|
MAX_TOKEN = 16384
|
|
|
|
class Refused(Exception):
|
|
pass
|
|
|
|
def need(condition, code):
|
|
if not condition:
|
|
raise Refused(code)
|
|
|
|
def unique(pairs):
|
|
out = {}
|
|
for key, value in pairs:
|
|
need(key not in out, 'duplicate_json_key')
|
|
out[key] = value
|
|
return out
|
|
|
|
def decode(raw):
|
|
try:
|
|
return json.loads(raw, object_pairs_hook=unique,
|
|
parse_constant=lambda _: (_ for _ in ()).throw(Refused('nonfinite_json')))
|
|
except Refused:
|
|
raise
|
|
except Exception:
|
|
raise Refused('invalid_json') from None
|
|
|
|
@contextlib.contextmanager
|
|
def deadline(seconds):
|
|
need(type(seconds) in (int, float) and math.isfinite(seconds) and 0 < seconds <= 5,
|
|
'invalid_delivery_budget')
|
|
need(threading.current_thread() is threading.main_thread(), 'main_thread_required')
|
|
need(signal.getitimer(signal.ITIMER_REAL) == (0.0, 0.0), 'existing_timer_refused')
|
|
previous = signal.getsignal(signal.SIGALRM)
|
|
def expired(*_):
|
|
raise Refused('delivery_timeout')
|
|
try:
|
|
signal.signal(signal.SIGALRM, expired)
|
|
signal.setitimer(signal.ITIMER_REAL, seconds)
|
|
yield
|
|
finally:
|
|
signal.setitimer(signal.ITIMER_REAL, 0)
|
|
signal.signal(signal.SIGALRM, previous)
|
|
|
|
READY_SCHEMA = 'key39-private-input-ready/v1'
|
|
READY_KEYS = {'schema','session_nonce','proposal_id','target','executor_sha256',
|
|
'envelope_sha256','bao_health_identity_sha256','valid_until_epoch',
|
|
'receive_budget_seconds'}
|
|
|
|
def finite(value):
|
|
try:
|
|
return type(value) in (int,float) and math.isfinite(value)
|
|
except (OverflowError,ValueError):
|
|
return False
|
|
|
|
def endpoint(fd, writable):
|
|
need(type(fd) is int and fd >= 3, 'private_pipe_fd_required')
|
|
try:
|
|
identity = os.fstat(fd)
|
|
mode = fcntl.fcntl(fd,fcntl.F_GETFL) & os.O_ACCMODE
|
|
need(stat.S_ISFIFO(identity.st_mode) and mode ==
|
|
(os.O_WRONLY if writable else os.O_RDONLY), 'private_pipe_fd_required')
|
|
for standard in (0,1,2):
|
|
try:
|
|
current = os.fstat(standard)
|
|
except OSError:
|
|
continue
|
|
need((identity.st_dev,identity.st_ino) != (current.st_dev,current.st_ino),
|
|
'stdio_pipe_alias_refused')
|
|
return identity.st_dev,identity.st_ino
|
|
except OSError:
|
|
raise Refused('private_pipe_fd_required') from None
|
|
|
|
def wait_ready(fd, expected):
|
|
need(isinstance(expected,dict) and set(expected) == READY_KEYS - {'schema','receive_budget_seconds'},
|
|
'expected_child_pins_required')
|
|
need(finite(expected['valid_until_epoch']) and expected['valid_until_epoch'] > time.time(),
|
|
'ready_window_expired')
|
|
raw = b''
|
|
while len(raw) < 4096:
|
|
chunk = os.read(fd,1)
|
|
need(chunk, 'child_ready_missing')
|
|
if chunk == b'\n':
|
|
break
|
|
raw += chunk
|
|
else:
|
|
raise Refused('child_ready_oversized')
|
|
marker = decode(raw)
|
|
need(isinstance(marker,dict) and set(marker) == READY_KEYS
|
|
and marker.get('schema') == READY_SCHEMA, 'child_ready_invalid')
|
|
need(all(marker.get(key) == value for key,value in expected.items()), 'child_ready_mismatch')
|
|
need(finite(marker['valid_until_epoch']) and finite(marker['receive_budget_seconds'])
|
|
and 0 < marker['receive_budget_seconds'] <= 15
|
|
and marker['valid_until_epoch'] > time.time(),
|
|
'ready_window_expired')
|
|
return marker
|
|
|
|
def claims(token, kind, now):
|
|
need(isinstance(token, str) and 0 < len(token) <= MAX_TOKEN, 'invalid_token_shape')
|
|
parts = token.split('.')
|
|
need(len(parts) == 3 and all(parts), 'invalid_token_shape')
|
|
try:
|
|
obj = decode(base64.urlsafe_b64decode(parts[1] + '=' * (-len(parts[1]) % 4)))
|
|
need(isinstance(obj, dict), 'invalid_token_shape')
|
|
exp, iat = obj.get('exp'), obj.get('iat')
|
|
need(type(exp) in (int,float) and type(iat) in (int,float)
|
|
and math.isfinite(exp) and math.isfinite(iat)
|
|
and iat <= now + 30 and iat < exp and exp >= now + 300, 'token_window_invalid')
|
|
need(obj.get('iss') == 'https://kc.coulomb.social', 'token_source_invalid')
|
|
if kind == 'human':
|
|
need(obj.get('principal_type') == 'human'
|
|
and obj.get('aud') == 'netkingdom-bootstrap-console'
|
|
and isinstance(obj.get('sub'), str) and obj['sub'], 'human_shape_invalid')
|
|
assurance = obj.get('assurance', {})
|
|
need(isinstance(assurance,dict) and assurance.get('level') == 'aal2'
|
|
and assurance.get('mfa') is True, 'human_mfa_shape_invalid')
|
|
else:
|
|
need(obj.get('principal_type') == 'service'
|
|
and obj.get('aud') == 'codex-railiance-platform'
|
|
and obj.get('sub') == 'service:codex:railiance-platform'
|
|
and obj.get('tenant') == 'tenant:platform'
|
|
and obj.get('roles') == ['coding-agent']
|
|
and obj.get('scope') == 'openbao:login', 'coding_agent_shape_invalid')
|
|
except Refused:
|
|
raise
|
|
except Exception:
|
|
raise Refused('invalid_token_shape') from None
|
|
return token
|
|
|
|
def load(path, kind, now):
|
|
p = Path(path)
|
|
need(p.is_absolute() and p.parent.resolve() == p.parent, 'private_path_required')
|
|
need(not any((ancestor / '.git').is_file() or (ancestor / '.git' / 'HEAD').exists()
|
|
for ancestor in p.parents), 'git_path_refused')
|
|
directory = os.open(p.parent, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW)
|
|
try:
|
|
ds = os.fstat(directory)
|
|
need(ds.st_uid == os.getuid() and stat.S_IMODE(ds.st_mode) == 0o700,
|
|
'private_directory_required')
|
|
fd = os.open(p.name, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK, dir_fd=directory)
|
|
try:
|
|
original = os.fstat(fd)
|
|
need(stat.S_ISREG(original.st_mode) and original.st_uid == os.getuid()
|
|
and stat.S_IMODE(original.st_mode) == 0o600 and original.st_nlink == 1
|
|
and 0 < original.st_size <= MAX_FILE, 'private_file_required')
|
|
raw = b''
|
|
while len(raw) <= MAX_FILE:
|
|
chunk = os.read(fd, MAX_FILE + 1 - len(raw))
|
|
if not chunk:
|
|
break
|
|
raw += chunk
|
|
need(len(raw) <= MAX_FILE, 'private_file_oversized')
|
|
after = os.fstat(fd)
|
|
need((original.st_size,original.st_mtime_ns,original.st_ctime_ns) ==
|
|
(after.st_size,after.st_mtime_ns,after.st_ctime_ns), 'private_file_changed')
|
|
obj = decode(raw)
|
|
need(isinstance(obj,dict) and obj.get('token_type','').lower() == 'bearer',
|
|
'invalid_cli_token_file')
|
|
token = claims(obj.get('access_token'), kind, now)
|
|
return token, directory, p.name, original
|
|
finally:
|
|
os.close(fd)
|
|
except BaseException:
|
|
os.close(directory)
|
|
raise
|
|
|
|
def consume(item):
|
|
# Comparison is not atomic inode-conditioned unlink. Continuous exclusive
|
|
# same-UID directory custody is mandatory; concurrent writers are outside scope.
|
|
_, directory, name, original = item
|
|
current = os.stat(name, dir_fd=directory, follow_symlinks=False)
|
|
need((current.st_dev,current.st_ino,current.st_uid,current.st_nlink,current.st_size,
|
|
current.st_mtime_ns,current.st_ctime_ns) ==
|
|
(original.st_dev,original.st_ino,original.st_uid,original.st_nlink,original.st_size,
|
|
original.st_mtime_ns,original.st_ctime_ns), 'private_file_changed')
|
|
os.unlink(name, dir_fd=directory)
|
|
|
|
def deliver(human_path, agent_path, fd, *, ready_fd=None, expected=None,
|
|
consume_owner_files=False, budget=5):
|
|
receipt = {'adapter_performs_native_backend_actions': False, 'native_accepted': False,
|
|
'private_input_delivery_confirmed': False, 'caller_path_removal_completed': False,
|
|
'exclusive_same_uid_custody_required': True, 'atomic_inode_delete_guaranteed': False,
|
|
'child_ready_matched': False,
|
|
'credential_files_remain_possible': True, 'credential_copy_cleanup_verified': False,
|
|
'private_input_delivery_unknown': False}
|
|
items = []
|
|
writing = False
|
|
try:
|
|
need(consume_owner_files is True, 'explicit_owner_file_consumption_required')
|
|
data_identity = endpoint(fd,True)
|
|
control_identity = endpoint(ready_fd,False)
|
|
need(data_identity != control_identity, 'data_control_pipe_alias_refused')
|
|
need(isinstance(expected,dict) and finite(expected.get('valid_until_epoch')),
|
|
'expected_child_pins_required')
|
|
budget = min(budget, expected['valid_until_epoch'] - time.time())
|
|
with deadline(budget):
|
|
marker = wait_ready(ready_fd,expected)
|
|
receipt['child_ready_matched'] = True
|
|
remaining_alarm = signal.getitimer(signal.ITIMER_REAL)[0]
|
|
remaining = min(remaining_alarm, marker['receive_budget_seconds'],
|
|
expected['valid_until_epoch'] - time.time())
|
|
need(remaining > 0, 'ready_window_expired')
|
|
# Clip the existing timer; never restart or extend its original budget.
|
|
signal.setitimer(signal.ITIMER_REAL, remaining)
|
|
delivery_deadline = time.monotonic() + remaining
|
|
now = time.time()
|
|
items.append(load(human_path, 'human', now))
|
|
items.append(load(agent_path, 'coding-agent', now))
|
|
need((items[0][3].st_dev,items[0][3].st_ino) !=
|
|
(items[1][3].st_dev,items[1][3].st_ino), 'distinct_files_required')
|
|
payload = json.dumps({'human_token':items[0][0], 'coding_agent_token':items[1][0]},
|
|
separators=(',', ':')).encode() + b'\n'
|
|
# Unlink inspected caller-owned inodes; Python memory is not a zeroization guarantee.
|
|
need(time.time() < expected['valid_until_epoch'] and
|
|
time.monotonic() < delivery_deadline, 'ready_window_expired')
|
|
for item in items:
|
|
consume(item)
|
|
receipt.update(caller_path_removal_completed=True)
|
|
writing = True
|
|
offset = 0
|
|
while offset < len(payload):
|
|
need(time.time() < expected['valid_until_epoch'] and
|
|
time.monotonic() < delivery_deadline, 'ready_window_expired')
|
|
written = os.write(fd, payload[offset:])
|
|
need(written > 0, 'private_delivery_failed')
|
|
offset += written
|
|
need(time.time() < expected['valid_until_epoch'] and
|
|
time.monotonic() < delivery_deadline, 'ready_window_expired')
|
|
receipt['private_input_delivery_confirmed'] = True
|
|
except Refused as error:
|
|
receipt['failure_code'] = str(error)
|
|
except Exception:
|
|
receipt['failure_code'] = 'private_delivery_failed'
|
|
finally:
|
|
if writing and not receipt['private_input_delivery_confirmed']:
|
|
receipt['private_input_delivery_unknown'] = True
|
|
for item in items:
|
|
os.close(item[1])
|
|
# Launcher owns these endpoints: EOF even after partial delivery.
|
|
for owned in {fd,ready_fd}:
|
|
if type(owned) is int and owned >= 3:
|
|
try:
|
|
os.close(owned)
|
|
except OSError:
|
|
pass
|
|
return receipt
|
|
|
|
# Deliberately no standalone ready-file/ready-boolean CLI. Only the reviewed
|
|
# source launcher supplies its own captured child-control endpoint and exact pins.
|