secrets-engine/tests/test_founder_admission.py

69 lines
2.7 KiB
Python
Raw Normal View History

"""Founder admission of the responder trust file (SECRETS-WP-0014-T02)."""
import json
import os
import subprocess
import pytest
from secrets_engine import founder_admission
from secrets_engine.decision_signature import load_trust
from secrets_engine.errors import DecisionError
from tests.signature_support import PUBLIC, FOUNDER, admit
TRUST = {'schema':'secrets-engine.pdp-trust.v1','evaluator':'stub/local','mode':'standalone',
'policy_package':'p','policy_version':'v','policy_package_digest':'sha256:'+'1'*64,
'keys':{'testdata-ed25519':PUBLIC}}
@pytest.fixture
def trust(tmp_path):
path=tmp_path/'pdp-trust.json';path.write_text(json.dumps(TRUST));return path
def test_admitted_trust_carries_founder_identity_only(trust):
loaded=load_trust(admit(trust))
assert dict(loaded)==TRUST
assert loaded.admission['principal']==FOUNDER
assert loaded.admission['key_fingerprint'].startswith('SHA256:')
assert loaded.admission['namespace']=='secrets-engine-pdp-trust'
@pytest.mark.parametrize('change',['unsigned','edited','other-namespace','gexec-namespace','garbage-signature'])
def test_unadmitted_trust_refuses(trust,change):
if change=='unsigned':pass
elif change=='edited':
admit(trust);trust.write_text(json.dumps({**TRUST,'keys':{'attacker':PUBLIC}}))
elif change=='other-namespace':admit(trust,namespace='secrets-engine-recovery-attempt')
elif change=='gexec-namespace':admit(trust,namespace='gexec-approval')
else:(trust.parent/(trust.name+'.sig')).write_text('-----BEGIN SSH SIGNATURE-----\nAAAA\n')
with pytest.raises(DecisionError):load_trust(trust)
def test_key_outside_anchor_refuses(trust,tmp_path):
key=tmp_path/'intruder'
subprocess.run(['ssh-keygen','-q','-t','ed25519','-N','','-f',str(key)],check=True,timeout=30)
with pytest.raises(DecisionError):load_trust(admit(trust,key=key))
def test_runtime_writable_anchor_refuses(trust,monkeypatch,founder_anchor):
admit(trust)
# The anchor is owned by the test's own uid, so the production rule must refuse.
monkeypatch.setattr(founder_admission,'_writable_by_runtime',founder_anchor['real_rule'])
assert os.stat(founder_anchor['anchor']).st_uid==os.geteuid()!=0
with pytest.raises(DecisionError):load_trust(trust)
def test_group_writable_anchor_refuses(trust,founder_anchor):
admit(trust);signers=founder_anchor['anchor']/'allowed_signers'
signers.chmod(0o664)
try:
with pytest.raises(DecisionError):load_trust(trust)
finally:signers.chmod(0o644)
def test_relocated_anchor_without_signers_refuses(trust,tmp_path,monkeypatch):
admit(trust);empty=tmp_path/'empty';empty.mkdir()
monkeypatch.setenv('SECRETS_ENGINE_ADMISSION_ANCHOR',str(empty))
with pytest.raises(DecisionError):load_trust(trust)