feat(execution): bind immutable task grants before native effects
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a140c4-354a-7823-8f0d-8f717397f786
This commit is contained in:
codex 2026-10-16 07:51:42 +02:00
parent 4021ca5e79
commit d042d468df
7 changed files with 460 additions and 7 deletions

View file

@ -3,6 +3,8 @@
These tests do not contact estate OpenBao or claim production owner acceptance.
"""
import hashlib
from dataclasses import replace
import yaml
import http.client
import json
import shutil
@ -20,6 +22,7 @@ from test_task_execution_plan import compiler,request,m
from task_execution_journal import Binding,Cleanup,Journal,Refused
from task_execution_cleanup import run_cleanup
from task_execution_native_fixture import FixtureAdmission,NativeFixtureAdapter,PrivateSession
import task_execution_grant as grant
@pytest.fixture
@ -79,8 +82,29 @@ def owner(compiler,bao_dev,tmp_path):
# grant/expected-state digests remain deliberately synthetic owner fixtures.
from secrets_engine.execution_contract import ExecutionContract
descriptor=ExecutionContract.parse(descriptor).as_dict()
# Install an independently pinned synthetic grant source and its pure
# validator. No real CCR is written or approved in the owner checkout.
grantfile=compiler.config.platform_repo/'credential-change-requests/CCR-FIXTURE-GRANT.yaml'
grantfile.write_text('placeholder: true\n')
grantcode=Path(grant.__file__).resolve()
pins=dict(compiler.config.source_pins)
pins.update({str(grantfile):m.tagged(grantfile.read_bytes()),str(grantcode):m.tagged(grantcode.read_bytes())})
compiler=m.Compiler(replace(compiler.config,source_pins=tuple(pins.items()),grant_filename=grantfile.name))
plan=compiler.compile(req)
expected_execution,expected_openbao=compiler.grant_effects(plan,descriptor)
record={'id':descriptor['execution_grant_ccr_id'],'kind':'credential-change-request',
'schema_version':1,'request_type':'task-execution-grant','title':'Synthetic disposable fixture grant',
'status':'approved','created':'2026-10-16','updated':'2026-10-16',
'requester':{'agent':'fixture','reason':'Synthetic disposable proof; no founder approval'},
'review':{'required':True,'required_approvers':['railiance-platform-owner','secrets-engine-owner','access-engine-owner','approval-engine-owner'],'comments':[]},
'target':{'domain':'infotech','tenant':'platform','workload':'secrets-engine','environment':'production','purpose':'Synthetic metadata primitive proof'},
'execution':expected_execution,'openbao':expected_openbao,'evidence':{'events':[]}}
grantfile.write_text(yaml.safe_dump(record,sort_keys=False))
pins[str(grantfile)]=m.tagged(grantfile.read_bytes())
compiler=m.Compiler(replace(compiler.config,source_pins=tuple(pins.items())))
descriptor['grant_immutable_sha256']=grant.immutable_digest(record)
installed=m.encoded(descriptor)
plan=compiler.bind_execution(plan,descriptor,installed)
plan=compiler.bind_execution(compiler.compile(req),descriptor,installed)
binding=Binding('SYNTHETIC-APPROVAL-NOT-PRODUCTION',req['request_digest'],req['operation_id'],plan.content_digest)
state=tmp_path/'private-journal';state.mkdir(mode=0o700);journal=Journal(state)
admission=FixtureAdmission(installed,root,identity,process)
@ -225,8 +249,62 @@ def test_actual_native_policy_retirement_does_not_prove_failed_token_revoke(owne
def test_nonloopback_estate_origin_is_refused_before_native_transport(owner):
native,journal,binding,call,compiler,descriptor=owner
changed=dict(descriptor,backend_origin='https://estate.invalid:8200')
# The owner grant now also binds origin. Build a matching synthetic grant so
# this case reaches the adapter's independent nonloopback refusal; ordinary
# origin substitution is already refused earlier by the grant join.
record=yaml.safe_load(compiler.grant_record.read_text())
record['execution'],record['openbao']=compiler.grant_effects(compiler.compile(native.meta['request']),changed)
compiler.grant_record.write_text(yaml.safe_dump(record))
pins=dict(compiler.config.source_pins);pins[str(compiler.grant_record)]=m.tagged(compiler.grant_record.read_bytes())
changed_compiler=m.Compiler(replace(compiler.config,source_pins=tuple(pins.items())))
changed['grant_immutable_sha256']=grant.immutable_digest(record)
installed=m.encoded(changed)
plan=compiler.bind_execution(compiler.compile(native.meta['request']),changed,installed)
plan=changed_compiler.bind_execution(changed_compiler.compile(native.meta['request']),changed,installed)
changed_binding=Binding(binding.approval_id,binding.request_digest,binding.operation_id,plan.content_digest)
admission=FixtureAdmission(installed,native.admission.owner_token,native.admission.identity_session,native.admission.server_process)
with pytest.raises(Refused):NativeFixtureAdapter(compiler,plan,journal,changed_binding,admission)
with pytest.raises(Refused):NativeFixtureAdapter(changed_compiler,plan,journal,changed_binding,admission)
def test_changed_grant_immutable_join_refuses_before_any_native_grant(owner):
native,journal,binding,call,compiler,descriptor=owner
changed=dict(descriptor,grant_immutable_sha256='sha256:'+'0'*64)
with pytest.raises(m.Refused):compiler.bind_execution(compiler.compile(native.meta['request']),changed,m.encoded(changed))
assert call('GET','sys/policies/acl/'+native.policy_name)[0]==404
def test_grant_body_repin_cannot_replace_independently_derived_effects(owner):
native,journal,binding,call,compiler,descriptor=owner
record=yaml.safe_load(compiler.grant_record.read_text())
record['openbao']['policy']['hcl']='path "*" { capabilities = ["sudo"] }'
compiler.grant_record.write_text(yaml.safe_dump(record))
pins=dict(compiler.config.source_pins);pins[str(compiler.grant_record)]=m.tagged(compiler.grant_record.read_bytes())
changed_compiler=m.Compiler(replace(compiler.config,source_pins=tuple(pins.items())))
changed_descriptor=dict(descriptor,grant_immutable_sha256=grant.immutable_digest(record))
with pytest.raises(m.Refused):changed_compiler.bind_execution(changed_compiler.compile(native.meta['request']),changed_descriptor,m.encoded(changed_descriptor))
assert call('GET','sys/policies/acl/'+native.policy_name)[0]==404
def test_proposed_source_grant_never_materializes_even_with_matching_digest(owner):
native,journal,binding,call,compiler,descriptor=owner
record=yaml.safe_load(compiler.grant_record.read_text());record['status']='proposed'
compiler.grant_record.write_text(yaml.safe_dump(record))
pins=dict(compiler.config.source_pins);pins[str(compiler.grant_record)]=m.tagged(compiler.grant_record.read_bytes())
changed_compiler=m.Compiler(replace(compiler.config,source_pins=tuple(pins.items())))
changed_descriptor=dict(descriptor,grant_immutable_sha256=grant.immutable_digest(record))
installed=m.encoded(changed_descriptor)
plan=changed_compiler.bind_execution(changed_compiler.compile(native.meta['request']),changed_descriptor,installed)
changed_binding=Binding(binding.approval_id,binding.request_digest,binding.operation_id,plan.content_digest)
admission=FixtureAdmission(installed,native.admission.owner_token,native.admission.identity_session,native.admission.server_process)
with pytest.raises(Refused):NativeFixtureAdapter(changed_compiler,plan,journal,changed_binding,admission)
assert call('GET','sys/policies/acl/'+native.policy_name)[0]==404
def test_grant_source_drift_after_admission_prevents_protected_effect(owner):
native,journal,binding,call,compiler,*_=owner
with journal.locked():
admit(native,journal,binding)
record=yaml.safe_load(compiler.grant_record.read_text());record['execution']['cleanup'][0]['call_digest']='f'*64
compiler.grant_record.write_text(yaml.safe_dump(record))
with pytest.raises(m.Refused):native.materialize_grant()
assert call('GET','sys/policies/acl/'+native.policy_name)[0]==404
assert run_cleanup(journal,binding,native)['confirmed']