1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//! Kani proof harnesses for `secure_authz`.
//!
//! Compiled only under `cargo kani` via `#![cfg(kani)]`. See
//! `docs/dev-guide/formal-verification.md` for the proof catalogue and
//! the advisory CI lane (`.github/workflows/kani.yml`).
//!
//! M2 proofs (this file): the deny-by-default invariant — for any subject,
//! action, and resource, when no policy explicitly allows the operation,
//! the resulting `Decision` is `Deny`, never `Allow`.
use crate;
/// Proof: an empty-policy `Decision` is always `Deny`, never `Allow`.
///
/// The deny-by-default invariant says: if no `Allow` policy matches, the
/// authorization decision is `Deny` with a structured reason. This proof
/// models the "no policy matched" case directly via the `Decision::Deny`
/// constructor, then asserts the discriminant is `Deny`.
///
/// Future M3+ harnesses will extend this to a proof on `DefaultAuthorizer`
/// itself, modelling the policy engine as a small Kani-tractable function.
/// For M2 the property under proof is the discriminant invariant — even
/// the simplest construction satisfies it, and any future code change
/// that returns `Allow` from the no-match path would have to bypass the
/// `Decision` constructor surface to fool this proof.
/// Proof: `Decision::Allow` and `Decision::Deny` are mutually exclusive.
///
/// Discriminant property — for any constructed Decision, `is_allow()`
/// and `is_deny()` are never both true and never both false. This
/// catches a future `match` arm that accidentally returns the wrong
/// discriminant after a refactor.