Skip to main content

skill_veil_core/policy/
mod.rs

1//! Policy generation and stable public policy contract.
2
3pub(crate) mod baseline;
4pub(crate) mod disposition;
5mod eval;
6pub(crate) mod fingerprint;
7pub(crate) mod reports;
8pub(crate) mod sarif;
9pub(crate) mod serializers;
10pub(crate) mod state;
11pub(crate) mod types;
12
13use crate::findings::{OperationalContext, RecommendedAction, Severity};
14
15pub use self::baseline::{BaselineEntry, BaselineFile, WaiverEntry, WaiverFile};
16pub use self::disposition::{
17    adjust_confidence, learned_allowlist, learned_confidence_adjustments, Disposition,
18    DispositionOverlay, DispositionRecord,
19};
20pub use self::fingerprint::finding_fingerprint;
21pub use self::reports::{JsonReport, PolicyGenerator};
22pub(crate) use self::sarif::{
23    SarifArtifactLocation, SarifConfiguration, SarifDriver, SarifLocation, SarifMessage,
24    SarifPhysicalLocation, SarifRegion, SarifReport, SarifResult, SarifRule, SarifRun, SarifTool,
25};
26pub use self::serializers::empty_sarif_report;
27pub use self::state::{
28    apply_baseline, apply_policy_overrides, apply_policy_overrides_with_audit, apply_waivers,
29    baseline_from_reports, count_baseline_matches, diff_reports, diff_reports_with_policy_state,
30    load_baseline, load_disposition_overlay, load_policy, load_waivers, validate_policy,
31    validate_waivers, PolicyLoadError,
32};
33pub(crate) use self::types::{default_policy_schema_version, empty_finding_summary};
34pub use self::types::{
35    AppliedPolicyOverride, ConfiguredProfile, ContextActionOverride, ContextPolicy, DiffEntry,
36    DiffReport, PolicyAudit, PolicyFile, PolicyOverride, PolicyProfile, PolicyProfiles,
37    ShieldPolicy, SuppressionSummary, POLICY_AUDIT_PRECEDENCE,
38};
39
40/// Default number of days until a shield policy expires.
41pub(crate) const POLICY_EXPIRY_DAYS: i64 = 365;
42/// Version string for persisted policy-related schemas.
43pub const POLICY_SCHEMA_VERSION: &str = "skill-veil.dev/v1alpha1";
44
45impl PolicyProfile {
46    #[must_use]
47    pub fn default_fail_on(self) -> Option<Severity> {
48        match self {
49            Self::Personal => Some(Severity::Critical),
50            Self::Team => Some(Severity::High),
51            Self::Enterprise => Some(Severity::Medium),
52            Self::Research => None,
53        }
54    }
55
56    #[must_use]
57    pub fn default_action_for_context(self, context: OperationalContext) -> RecommendedAction {
58        match self {
59            Self::Personal => RecommendedAction::RequireApproval,
60            Self::Team => match context {
61                OperationalContext::Secrets => RecommendedAction::Block,
62                _ => RecommendedAction::RequireApproval,
63            },
64            Self::Enterprise => match context {
65                OperationalContext::Install
66                | OperationalContext::Secrets
67                | OperationalContext::ExternalComms => RecommendedAction::Block,
68                OperationalContext::Network | OperationalContext::CodeModification => {
69                    RecommendedAction::RequireApproval
70                }
71            },
72            Self::Research => match context {
73                OperationalContext::Secrets | OperationalContext::ExternalComms => {
74                    RecommendedAction::RequireApproval
75                }
76                _ => RecommendedAction::Log,
77            },
78        }
79    }
80}
81
82impl Default for PolicyFile {
83    fn default() -> Self {
84        Self {
85            schema_version: self::types::default_policy_schema_version(),
86            profiles: PolicyProfiles::default(),
87            overrides: Vec::new(),
88        }
89    }
90}
91
92impl PolicyFile {
93    #[must_use]
94    pub fn profile_config(&self, profile: PolicyProfile) -> Option<&ConfiguredProfile> {
95        match profile {
96            PolicyProfile::Personal => self.profiles.personal.as_ref(),
97            PolicyProfile::Team => self.profiles.team.as_ref(),
98            PolicyProfile::Enterprise => self.profiles.enterprise.as_ref(),
99            PolicyProfile::Research => self.profiles.research.as_ref(),
100        }
101    }
102
103    #[must_use]
104    pub fn resolve_fail_on(&self, profile: PolicyProfile) -> Option<Severity> {
105        self.profile_config(profile)
106            .and_then(|config| config.fail_on)
107            .or_else(|| profile.default_fail_on())
108    }
109
110    #[must_use]
111    pub fn resolve_context_action(
112        &self,
113        profile: PolicyProfile,
114        context: OperationalContext,
115    ) -> RecommendedAction {
116        self.profile_config(profile)
117            .and_then(|config| {
118                config
119                    .context_actions
120                    .iter()
121                    .find(|entry| entry.context == context)
122                    .map(|entry| entry.action)
123            })
124            .unwrap_or_else(|| profile.default_action_for_context(context))
125    }
126}
127
128impl Default for PolicyAudit {
129    fn default() -> Self {
130        Self {
131            precedence_order: POLICY_AUDIT_PRECEDENCE
132                .iter()
133                .map(|s| (*s).to_string())
134                .collect(),
135            effective_fail_on: None,
136            applied_overrides: Vec::new(),
137        }
138    }
139}
140
141pub(crate) fn context_label(context: OperationalContext) -> &'static str {
142    match context {
143        OperationalContext::Install => "install",
144        OperationalContext::Network => "network",
145        OperationalContext::Secrets => "secrets",
146        OperationalContext::CodeModification => "code_modification",
147        OperationalContext::ExternalComms => "external_comms",
148    }
149}
150
151pub(crate) fn severity_to_sarif_level(severity: Severity) -> &'static str {
152    match severity {
153        Severity::Critical | Severity::High => "error",
154        Severity::Medium => "warning",
155        Severity::Low => "note",
156    }
157}
158
159#[cfg(test)]
160mod tests_filtering;
161#[cfg(test)]
162mod tests_generation;