Execute task-plan catalogue validation from verified source bytes

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a13690-09e0-7cd1-964b-ec83cdbcd348
This commit is contained in:
codex 2026-10-13 20:55:05 +02:00
parent 7c79293831
commit b98d17c09f
5 changed files with 139 additions and 11 deletions

View file

@ -0,0 +1,16 @@
{
"schema": "platform-source-correction/v1",
"observed_at": "2026-10-13T18:53:24.250702+00:00",
"task": "RPF-WP-0035-T12",
"source_sha256": {
"scripts/task_execution_plan.py": "f81bd36523c42ce880111f774409cb7c554c6ffc06c2686a1864e6e6cd70111a",
"tests/test_task_execution_plan.py": "14c587c05a612955f6fc972ad3967a051b36fb127aecc246a28168331a034468",
"docs/task-execution-plan-compiler.md": "cf28c8e1ab702a66d5608f56566f184afd8d41b2693a38034e001de46f023b94"
},
"initial_regression": "cached catalogue validator replacement caused proposal refusal despite unchanged pinned source; regression failed before correction",
"test_command": "PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=/home/worsch/secrets-engine/src python3 -m pytest -q tests/test_task_execution_plan.py tests/test_task_execution_journal.py",
"compiler_tests_passed": 45,
"journal_tests_passed": 15,
"native_execution_authorized": false,
"independent_integration_review": "pending"
}

View file

@ -25,7 +25,13 @@ No other lane module, mount enable/config, KV read/write, token/SecretID deliver
AppRole mutation, caller HCL/JSON body, paths or command argv can be compiled.
The catalogue comes from a separately owner-pinned local Secrets catalogue file,
loaded by the actual installed/pinned `secrets_engine.catalog.validate_entry`.
loaded by `secrets_engine.catalog.validate_entry` executed directly from the
verified source snapshot, together with its pinned package/errors/redactor.
Each compilation uses temporary private module names and removes them on success
or failure. Canonical application imports remain untouched. Cached modules and
timestamp-based bytecode cannot substitute different code for the verified bytes;
new Secrets Engine imports outside the closed source set refuse. This is source
consistency within the admitted Python runtime, not a Python security sandbox.
Its catalogue id, existing mount, logical path, fields, production stage and
approval CCR reference must match the owner CCR. Engine-managed mounts and auth
are refused: existing delivery-auth metadata can be carried, but is never applied
@ -91,8 +97,14 @@ Token custody/revoke, temporary object removal, owner-private recovery after unk
and truthful native readback remain gates. General lifecycle/apply/exec/default
migration and SecretsT06/RPF35T02/T12 cannot close from this source component.
Validation:40 offline tests pass, covering real owner K8s/OIDC body generation,
Validation:45 compiler tests and 15 journal tests pass, covering real owner K8s/OIDC body generation,
exact catalogue unicode digest interoperability, refusal of alternate action/kind/
path/body/ordinal, re-pinned root policy, nonapproved/unconfirmed/replayed CCR,
engine-managed catalogue, forged immutable plan, source drift/duplicates and
symlink/hardlink/writable source. No native action, credential/Secret access or owner checkout writes.
October 13 correction: a regression first demonstrated that a cached validator
could replace the hashed source. Additional cases cover cached dependencies,
invalid catalogue acceptance through a substituted validator, and private-module
cleanup on both success and refusal. The October 11 v2 review remains evidence
for its original bytes only; it does not attest this corrected revision.

View file

