Skip to main content

systemprompt_security/policy/
registry.rs

1//! Inventory-based registration for governance policies.
2//!
3//! Companion to [`crate::authz::AuthzHookRegistration`]: policies register a
4//! factory at static-init time and [`super::GovernanceEngine::from_config`]
5//! resolves configured ids against the collected set. The four built-in
6//! policies in [`super::builtin`] self-register here; extensions add their own
7//! via [`crate::register_governance_policy!`] and enable them from the same
8//! `governance.policies` YAML sequence.
9//!
10//! Copyright (c) systemprompt.io — Business Source License 1.1.
11//! See <https://systemprompt.io> for licensing details.
12
13use serde_yaml::Value as YamlValue;
14
15use super::secrets::SecretPatternError;
16use super::types::GovernancePolicy;
17
18/// Constructs one policy instance from its raw YAML config entry.
19///
20/// Runs once per [`super::GovernanceEngine::from_config`] call and must not
21/// block; a factory receives `YamlValue::Null` when the policy is absent from
22/// config. A rejected entry is an error the engine refuses to start on.
23pub type PolicyFactory =
24    fn(&YamlValue) -> Result<Box<dyn GovernancePolicy>, PolicyConfigurationError>;
25
26/// Why a policy factory rejected its YAML entry.
27#[derive(Debug, thiserror::Error)]
28pub enum PolicyConfigurationError {
29    #[error("unknown access scope `{scope}` in require_approval exempt_scopes")]
30    UnknownExemptScope { scope: String },
31
32    #[error(
33        "require_approval condition on `{tool}` at `{path}` has no operand its `{operator}` \
34         operator can use"
35    )]
36    UnusableCondition {
37        tool: String,
38        path: String,
39        operator: &'static str,
40    },
41
42    #[error(
43        "secret_scan is in enforce mode but compiles no secret patterns; declare `patterns` or \
44         set `mode: warn`"
45    )]
46    ToothlessSecretScan,
47
48    #[error("{context}: {source}")]
49    Yaml {
50        context: &'static str,
51        #[source]
52        source: serde_yaml::Error,
53    },
54
55    #[error(transparent)]
56    SecretPatterns(#[from] SecretPatternError),
57}
58
59/// One inventory submission per policy. `id` is the stable referent used in
60/// `governance.policies` YAML and in `governance_decisions.policy`.
61#[derive(Debug, Clone, Copy)]
62pub struct PolicyRegistration {
63    pub id: &'static str,
64    pub factory: PolicyFactory,
65}
66
67inventory::collect!(PolicyRegistration);
68
69#[doc(hidden)]
70pub use inventory;
71
72#[macro_export]
73macro_rules! register_governance_policy {
74    ($id:expr, $factory:expr) => {
75        $crate::policy::registry::inventory::submit! {
76            $crate::policy::PolicyRegistration {
77                id: $id,
78                factory: $factory,
79            }
80        }
81    };
82}