"""Bounded runtime reconciliation of fixed host-agent result evidence.""" import json import os from pathlib import Path import stat from host_agent_state import MAX_STATE_BYTES from runtime_security import reject_reparse_components HOST_RESULT_DIRECTORY = Path('/data/host-agent-results') HOST_ROOT_UID = 0 HOST_RUNTIME_GID = 10001 class HostResultError(RuntimeError): pass def fixed_result_directory_is_safe(): try: reject_reparse_components(HOST_RESULT_DIRECTORY) details = os.stat(HOST_RESULT_DIRECTORY, follow_symlinks=False) return ( stat.S_ISDIR(details.st_mode) and details.st_uid == HOST_ROOT_UID and details.st_gid == HOST_RUNTIME_GID and stat.S_IMODE(details.st_mode) == 0o750 ) except Exception: return False def _read_result(operation_id): path = HOST_RESULT_DIRECTORY / f'{operation_id}.json' descriptor = None try: flags = os.O_RDONLY | getattr(os, 'O_CLOEXEC', 0) | getattr(os, 'O_NOFOLLOW', 0) descriptor = os.open(path, flags) before = os.fstat(descriptor) if ( not stat.S_ISREG(before.st_mode) or before.st_nlink != 1 or before.st_uid != HOST_ROOT_UID or before.st_gid != HOST_RUNTIME_GID or stat.S_IMODE(before.st_mode) != 0o640 ): raise HostResultError('host result metadata is invalid') with os.fdopen(descriptor, 'rb') as handle: descriptor = None payload = handle.read(MAX_STATE_BYTES + 1) after = os.fstat(handle.fileno()) current = os.stat(path, follow_symlinks=False) identity = lambda item: ( item.st_dev, item.st_ino, item.st_size, getattr(item, 'st_mtime_ns', None), getattr(item, 'st_ctime_ns', None), ) if ( len(payload) > MAX_STATE_BYTES or identity(before) != identity(after) or identity(after) != identity(current) ): raise HostResultError('host result changed during read') value = json.loads(payload.decode('ascii')) canonical = json.dumps( value, sort_keys=True, separators=(',', ':'), ensure_ascii=True, allow_nan=False, ).encode('ascii') if canonical != payload: raise HostResultError('host result is not canonical') return payload except FileNotFoundError: return None except HostResultError: raise except Exception: raise HostResultError('host result is invalid') from None finally: if descriptor is not None: os.close(descriptor) def reconcile_pending_host_results(database, *, limit=32): if not fixed_result_directory_is_safe(): return 0 reconciled = 0 for operation in database.pending_runtime_agent_operations(limit=limit): envelope = _read_result(operation['operation_id']) if envelope is None: continue if database.reconcile_runtime_operation_result(envelope) is not None: reconciled += 1 return reconciled