Skip to main content

taudit_core/
finding.rs

1use crate::graph::NodeId;
2use crate::propagation::PropagationPath;
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
6#[serde(rename_all = "snake_case")]
7pub enum Severity {
8    Critical,
9    High,
10    Medium,
11    Low,
12    Info,
13}
14
15impl Severity {
16    fn rank(self) -> u8 {
17        match self {
18            Severity::Critical => 0,
19            Severity::High => 1,
20            Severity::Medium => 2,
21            Severity::Low => 3,
22            Severity::Info => 4,
23        }
24    }
25}
26
27impl Ord for Severity {
28    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
29        self.rank().cmp(&other.rank())
30    }
31}
32
33impl PartialOrd for Severity {
34    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
35        Some(self.cmp(other))
36    }
37}
38
39/// MVP categories (1-5) are derivable from pipeline YAML alone.
40/// Stretch categories (6-9) need heuristics or metadata enrichment.
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
42#[serde(rename_all = "snake_case")]
43pub enum FindingCategory {
44    // MVP
45    AuthorityPropagation,
46    OverPrivilegedIdentity,
47    UnpinnedAction,
48    UntrustedWithAuthority,
49    ArtifactBoundaryCrossing,
50    // Stretch
51    EgressBlindspot,
52    MissingAuditTrail,
53    FloatingImage,
54    LongLivedCredential,
55}
56
57/// Routing: scope findings -> TsafeRemediation; isolation findings -> CellosRemediation.
58#[derive(Debug, Clone, Serialize, Deserialize)]
59#[serde(tag = "type", rename_all = "snake_case")]
60pub enum Recommendation {
61    TsafeRemediation {
62        command: String,
63        explanation: String,
64    },
65    CellosRemediation {
66        reason: String,
67        spec_hint: String,
68    },
69    PinAction {
70        current: String,
71        pinned: String,
72    },
73    ReducePermissions {
74        current: String,
75        minimum: String,
76    },
77    FederateIdentity {
78        static_secret: String,
79        oidc_provider: String,
80    },
81    Manual {
82        action: String,
83    },
84}
85
86/// A finding is a concrete, actionable authority issue.
87#[derive(Debug, Clone, Serialize, Deserialize)]
88pub struct Finding {
89    pub severity: Severity,
90    pub category: FindingCategory,
91    #[serde(skip_serializing_if = "Option::is_none")]
92    pub path: Option<PropagationPath>,
93    pub nodes_involved: Vec<NodeId>,
94    pub message: String,
95    pub recommendation: Recommendation,
96}