openvet_policy/lib.rs
1//! # Requirement language and policy evaluator for OpenVet
2//!
3//! Turns a project's TOML-defined policy and the audits collected
4//! for a single subject into a pass-or-fail [`Verdict`] with
5//! per-requirement diagnostics. The wire format and evaluator
6//! semantics are specified in `spec/policy.md`.
7//!
8//! # Requirements, claims, expressions
9//!
10//! A *claim* is a `(name, bool)` pair emitted by an audit (e.g.
11//! `memory-unsafe-code: true`). OpenVet's canonical claim
12//! vocabulary (`spec/vocabulary.md`) is deliberately atomic: each
13//! claim is an observable property of the code, rather than a
14//! context-sensitive judgement.
15//!
16//! A *requirement* is a boolean expression over claim names that
17//! the consumer wants to hold for every dependency, e.g.
18//! `(not memory-unsafe-code) or reviewed-unsafe`. Expressions
19//! support `and`, `or`, `not`, and parentheses with standard
20//! precedence; claim names are `[a-zA-Z_][a-zA-Z0-9_-]*`. See
21//! [`expr::Expr`] and [`expr::parse`].
22//!
23//! # TOML shape
24//!
25//! ```toml
26//! # Bare form: default-on requirement, value is the expression.
27//! [requirement]
28//! safe-to-deploy = "(not memory-unsafe-code) or reviewed-unsafe"
29//!
30//! # Table form: opt-in by overriding into the requirement set.
31//! [requirement.fuzz-tested]
32//! condition = "has-fuzz-tests"
33//! default = false
34//!
35//! # Per-subject overrides. Matcher fields are AND'd; "*" or omitted
36//! # is a wildcard.
37//! [[override]]
38//! registry = "cargo"
39//! package = "libc"
40//! requirements = { add = ["fuzz-tested"], remove = ["safe-to-deploy"] }
41//!
42//! [[override]]
43//! package = "serde"
44//! requirements = ["safe-to-deploy"] # replace form
45//!
46//! # Cross-log claim renames: "log:claim" → canonical name.
47//! [alias]
48//! reviewed-unsafe = ["mozilla:audited-unsafe", "google:unsafe-verified"]
49//! ```
50//!
51//! # Overrides and aliases
52//!
53//! The effective requirement set for a subject is computed by
54//! walking the `[[override]]` blocks in declaration order: each
55//! override whose matcher matches the subject either replaces the
56//! working set or patches it, depending on whether `requirements`
57//! is a plain list (replace) or an `{ add = [...], remove = [...] }`
58//! table (patch). [`config::Override`], [`config::SubjectMatcher`],
59//! and [`config::OverrideOp`] carry the parsed shapes.
60//!
61//! Aliases let a consumer treat differently-named claims from
62//! different logs as the same canonical claim. They apply at
63//! claim-lookup time; an audit from a log not listed under an
64//! alias falls through to the canonical name unchanged. See
65//! [`config::Alias`].
66//!
67//! # Evaluation
68//!
69//! Three-valued Kleene logic per audit ([`expr::Tri`]) with
70//! standard short-circuiting: `False` short-circuits `and`, `True`
71//! short-circuits `or`, and `not Unknown == Unknown`. The
72//! `Unknown` branch is load-bearing: an audit can perfectly well
73//! not have an opinion on a claim, and that is not the same as
74//! actively asserting it false.
75//!
76//! A requirement passes for a subject iff at least one audit
77//! returns `True` and no audit returns `False`. Fail variants
78//! distinguish "nobody had enough info"
79//! ([`eval::FailureKind::NotAsserted`]) from "an audit explicitly
80//! disagrees" ([`eval::FailureKind::Contradicted`]); the latter
81//! carries a snapshot of the relevant claims so the failure
82//! message can show why. A subject passes iff all of its effective
83//! requirements pass.
84//!
85//! # Usage
86//!
87//! ```rust,ignore
88//! use openvet_policy::{parse_str, evaluate};
89//!
90//! let policy = parse_str(r#"
91//! [requirement]
92//! safe-to-deploy = "(not memory-unsafe-code) or reviewed-unsafe"
93//! "#)?;
94//! let verdict = evaluate(&policy, &subject, &[("alice", &audit)]);
95//! println!("{verdict}");
96//! # Ok::<(), openvet_policy::PolicyError>(())
97//! ```
98//!
99//! [`Verdict`]'s `Display` impl produces the human-readable
100//! explanation `openvet check` prints; programmatic consumers can
101//! walk the [`Verdict::Fail`] variant's [`eval::FailureReason`]s
102//! directly.
103
104pub mod config;
105pub(crate) mod error;
106pub mod eval;
107pub mod explain;
108pub mod expr;
109
110pub use config::{Policy, parse, parse_str};
111pub use error::{PolicyError, Result};
112pub use eval::{Verdict, evaluate};