railiance-platform/tests/test_task_execution_grant.py
codex d042d468df
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
feat(execution): bind immutable task grants before native effects
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a140c4-354a-7823-8f0d-8f717397f786
2026-10-16 07:51:42 +02:00

98 lines
6.6 KiB
Python

"""Pure proposed grant projection: source provenance, never approval/native proof."""
import copy
from pathlib import Path
import sys
import pytest
sys.path.insert(0,str(Path(__file__).resolve().parents[1]/'scripts'))
import task_execution_grant as grant
@pytest.fixture
def record():
return {'id':'CCR-FIXTURE-GRANT','kind':'credential-change-request','schema_version':1,
'request_type':'task-execution-grant','title':'Proposed source fixture','status':'proposed',
'created':'2026-10-16','updated':'2026-10-16','requester':{'agent':'fixture','reason':'Local proof'},
'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':'fixture'},
'execution':{'action':'execute-ccr','descriptor_binding':{'operation_deadline':'2026-10-16T12:00:00Z','ccr_immutable_sha256':'sha256:'+'a'*64},
'source_pins':{'/fixture/source':'sha256:'+'b'*64},'cleanup':[{'kind':'task-token-revoke','call_digest':'c'*64}]},
'openbao':{'policy':{'name':'task-exec-fixture','hcl':'exact owner generated HCL'},
'token_create_body':{'ttl':'120s','num_uses':12,'renewable':False}},'evidence':{'events':[]}}
def comment():return {'at':'2026-10-16T12:00:00Z','reviewer':'railiance-platform-owner','decision':'approved','comment':'Source only; no execution authority'}
def event():return {'at':'2026-10-16T12:00:00Z','kind':'test','result':'unknown','reference_sha256':'sha256:'+'e'*64}
def test_only_typed_review_comments_and_evidence_events_are_excluded(record):
changed=copy.deepcopy(record);changed['review']['comments'].append(comment());changed['evidence']['events'].append(event())
assert grant.immutable_digest(changed)==grant.immutable_digest(record)
grant.require_append_only(record,changed)
assert record['review']['comments']==[] # detached immutable projection
@pytest.mark.parametrize('field',['status','updated','title','requester','target','execution','openbao','review'])
def test_every_other_root_or_effectful_field_changes_digest(record,field):
changed=copy.deepcopy(record)
if field=='status':changed[field]='approved'
elif field=='updated':changed[field]='2026-10-17'
elif field=='title':changed[field]='different title'
elif field=='requester':changed[field]['reason']='different requester reason'
elif field=='target':changed[field]['purpose']='different purpose'
elif field=='execution':changed[field]['source_pins']['/fixture/source']='sha256:'+'f'*64
elif field=='openbao':changed[field]['policy']['hcl']='different HCL'
elif field=='review':changed[field]['required_approvers'].reverse()
if field=='review':
with pytest.raises(grant.Refused):grant.immutable_digest(changed)
else:
assert grant.immutable_digest(changed)!=grant.immutable_digest(record)
with pytest.raises(grant.Refused):grant.require_append_only(record,changed)
@pytest.mark.parametrize('location,key',[('root','audit'),('root','receipts'),('root','evidence_exclusions'),('evidence','files'),('evidence','policy'),('review','approval_ref'),('review','exclusions')])
def test_guessed_evidence_exclusions_or_unknown_fields_refuse(record,location,key):
changed=copy.deepcopy(record)
(changed if location=='root' else changed[location])[key]=[]
with pytest.raises(grant.Refused):grant.immutable_digest(changed)
def test_append_only_rejects_removal_rewrite_and_reordering(record):
previous=copy.deepcopy(record);previous['review']['comments']=[comment(),dict(comment(),comment='Second')]
previous['evidence']['events']=[event()]
for field,value in [('comments',[]),('comments',[dict(comment(),decision='declined')]),('comments',list(reversed(previous['review']['comments']))),('events',[])]:
changed=copy.deepcopy(previous);changed['review' if field=='comments' else 'evidence'][field]=value
assert grant.immutable_digest(changed)==grant.immutable_digest(previous)
with pytest.raises(grant.Refused):grant.require_append_only(previous,changed)
@pytest.mark.parametrize('change',['policy','token_budget','cleanup','source','deadline','target_ccr','wrong_hash','grant_id'])
def test_bind_reconstructs_effects_and_exact_existing_descriptor_join(record,change):
expected=copy.deepcopy(record);changed=copy.deepcopy(record);digest=grant.immutable_digest(record);grant_id=record['id']
if change=='policy':changed['openbao']['policy']['hcl']='path "*" {}'
elif change=='token_budget':changed['openbao']['token_create_body']['num_uses']=13
elif change=='cleanup':changed['execution']['cleanup'][0]['kind']='kv-delete'
elif change=='source':changed['execution']['source_pins']['/fixture/source']='sha256:'+'f'*64
elif change=='deadline':changed['execution']['descriptor_binding']['operation_deadline']='2027-01-01T00:00:00Z'
elif change=='target_ccr':changed['execution']['descriptor_binding']['ccr_immutable_sha256']='sha256:'+'f'*64
elif change=='wrong_hash':digest='sha256:'+'f'*64
elif change=='grant_id':grant_id='CCR-OTHER'
with pytest.raises(grant.Refused):grant.bind(changed,grant_id=grant_id,expected_execution=expected['execution'],expected_openbao=expected['openbao'],expected_digest=digest)
def test_proposed_source_record_can_bind_without_becoming_approved(record):
projection=grant.bind(record,grant_id=record['id'],expected_execution=record['execution'],expected_openbao=record['openbao'],expected_digest=grant.immutable_digest(record))
assert projection['status']=='proposed'
@pytest.mark.parametrize('change',['schema_bool','comment_extra','event_extra','unaware_comment','event_missing','nan','tuple','integer_key','status_list'])
def test_closed_types_refuse_instead_of_silent_coercion(record,change):
if change=='schema_bool':record['schema_version']=True
elif change=='comment_extra':record['review']['comments']=[dict(comment(),policy='unreviewed')]
elif change=='event_extra':record['evidence']['events']=[dict(event(),token='never accepted')]
elif change=='unaware_comment':record['review']['comments']=[dict(comment(),at='2026-10-16T12:00:00')]
elif change=='event_missing':record['evidence']['events']=[{'kind':'test'}]
elif change=='nan':record['execution']['value']=float('nan')
elif change=='tuple':record['execution']['value']=(1,2)
elif change=='integer_key':record['execution'][1]='invalid'
elif change=='status_list':record['status']=[]
with pytest.raises(grant.Refused):grant.immutable_digest(record)