tests/test_policy.py
4.1 KB · 131 lines · python Raw
1 """Tests for LoadPolicy / PolicyRule."""
2
3 from __future__ import annotations
4
5 import pytest
6
7 from pqc_ebpf_attestation import (
8 BPFProgram,
9 BPFProgramType,
10 LoadPolicy,
11 PolicyDecision,
12 PolicyDeniedError,
13 PolicyRule,
14 UntrustedSignerError,
15 )
16
17
18 def test_empty_policy_denies_everything(signed_program) -> None:
19 policy = LoadPolicy()
20 decision, reason = policy.evaluate(signed_program)
21 assert decision == PolicyDecision.DENY
22 assert "no rule" in reason
23
24
25 def test_rule_matches_by_program_type(signed_program) -> None:
26 policy = LoadPolicy().add_rule(
27 PolicyRule(
28 program_types=(BPFProgramType.KPROBE,),
29 allowed_signers=frozenset({signed_program.signer_did}),
30 )
31 )
32 decision, _ = policy.evaluate(signed_program)
33 assert decision == PolicyDecision.ALLOW
34
35 # A rule that doesn't match the program_type falls through to default deny.
36 policy2 = LoadPolicy().add_rule(
37 PolicyRule(
38 program_types=(BPFProgramType.XDP,),
39 allowed_signers=frozenset({signed_program.signer_did}),
40 )
41 )
42 decision2, reason2 = policy2.evaluate(signed_program)
43 assert decision2 == PolicyDecision.DENY
44 assert "no rule" in reason2
45
46
47 def test_size_cap_enforced(signer, sample_bpf_metadata, sample_bytecode) -> None:
48 program = BPFProgram.from_bytes(sample_bpf_metadata, sample_bytecode)
49 signed = signer.sign(program)
50 policy = LoadPolicy().add_rule(
51 PolicyRule(
52 program_types=(BPFProgramType.KPROBE,),
53 allowed_signers=frozenset({signed.signer_did}),
54 max_bytecode_size=16, # tiny cap to force denial
55 )
56 )
57 decision, reason = policy.evaluate(signed)
58 assert decision == PolicyDecision.DENY
59 assert "exceeds cap" in reason
60
61
62 def test_allow_list_filters_signers(
63 signer, untrusted_identity, sample_bpf_metadata, sample_bytecode
64 ) -> None:
65 from pqc_ebpf_attestation import BPFSigner
66
67 # Sign the same program with an untrusted identity.
68 untrusted_signer = BPFSigner(untrusted_identity)
69 untrusted_signed = untrusted_signer.sign(
70 BPFProgram.from_bytes(sample_bpf_metadata, sample_bytecode)
71 )
72
73 # Allow-list only contains the trusted signer.
74 policy = LoadPolicy().add_rule(
75 PolicyRule(
76 program_types=(BPFProgramType.KPROBE,),
77 allowed_signers=frozenset({signer.identity.did}),
78 )
79 )
80 decision, reason = policy.evaluate(untrusted_signed)
81 assert decision == PolicyDecision.DENY
82 assert "not in allow-list" in reason
83
84
85 def test_untrusted_signer_raises_specific_error(
86 signer, untrusted_identity, sample_bpf_metadata, sample_bytecode
87 ) -> None:
88 from pqc_ebpf_attestation import BPFSigner
89
90 untrusted_signed = BPFSigner(untrusted_identity).sign(
91 BPFProgram.from_bytes(sample_bpf_metadata, sample_bytecode)
92 )
93
94 policy = LoadPolicy().add_rule(
95 PolicyRule(
96 program_types=(BPFProgramType.KPROBE,),
97 allowed_signers=frozenset({signer.identity.did}),
98 )
99 )
100 with pytest.raises(UntrustedSignerError):
101 policy.enforce(untrusted_signed)
102
103
104 def test_general_deny_raises_policy_denied(signed_program) -> None:
105 policy = LoadPolicy() # no rules => default deny, but not allow-list related
106 with pytest.raises(PolicyDeniedError):
107 policy.enforce(signed_program)
108
109
110 def test_multiple_rules_first_match_wins(signed_program) -> None:
111 # First rule matches KPROBE with allow-list containing the signer - ALLOW.
112 # Second rule also matches KPROBE but with an empty allow-list of a different
113 # signer - it would deny, but should never be consulted.
114 policy = LoadPolicy()
115 policy.add_rule(
116 PolicyRule(
117 program_types=(BPFProgramType.KPROBE,),
118 allowed_signers=frozenset({signed_program.signer_did}),
119 )
120 )
121 policy.add_rule(
122 PolicyRule(
123 program_types=(BPFProgramType.KPROBE,),
124 allowed_signers=frozenset({"did:pqaid:not-this-one"}),
125 )
126 )
127 decision, _ = policy.evaluate(signed_program)
128 assert decision == PolicyDecision.ALLOW
129
130
131