"""Preparation must never promote stale metadata or expand its data-read scope.""" import importlib.util import json from pathlib import Path import pytest ROOT=Path(__file__).resolve().parents[1] spec=importlib.util.spec_from_file_location('prepare',ROOT/'tools/bootstrap-recovery/prepare-identity-coverage.py') m=importlib.util.module_from_spec(spec);spec.loader.exec_module(m) def fixture(): profile=json.loads(m.PROFILE.read_text());objects={} objects[(None,'namespace','kube-system')]={'metadata':{'uid':m.CLUSTER_UID}} for c in profile['components']: ns=c['namespace'];name=c['deployment'] objects[(ns,'deployment',name)]={'metadata':{'uid':c['deployment_uid'],'generation':9}, 'status':{'observedGeneration':9,'readyReplicas':1},'spec':{'template':{'spec':{ 'containers':[{'name':'service','env':[{'name':'DO_NOT_EXPORT','value':'private-value-canary'}], 'envFrom':[{'secretRef':{'name':n}} for n in c['secret_refs']]}], 'volumes':[{'configMap':{'name':n}} for n in c['configmap_refs']]+ [{'persistentVolumeClaim':{'claimName':p['name']}} for p in c['pvc_refs']]}}}} for pvc in c['pvc_refs']:objects[(ns,'pvc',pvc['name'])]={'metadata':{'uid':pvc['uid']},'status':{'phase':'Bound'}} calls=[] def get(ns,kind,name):calls.append((ns,kind,name));return objects[(ns,kind,name)] return profile,objects,calls,get def test_only_safe_metadata_leaves_preparation_and_no_secret_api_is_called(): p,objects,calls,get=fixture();r=m.prepare(p,get) assert r['status']=='prepared_not_admitted' assert not any(r[k] for k in ['secret_values_read','backup_captured','uploaded','runtime_changed','credential_grant_created']) assert 'private-value-canary' not in json.dumps(r) assert all(kind in {'namespace','deployment','pvc'} for ns,kind,name in calls) @pytest.mark.parametrize('change',['cluster','pvc','deployment','not-ready','unobserved','terminating','new-secret','projected-secret','new-pvc']) def test_stale_or_expanded_live_state_requires_fresh_owner_review(change): p,o,calls,get=fixture();d=o[('sso','deployment','lldap')];s=d['spec']['template']['spec'] if change=='cluster':o[(None,'namespace','kube-system')]['metadata']['uid']='other-cluster' if change=='pvc':o[('sso','pvc','lldap-data')]['metadata']['uid']='replacement' if change=='deployment':d['metadata']['uid']='replacement' if change=='not-ready':d['status']['readyReplicas']=0 if change=='unobserved':d['status']['observedGeneration']=8 if change=='terminating':d['metadata']['deletionTimestamp']='2026-10-16' if change=='new-secret':s['containers'][0]['envFrom'].append({'secretRef':{'name':'unreviewed-secret'}}) if change=='projected-secret':s['volumes'].append({'projected':{'sources':[{'secret':{'name':'unreviewed-secret'}}]}}) if change=='new-pvc':s['volumes'].append({'persistentVolumeClaim':{'claimName':'unreviewed-pvc'}}) with pytest.raises(m.Refused):m.prepare(p,get) @pytest.mark.parametrize('kind',['secret','configmap','pods/exec','clusterrolebinding']) def test_preparation_transport_has_no_private_data_or_mutation_route(kind): with pytest.raises(m.Refused):m.query('sso',kind,'anything')