From a43992ba237065cd3ed550afa57387c5ec203548 Mon Sep 17 00:00:00 2001 From: EHotwagner Date: Mon, 5 Oct 2026 03:05:54 +0200 Subject: [PATCH 1/2] Bind native seed startup to its fresh fixed operation root --- .../fixtures/seed-attribution-helper/startup_runner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Broker.NativeProof/RuntimeEvidence/fixtures/seed-attribution-helper/startup_runner.py b/tests/Broker.NativeProof/RuntimeEvidence/fixtures/seed-attribution-helper/startup_runner.py index 47e86ba..4a337d1 100644 --- a/tests/Broker.NativeProof/RuntimeEvidence/fixtures/seed-attribution-helper/startup_runner.py +++ b/tests/Broker.NativeProof/RuntimeEvidence/fixtures/seed-attribution-helper/startup_runner.py @@ -7,7 +7,7 @@ from runtime_socket import RuntimeSocket from stock_bootstrap import authenticate_ready,authenticate_metadata,authenticate_setup,persist_authenticated_snapshots from runtime_identity import proc_bytes,start_ticks,verify_executable_map_lines,verify_host_dynamic_map_lines -ROOT='/tmp/bar-stock-seed-attribution-20261004';PYTHON='/usr/bin/python3.14' +ROOT='/tmp/bar-stock-seed-attribution-20261005-v1';PYTHON='/usr/bin/python3.14' need=m.need;clock=m.clock RUNTIME_SOCKET=None From 1a5b6fea0451ef685e7b273c90ce2bcd33a9d543 Mon Sep 17 00:00:00 2001 From: EHotwagner Date: Mon, 5 Oct 2026 03:37:52 +0200 Subject: [PATCH 2/2] Separate fixed native mapping semantics from observed placement custody --- .../helper/mapping_profile.py | 75 +++++++++-- .../helper/test_mapping_profile.py | 117 ++++++++++++++++++ .../run-source-controls.py | 4 +- .../seed-attribution-helper/startup_runner.py | 4 +- 4 files changed, 189 insertions(+), 11 deletions(-) create mode 100644 tests/Broker.NativeProof/RuntimeEvidence/fixtures/seed-attribution-helper/helper/test_mapping_profile.py diff --git a/tests/Broker.NativeProof/RuntimeEvidence/fixtures/seed-attribution-helper/helper/mapping_profile.py b/tests/Broker.NativeProof/RuntimeEvidence/fixtures/seed-attribution-helper/helper/mapping_profile.py index 463b799..d914194 100644 --- a/tests/Broker.NativeProof/RuntimeEvidence/fixtures/seed-attribution-helper/helper/mapping_profile.py +++ b/tests/Broker.NativeProof/RuntimeEvidence/fixtures/seed-attribution-helper/helper/mapping_profile.py @@ -22,19 +22,78 @@ def admitted_pins(p): pins={} for name in ['originalDataPins','hostPins','clrPins','elfPins','pythonPins','authorityPins']: for pin in p[name]: - prior=pins.get(pin['path']);need(prior is None or prior['sha256']==pin['sha256'],'mapping original pin ambiguity');pins[pin['path']]=pin + prior=pins.get(pin['path']);need(prior is None or prior==pin,'mapping original pin ambiguity');pins[pin['path']]=pin return pins -SUCCESSOR_BASE_ALLOWED_SHA=None # Must be selected as an exact source literal after fresh placement. -SUCCESSOR_MAPPING_DELTA_SHA=None +HISTORICAL_PROFILE_SHA='b2d3dd34b88a838a073fa64aa514681c01f1dfee0bd4388fea8441a87bc93dcf' +BASELINE_SHA='11224181c3b33d6894a35cc7e32f655b2087442e4ba4958ef7700dc2c333cad0' +HISTORICAL_ROWS_SHA='8b5d4e2c59978b2e5f0d3d6457a3276935de52859279a0047102017ebd11324c' +RESULT_ROWS_SHA='82cf6fef0c42fd06f4455030af39bb56aee210f7518531a9361ea435fdeb7022' +SUCCESSOR_BASE_ALLOWED_SHA='9d9a26a4c655f07c15072ddd715917e5ae7dbb77ec701e5ddb450ef8ab5f16f8' +SUCCESSOR_MAPPING_DELTA_SHA='e90554a5a7ead26b9db6eb3d250dd732bba1677842051406cf4d192437971060' +RETAINED_PHYSICAL_ROWS_SHA='33f0adb4e40baf2da15d1794e46311b66ecb1bbb2c80cf517ce98d2f83d0d07a' +BASE='/home/developer/.local/share/fs-gg-private' +HISTORICAL_DATA=BASE+'/bar-stock-policy968-private-input-successor-20261003/packet/runtime-data' +DATA=BASE+'/bar-native-seed-data-preparation-20261005-v1/packet/runtime-data' +HOST=BASE+'/bar-native-seed-input-preparation-20261005-v1/native-host/Broker.NativeProof' +POLICY=BASE+'/bar-native-seed-policy-adoption-root-operation-20261005-v1/placement/managed/RuntimeEvidence' +OLD_HOST='/tmp/bar-stock-fb00-managed-artifact-preparation-synthetic-pdb-successor-20261004/native-host/Broker.NativeProof' +AI={'officialCInterface':'AI/Interfaces/C/0.1/libAIInterface.so','highBarPlugin':'AI/Skirmish/highBar/stable/libSkirmishAI.so','stockOpponentNullAI':'AI/Skirmish/NullAI/0.1/libSkirmishAI.so'} +SEMANTIC_FIELDS={'declaredPath','canonicalPath','kind','baseAllowedKey','sha256','bytes'} +def semantic_digest(value): + return hashlib.sha256(json.dumps(value,sort_keys=True,separators=(',',':'),ensure_ascii=True,allow_nan=False).encode()).hexdigest() +def semantic_row(row): + return {**{k:row[k]for k in ['declaredPath','canonicalPath','kind','baseAllowedKey']},'sha256':row['pin']['sha256'],'bytes':row['pin']['bytes']} +def unique(pairs): + result={} + for k,v in pairs:need(k not in result,'duplicate mapping JSON key');result[k]=v + return result +def nonfinite(value):raise ValueError('nonfinite mapping JSON value') +def load_profile(path): + # Read one exact bounded private regular file; duplicate/nonfinite JSON refuses. + p=Path(path);before=p.lstat();need(stat.S_ISREG(before.st_mode)and before.st_nlink==1 and before.st_uid==os.geteuid()and stat.S_IMODE(before.st_mode)==0o600 and before.st_size<=16777216,'closed mapping profile custody') + fd=os.open(p,os.O_RDONLY|os.O_NOFOLLOW|os.O_CLOEXEC) + try: + opened=os.fstat(fd);body=b'' + while len(body)<=16777216: + chunk=os.read(fd,min(65536,16777217-len(body))) + if not chunk:break + body+=chunk + after=os.fstat(fd) + finally:os.close(fd) + fields=['st_dev','st_ino','st_mode','st_uid','st_nlink','st_size','st_mtime_ns','st_ctime_ns'];current=p.lstat() + need(len(body)==before.st_size and all(getattr(before,k)==getattr(opened,k)==getattr(after,k)==getattr(current,k)for k in fields),'stable complete mapping profile') + return json.loads(body,object_pairs_hook=unique,parse_constant=nonfinite) +def semantic_delta(baseline): + need(set(baseline)=={'schema','historicalProfileSha256','baseAllowed','rows'}and baseline['schema']=='bar.native-seed-semantic-mapping-baseline/v1'and baseline['historicalProfileSha256']==HISTORICAL_PROFILE_SHA and semantic_digest(baseline)==BASELINE_SHA,'exact historical semantic baseline') + rows=baseline['rows'];need(type(rows)is list and len(rows)==457 and rows==sorted(rows,key=lambda r:r['declaredPath'])and all(set(r)==SEMANTIC_FIELDS for r in rows)and semantic_digest(rows)==HISTORICAL_ROWS_SHA and semantic_digest(baseline['baseAllowed'])==HISTORICAL_BASE_ALLOWED_SHA,'complete ordered historical mapping rows') + by={r['declaredPath']:r for r in rows};need(len(by)==457,'unique baseline rows') + roles={'host':OLD_HOST,**{k:HISTORICAL_DATA+'/'+v for k,v in AI.items()}} + removed=[{'role':role,'row':by[path]}for role,path in sorted(roles.items())] + new_roles={k:{**by[HISTORICAL_DATA+'/'+suffix],'declaredPath':DATA+'/'+suffix,'canonicalPath':DATA+'/'+suffix,'baseAllowedKey':DATA+'/'+suffix}for k,suffix in AI.items()} + for role,path,sha in [('host',HOST,'0f11cb2012771c17a3f191ac912633586ee5f35a1969e2f8bfaa6d54ff7aec28'),('policy',POLICY,'0dc6787b588acf19b07f1cd80aab98cbd9c250dbba2b5e00c382706bcfefbd84')]:new_roles[role]=dict(declaredPath=path,canonicalPath=path,kind='existingExecutable',baseAllowedKey=path,sha256=sha,bytes=78256) + added=[{'role':role,'row':row}for role,row in sorted(new_roles.items())];engine={'role':'engine','row':by[HISTORICAL_DATA+'/spring-headless']} + result=sorted([r for r in rows if r['declaredPath']not in roles.values()]+list(new_roles.values()),key=lambda r:r['declaredPath']);base={r['baseAllowedKey']:r['sha256']for r in result if r['kind']=='existingExecutable'} + delta=dict(schema='bar.native-seed-semantic-mapping-delta/v2',historicalProfileSha256=HISTORICAL_PROFILE_SHA,historicalBaseAllowedSha256=HISTORICAL_BASE_ALLOWED_SHA,historicalRowsSha256=HISTORICAL_ROWS_SHA,retainedClrPinsSha256=CLR_PINS_SHA,removed=removed,added=added,retainedEngine=engine,retainedRows=453,resultRowCount=458,resultExistingExecutableCount=145,resultClrPECount=313,resultRowsSha256=semantic_digest(result),resultBaseAllowedSha256=semantic_digest(base)) + need(delta['resultRowsSha256']==RESULT_ROWS_SHA and delta['resultBaseAllowedSha256']==SUCCESSOR_BASE_ALLOWED_SHA and semantic_digest(delta)==SUCCESSOR_MAPPING_DELTA_SHA,'independent closed semantic golden join') + return delta,result,base def validate_schema(p): - need(p.get('preparationReady') is True and SUCCESSOR_BASE_ALLOWED_SHA is not None and SUCCESSOR_MAPPING_DELTA_SHA is not None,'fresh finite mapping delta not yet selected') - need(hashlib.sha256(json.dumps(p['mappingDelta'],sort_keys=True,separators=(',',':')).encode()).hexdigest()==SUCCESSOR_MAPPING_DELTA_SHA,'literal successor delta') - + need(p.get('preparationReady') is True,'actual mapping preparation remains required') + delta,projection,base=semantic_delta(p['mappingBaseline']);need(p['mappingDelta']==delta,'exact closed semantic delta') + physical=p['executableMappingIdentities']['rows'];need(len(physical)==458 and sorted(map(semantic_row,physical),key=lambda r:r['declaredPath'])==projection,'complete actual physical→semantic projection') + removed={x['row']['declaredPath']for x in delta['removed']};added={x['row']['declaredPath']for x in delta['added']} + unchanged=sorted((r for r in physical if r['declaredPath']not in added),key=lambda r:r['declaredPath']);need(len(unchanged)==453 and semantic_digest(unchanged)==RETAINED_PHYSICAL_ROWS_SHA,'original retained physical row/ancestor custody') + need(p['nativeHost']==HOST and p['policy']['apphost']==POLICY and p['dataRoot']==DATA and p['engine']==HISTORICAL_DATA+'/spring-headless','exact selected native paths') + need(p['requiredHostMappings']==[HOST]+[x for x in p['allowedExecutableMappings']if Path(x).name in('libcoreclr.so','libclrjit.so')],'exact three required host maps') + need(set(p['requiredEngineMappings'])=={'engine',*AI},'closed four native engine roles') + for role,path in {'engine':p['engine'],**{k:DATA+'/'+v for k,v in AI.items()}}.items(): + entry=p['requiredEngineMappings'][role];selected=delta['retainedEngine']['row']if role=='engine'else next(x['row']for x in delta['added']if x['role']==role) + need(entry=={'path':path,'role':role,'sha256':selected['sha256']},'source-bound required engine role') + need(any(x['path']==p['engine']for x in p['authorityPins']),'retained original engine explicitly admitted outside fresh data') table=p['executableMappingIdentities'];need(set(table)=={'schema','baseAllowed','clrPinCount','rows'} and table['schema']==SCHEMA and table['clrPinCount']==len(p['clrPins'])==335,'closed mapping identity table') - digest=lambda value:hashlib.sha256(json.dumps(value,sort_keys=True,separators=(',',':')).encode()).hexdigest() - need(digest(table['baseAllowed'])==SUCCESSOR_BASE_ALLOWED_SHA and digest(p['clrPins'])==CLR_PINS_SHA,'mapping immutable inherited table and CLR pin set') + digest=semantic_digest + need(table['baseAllowed']==base and digest(table['baseAllowed'])==SUCCESSOR_BASE_ALLOWED_SHA and digest(p['clrPins'])==CLR_PINS_SHA,'mapping immutable inherited table and CLR pin set') rows=table['rows'];need(type(rows) is list and len(rows)<=len(table['baseAllowed'])+335,'finite mapping identity rows') pins=admitted_pins(p);declared=set();allowed={};clr={pin['path'] for pin in p['clrPins']} for row in rows: diff --git a/tests/Broker.NativeProof/RuntimeEvidence/fixtures/seed-attribution-helper/helper/test_mapping_profile.py b/tests/Broker.NativeProof/RuntimeEvidence/fixtures/seed-attribution-helper/helper/test_mapping_profile.py new file mode 100644 index 0000000..34fc220 --- /dev/null +++ b/tests/Broker.NativeProof/RuntimeEvidence/fixtures/seed-attribution-helper/helper/test_mapping_profile.py @@ -0,0 +1,117 @@ +"""Portable pure mapping controls. Physical rosters below are explicitly synthetic. +Only semantic source-contract paths/bytes/hashes are embedded; no private runtime +receipts or original physical metadata. Two immutable historical digest results +are mocked for the complete-schema control. Physical revalidate is tested separately +on genuinely captured temporary files; it is never patched for those controls. +""" +import unittest,copy,json,base64,zlib,tempfile,pathlib,os,hashlib,subprocess +from unittest.mock import patch +import mapping_profile as m +SEMANTIC_BASELINE_B64='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' +class Mapping(unittest.TestCase): + def setUp(self): + self.blocks=[] + for obj,name in[(subprocess,'Popen'),(os,'execve'),(os,'pidfd_open'),(os,'system')]: + block=patch.object(obj,name,side_effect=AssertionError('process effects prohibited'));block.start();self.blocks.append(block) + self.addCleanup(lambda:[x.stop()for x in reversed(self.blocks)]) + self.baseline=json.loads(zlib.decompress(base64.b64decode(SEMANTIC_BASELINE_B64)));self.delta,self.projection,self.base=m.semantic_delta(self.baseline) + def denied(self,fn,*a): + with self.assertRaises((ValueError,RuntimeError,KeyError,FileNotFoundError)):fn(*a) + def fixture(self): + # Complete semantic roster joined to synthetic physical observations, never runtime authority. + physical=[] + for n,row in enumerate(self.projection): + physical.append({**{k:row[k]for k in('declaredPath','canonicalPath','kind','baseAllowedKey')},'pin':dict(path=row['declaredPath'],sha256=row['sha256'],bytes=row['bytes'],device=7,inode=10000+n,uid=os.geteuid(),mode=0o600,nlink=1),'ancestorLinks':[]}) + clr_candidates=[r['pin']for r in physical if r['kind']=='clrPE'];clr_candidates+= [r['pin']for r in physical if r['kind']=='existingExecutable'and '/shared/Microsoft.NETCore.App/10.0.12/'in r['declaredPath']and r['declaredPath'].endswith('.dll')][:22] + # There are22 historical CLR paths already in existingExecutable. Choose their + # exact semantic entries from inherited rows where possible; digest is explicitly mocked. + if len(clr_candidates)<335:clr_candidates+= [r['pin']for r in physical if r['kind']=='existingExecutable'and r['pin']not in clr_candidates][:335-len(clr_candidates)] + assert len(clr_candidates)==335 + registry=[copy.deepcopy(r['pin'])for r in physical] + for row,pin in zip(physical,registry): + if row['kind']=='existingExecutable'and row['baseAllowedKey']!=row['declaredPath']:pin['symlinkCustody']={'resolvedPath':row['baseAllowedKey']} + clr_candidates=[next(x for x in registry if x['path']==pin['path'])for pin in clr_candidates] + p=dict(preparationReady=True,mappingBaseline=copy.deepcopy(self.baseline),mappingDelta=copy.deepcopy(self.delta),executableMappingIdentities=dict(schema=m.SCHEMA,baseAllowed=copy.deepcopy(self.base),clrPinCount=335,rows=physical),clrPins=copy.deepcopy(clr_candidates),hostPins=[],originalDataPins=copy.deepcopy(registry),elfPins=[],pythonPins=[],authorityPins=[],nativeHost=m.HOST,engine=m.HISTORICAL_DATA+'/spring-headless',dataRoot=m.DATA,policy=dict(apphost=m.POLICY)) + engine=next(r['pin']for r in physical if r['declaredPath']==p['engine']);p['originalDataPins']=[x for x in p['originalDataPins']if x['path']!=p['engine']];p['authorityPins']=[copy.deepcopy(engine)] + p['allowedExecutableMappings']={r['canonicalPath']:r['pin']['sha256']for r in physical};p['requiredHostMappings']=[m.HOST]+[x for x in p['allowedExecutableMappings']if pathlib.Path(x).name in('libcoreclr.so','libclrjit.so')] + p['requiredEngineMappings']={x['role']:dict(path=x['row']['declaredPath'],role=x['role'],sha256=x['row']['sha256'])for x in self.delta['added']if x['role']in m.AI};p['requiredEngineMappings']['engine']=dict(path=p['engine'],role='engine',sha256=self.delta['retainedEngine']['row']['sha256']) + retained=copy.deepcopy(sorted([r for r in physical if r['declaredPath']not in{x['row']['declaredPath']for x in self.delta['added']}],key=lambda r:r['declaredPath']));clr=copy.deepcopy(p['clrPins']);actual=m.semantic_digest + def digest(value): + if value==clr:return m.CLR_PINS_SHA + if value==retained:return m.RETAINED_PHYSICAL_ROWS_SHA + return actual(value) + return p,digest + def validate(self,p,digest): + with patch.object(m,'semantic_digest',side_effect=digest):return m.validate_schema(p) + def test_independent_golden_delta(self): + self.assertEqual(m.semantic_digest(self.delta),'e90554a5a7ead26b9db6eb3d250dd732bba1677842051406cf4d192437971060');self.assertEqual(len(self.projection),458);self.assertEqual(len(self.base),145);self.assertEqual(sum(x['kind']=='clrPE'for x in self.projection),313) + def test_complete_projection_and_retained_engine_authority(self): + p,digest=self.fixture();self.assertEqual(len(self.validate(p,digest)),458);p['authorityPins']=[];self.denied(self.validate,p,digest) + def test_missing_extra_duplicate_reordered_delta(self): + p,digest=self.fixture() + for change in('missing','extra','duplicate','reordered'): + q=copy.deepcopy(p) + if change=='missing':q['mappingDelta']['added'].pop() + elif change=='extra':q['mappingDelta']['unexpected']=1 + elif change=='duplicate':q['mappingDelta']['added'].append(q['mappingDelta']['added'][0]) + else:q['mappingDelta']['added'].reverse() + with self.subTest(change=change):self.denied(self.validate,q,digest) + def test_role_path_hash_size_and_noncanonical_delta(self): + p,digest=self.fixture() + for field,value in [('role','engine'),('declaredPath','/old/AI'),('canonicalPath','/path/../alias'),('sha256','0'*64),('bytes',1)]: + q=copy.deepcopy(p);entry=q['mappingDelta']['added'][0];entry[field if field=='role'else'row']=value if field=='role'else dict(entry['row'],**{field:value});self.denied(self.validate,q,digest) + def test_baseline_replacement_and_count_only_refuse(self): + p,digest=self.fixture() + for change in('row','count','base'): + q=copy.deepcopy(p) + if change=='row':q['mappingBaseline']['rows'][0]['sha256']='0'*64 + elif change=='count':q['executableMappingIdentities']['rows'][-1]=q['executableMappingIdentities']['rows'][0] + else:q['mappingBaseline']['baseAllowed']['extra']='a'*64 + self.denied(self.validate,q,digest) + def test_unchanged_clr_and_guard_injection(self): + p,digest=self.fixture();q=copy.deepcopy(p);q['clrPins'][0]['inode']+=1;self.denied(self.validate,q,digest) + q=copy.deepcopy(p);q['mappingDelta']['callerGuard']='ignored';self.denied(self.validate,q,digest) + def test_original_physical_and_ancestor_retention(self): + p,digest=self.fixture() + for field,value in [('inode',1),('device',1),('uid',-1),('mode',0o644),('nlink',2),('ancestorLinks',[{'unselected':True}])]: + q=copy.deepcopy(p);row=next(r for r in q['executableMappingIdentities']['rows']if r['declaredPath']not in{x['row']['declaredPath']for x in self.delta['added']}) + if field=='ancestorLinks':row[field]=value + else:row['pin'][field]=value + self.denied(self.validate,q,digest) + def test_ambiguous_pin_metadata_refuses(self): + p,digest=self.fixture();p['authorityPins'].append(dict(p['originalDataPins'][0],inode=-1));self.denied(self.validate,p,digest) + def test_new_role_path_and_full_pin_join(self): + p,digest=self.fixture() + for field in('nativeHost','dataRoot','engine'): + q=copy.deepcopy(p);q[field]='/unselected';self.denied(self.validate,q,digest) + q=copy.deepcopy(p);q['policy']['apphost']='/old/policy';self.denied(self.validate,q,digest) + q=copy.deepcopy(p);row=next(x for x in q['executableMappingIdentities']['rows']if x['declaredPath']==m.POLICY);row['pin']['inode']+=1;self.denied(self.validate,q,digest) + def test_missing_preparation_refuses(self): + p,digest=self.fixture();p['preparationReady']=False;self.denied(self.validate,p,digest) + def test_unknown_executable_or_clr_row_refuses(self): + p,digest=self.fixture() + for kind in('existingExecutable','clrPE'): + q=copy.deepcopy(p);new=copy.deepcopy(q['executableMappingIdentities']['rows'][-1]);new['declaredPath']='/unknown';new['kind']=kind;q['executableMappingIdentities']['rows'].append(new);self.denied(self.validate,q,digest) + def test_strict_json_duplicate_nonfinite_and_link(self): + with tempfile.TemporaryDirectory()as d: + path=pathlib.Path(d)/'profile';path.write_text('{"a":1,"a":2}');path.chmod(0o600);self.denied(m.load_profile,path) + path.write_text('{"a":NaN}');self.denied(m.load_profile,path);path.write_text('{"a":1}');self.assertEqual(m.load_profile(path),{'a':1});link=pathlib.Path(d)/'link';link.symlink_to(path);self.denied(m.load_profile,link) + def actual_row(self,path): + st=path.lstat();pin=dict(path=str(path),sha256=hashlib.sha256(path.read_bytes()).hexdigest(),bytes=st.st_size,device=st.st_dev,inode=st.st_ino,uid=st.st_uid,mode=st.st_mode&0o777,nlink=st.st_nlink) + return dict(declaredPath=str(path),canonicalPath=str(path),kind='existingExecutable',baseAllowedKey=str(path),pin=pin,ancestorLinks=m.ancestor_links(path)) + def physical_check(self,row): + with patch.object(m,'validate_schema',return_value=[row]):return m.revalidate({},lambda:None) + def test_real_fresh_inode_same_semantics_needs_actual_capture(self): + with tempfile.TemporaryDirectory()as d: + path=pathlib.Path(d)/'artifact';path.write_bytes(b'actual controlled source bytes');path.chmod(0o600);old=self.actual_row(path);self.physical_check(old);path.rename(path.with_name('old-held'));path.write_bytes(b'actual controlled source bytes');path.chmod(0o600);fresh=self.actual_row(path) + self.assertEqual(m.semantic_row(old),m.semantic_row(fresh));self.assertNotEqual(old['pin']['inode'],fresh['pin']['inode']);self.denied(self.physical_check,old);self.physical_check(fresh) + def test_real_physical_drift_not_removed_from_revalidate(self): + with tempfile.TemporaryDirectory()as d: + path=pathlib.Path(d)/'artifact';path.write_bytes(b'bytes');path.chmod(0o600);row=self.actual_row(path) + for field,value in [('device',-1),('inode',-1),('uid',-1),('mode',0o644),('nlink',2),('sha256','0'*64)]: + wrong=copy.deepcopy(row);wrong['pin'][field]=value;self.denied(self.physical_check,wrong) + wrong=copy.deepcopy(row);wrong['ancestorLinks']=[{}];self.denied(self.physical_check,wrong);path.write_bytes(b'drift');self.denied(self.physical_check,row) + def test_missing_selected_placement_cannot_revalidate(self): + p,digest=self.fixture() + with patch.object(m,'semantic_digest',side_effect=digest):self.denied(m.revalidate,p,lambda:None) +if __name__=='__main__':unittest.main() diff --git a/tests/Broker.NativeProof/RuntimeEvidence/fixtures/seed-attribution-helper/run-source-controls.py b/tests/Broker.NativeProof/RuntimeEvidence/fixtures/seed-attribution-helper/run-source-controls.py index 6754c5f..3f243dd 100644 --- a/tests/Broker.NativeProof/RuntimeEvidence/fixtures/seed-attribution-helper/run-source-controls.py +++ b/tests/Broker.NativeProof/RuntimeEvidence/fixtures/seed-attribution-helper/run-source-controls.py @@ -13,11 +13,11 @@ def forbidden(*args,**kwargs):raise RuntimeError('portable source controls prohi os.system=forbidden for name in ['fork','forkpty','posix_spawn','posix_spawnp','execv','execve','execvp','execvpe']: if hasattr(os,name):setattr(os,name,forbidden) -FILES=('helper/test_immutable_closure.py','test_policy_transport_runner.py','test_qualify_prearm.py','test_native_evidence.py','helper/test_prearm_startup.py','helper/test_seed_policy.py','helper/test_capture_custody.py','helper/test_loaded_custody.py','helper/test_settings_custody.py','helper/test_proc_bytes.py','fixture/tests/test_prearm_observation.py') +FILES=('helper/test_mapping_profile.py','helper/test_immutable_closure.py','test_policy_transport_runner.py','test_qualify_prearm.py','test_native_evidence.py','helper/test_prearm_startup.py','helper/test_seed_policy.py','helper/test_capture_custody.py','helper/test_loaded_custody.py','helper/test_settings_custody.py','helper/test_proc_bytes.py','fixture/tests/test_prearm_observation.py') suite=unittest.TestSuite() for index,name in enumerate(FILES): spec=importlib.util.spec_from_file_location('portable_seed_controls_'+str(index),ROOT/name);module=importlib.util.module_from_spec(spec);spec.loader.exec_module(module);suite.addTests(unittest.defaultTestLoader.loadTestsFromModule(module)) -expected=99 +expected=114 if suite.countTestCases()!=expected:raise SystemExit('portable source control roster changed: '+str(suite.countTestCases())) result=unittest.TextTestRunner(verbosity=2).run(suite) if not result.wasSuccessful()or result.testsRun!=expected or result.skipped:raise SystemExit(1) diff --git a/tests/Broker.NativeProof/RuntimeEvidence/fixtures/seed-attribution-helper/startup_runner.py b/tests/Broker.NativeProof/RuntimeEvidence/fixtures/seed-attribution-helper/startup_runner.py index 4a337d1..0bba9c7 100644 --- a/tests/Broker.NativeProof/RuntimeEvidence/fixtures/seed-attribution-helper/startup_runner.py +++ b/tests/Broker.NativeProof/RuntimeEvidence/fixtures/seed-attribution-helper/startup_runner.py @@ -12,7 +12,9 @@ RUNTIME_SOCKET=None def profile(): - p=m.load(HERE/'startup-profile.json');need(p['schema']=='bar.seed-attribution-profile/v2' and p['preparationReady'] is True and p['attemptRoot']==ROOT and p['roles']==['host','engine'],'closed startup profile') + from mapping_profile import load_profile + need((HERE/'startup-profile.json').is_file()and not(HERE/'startup-profile.json').is_symlink(),'closed JSON input') + p=load_profile(HERE/'startup-profile.json');need(p['schema']=='bar.seed-attribution-profile/v2' and p['preparationReady'] is True and p['attemptRoot']==ROOT and p['roles']==['host','engine'],'closed startup profile') need(p['source']=={'fsbarCommit':'361f5060aea0e340fb8a33da96a91fd234e7fc39','highbarCommit':'b57f11f290dc3239d894b0285ae272b6b21bc25d'} and p['engineSource']=='de69361239d8c8b1012dba3f5aa3122954ea4da3','literal source roles') need(p['limits']==dict(wholeSeconds=180,hostReadinessSeconds=60,cleanupReserve=8,termSeconds=3,killSeconds=3,streamBytes=33554432,attemptBytes=134217728,attemptFiles=1024,mapsBytes=4194304,ownedIdentities=128),'literal diagnostic caps') need(p['runtimeEnvironment']['DOTNET_EnableDiagnostics']=='0' and p['diagnosticConfigRootSourceDecision']=='Root approved SOURCE profile only; runtime diagnostic admission must bind this exact environment','explicit preparation-independent runtime configuration')