@ -5,15 +5,17 @@ Six fixed calls on two structure objects. Recompile immediately before each call
Canonical PDP request digest is opaque: the authenticated mediator verifies it.
"""
from __future__ import annotations
import builtins
import hashlib
import json
import os
from pathlib import Path
import re
import stat
import sys
import types
from dataclasses import dataclass
from uuid import UUID
from uuid import UUID, uuid4
import yaml
class Refused(Exception): pass
@ -123,6 +125,49 @@ class Compiler:
finally:
if fd is not None: os.close(fd)
def _catalogue_entry(self, snapshot):
# Execute the verified bytes, not cached imports or timestamp-valid .pyc
# files. Private names support dataclass annotation lookup without replacing
# the application's secrets_engine modules, even during concurrent compiles.
prefix = '_platform_catalogue_' + uuid4().hex
modules = {}
registered = []
def snapshot_import(name, globals=None, locals=None, fromlist=(), level=0):
if name == 'secrets_engine' or name.startswith('secrets_engine.'):
need(level == 0 and name in modules, 'unpinned_catalogue_import')
module = modules[name]
if fromlist:
need(all(hasattr(module, item) for item in fromlist), 'unpinned_catalogue_import')
return module
return modules['secrets_engine']
need(level == 0, 'unpinned_catalogue_import')
return builtins.__import__(name, globals, locals, fromlist, level)
try:
sources = [('secrets_engine', self.dependencies[0]),
('secrets_engine.errors', self.dependencies[1]),
('secrets_engine.redact', self.dependencies[2]),
('secrets_engine.catalog', self.catalog_code)]
for name, path in sources:
private_name = prefix + name[len('secrets_engine'):]
module = types.ModuleType(private_name)
module.__file__ = str(path)
module.__package__ = prefix
module.__dict__['__builtins__'] = dict(vars(builtins), __import__=snapshot_import)
modules[name] = module
sys.modules[private_name] = module
registered.append(private_name)
if name != 'secrets_engine':
setattr(modules['secrets_engine'], name.rsplit('.', 1)[1], module)
exec(compile(snapshot[str(path)], str(path), 'exec'), module.__dict__)
return modules['secrets_engine.catalog'].validate_entry(
yaml.load(snapshot[str(self.catalog)], Loader=UniqueLoader),
source='<owner-pinned-catalogue>')
finally:
for name in registered:
sys.modules.pop(name, None)
def _derive(self):
# No native operations, CLI applier, subprocess, network or ambient credentials.
snapshot={p:self.read(Path(p)) for p in self.pins}
@ -147,13 +192,7 @@ class Compiler:
generated=owner.generated_policy_hcl(ccr)
need(owner.normalized_policy_body(snapshot[str(policy)].decode())==owner.normalized_policy_body(generated),'owner_policy_not_generated')
# Use the actual consumer catalog validator/defaults, not an alternative broker catalog.
import secrets_engine.catalog as catalogue
import secrets_engine.errors as errors_module
import secrets_engine.redact as redact_module
import secrets_engine
for module,p in [(catalogue,self.catalog_code),(secrets_engine,self.dependencies[0]),(errors_module,self.dependencies[1]),(redact_module,self.dependencies[2])]:
need(Path(module.__file__).resolve()==p,'catalogue_runtime_source_mismatch')
entry=catalogue.validate_entry(yaml.load(snapshot[str(self.catalog)],Loader=UniqueLoader),source='<owner-pinned-catalogue>')
entry=self._catalogue_entry(snapshot)
need(entry.id==ccr['access_frontdoor']['catalog_id'] and entry.stage=='prod'
and ccr['target']['environment']=='production' and entry.kind=='kv'
and entry.mount_management=='existing'

View file

@ -144,3 +144,52 @@ def test_oidc_role_body_reuses_owner_auth_payload_and_existing_mount(compiler):
assert call.path=='auth/netkingdom/role/synthetic-operator-workload-kv-read'
assert role['role_type']=='oidc' and role['policies']=='workload-kv-read-issue-core-runtime'
assert role['bound_claims']==source['openbao']['auth']['bound_claims']
def test_cached_catalogue_code_cannot_replace_verified_source(compiler, monkeypatch):
import secrets_engine.catalog as catalogue
expected = compiler.proposal()
def stale_validator(*args, **kwargs):
raise AssertionError('cached code differs from the pinned source')
monkeypatch.setattr(catalogue, 'validate_entry', stale_validator)
assert compiler.proposal() == expected
def test_cached_catalogue_dependency_cannot_replace_verified_source(compiler, monkeypatch):
import secrets_engine.catalog as catalogue
expected = compiler.proposal()
def stale_redactor(*args, **kwargs):
raise AssertionError('cached dependency differs from the pinned source')
monkeypatch.setattr(catalogue, 'looks_secret', stale_redactor)
assert compiler.proposal() == expected
def test_cached_validator_cannot_admit_invalid_pinned_catalogue(compiler, monkeypatch):
import secrets_engine.catalog as catalogue
accepted = catalogue.validate_entry(yaml.safe_load(compiler.catalog.read_text()), source='fixture')
data = yaml.safe_load(compiler.catalog.read_text())
del data['rotation']
compiler.catalog.write_text(yaml.safe_dump(data))
compiler = repin(compiler, compiler.catalog)
monkeypatch.setattr(catalogue, 'validate_entry', lambda *a, **kw: accepted)
with pytest.raises(m.Refused):
compiler.proposal()
@pytest.mark.parametrize('invalid', [False, True])
def test_snapshot_modules_removed_without_replacing_ambient_modules(compiler, invalid):
import secrets_engine.catalog
before = {k: v for k, v in sys.modules.items() if k.startswith('secrets_engine')}
if invalid:
compiler.catalog.write_text('id: incomplete\n')
compiler = repin(compiler, compiler.catalog)
with pytest.raises(m.Refused):
compiler.proposal()
else:
compiler.proposal()
assert {k: v for k, v in sys.modules.items() if k.startswith('secrets_engine')} == before
assert not any(k.startswith('_platform_catalogue_') for k in sys.modules)

View file

@ -8,7 +8,7 @@ status: blocked
flavor: implementation
owner: codex
created: "2026-09-05"
updated: "2026-10-11"
updated: "2026-10-13"
related:
- RPF-WP-0032
- RPF-WP-0033
@ -1213,3 +1213,15 @@ owner-private unknown recovery remain unimplemented integration/native gates.
Actual CCR42 has no eligible approved pilot/catalogue; no source metadata is
invented. T02 and original negative/capability/native evidence remain unchanged;
T11/T13 history retained, no defaultflip/nativego or SecretsT06 closure follows.
2026-10-13 T12 source correction: the compiler hashed the catalogue sources but
used cached Python modules with matching paths, which did not establish executing
those exact bytes. A failing regression reproduced the substitution. Catalogue,
package and dependencies now execute from the verified snapshot in temporary
private modules; ambient modules remain untouched and cleanup runs on refusal.
45 compiler plus 15 journal tests pass. The v2 source review remains historical;
this change needs its own integration review. T12 retains all existing full
contract, identity/consume, dispatch, custody and native acceptance requirements.
RPF-IN-0012 remains open for the independent contract/wrapped-delivery return;
this code correction does not authorize either operation.