Skip to main content

allow_report/
diff_posture.rs

1use crate::{DiffFindingChange, DiffPolicyChange, DiffPostureSummary};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub enum DiffNetPosture {
5    Worse,
6    ReviewRequired,
7    Improved,
8    Unchanged,
9}
10
11#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
12pub(crate) struct DiffEvidenceDeltaSummary {
13    pub(crate) evidence_added: usize,
14    pub(crate) weak_evidence_added: usize,
15    pub(crate) broken_evidence_added: usize,
16    pub(crate) evidence_removed: usize,
17    pub(crate) evidence_removal_failures: usize,
18    pub(crate) evidence_removal_review_items: usize,
19    pub(crate) evidence_removal_improvements: usize,
20    pub(crate) link_added: usize,
21    pub(crate) weak_link_added: usize,
22    pub(crate) broken_link_added: usize,
23    pub(crate) link_removed: usize,
24    pub(crate) link_removal_failures: usize,
25    pub(crate) link_removal_review_items: usize,
26    pub(crate) link_removal_improvements: usize,
27}
28
29#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
30pub(crate) struct DiffStructuralDeltaSummary {
31    pub(crate) scope_broadened: usize,
32    pub(crate) scope_changed: usize,
33    pub(crate) scope_narrowed: usize,
34    pub(crate) selector_changed: usize,
35    pub(crate) selector_precision_decreased: usize,
36    pub(crate) selector_precision_increased: usize,
37}
38
39impl DiffNetPosture {
40    pub fn as_str(self) -> &'static str {
41        match self {
42            Self::Worse => "worse",
43            Self::ReviewRequired => "review-required",
44            Self::Improved => "improved",
45            Self::Unchanged => "unchanged",
46        }
47    }
48
49    pub fn reviewer_action(self) -> &'static str {
50        match self {
51            Self::Worse => {
52                "block until failing source exception changes are fixed, narrowed, or receipted."
53            }
54            Self::ReviewRequired => "review the source exception posture change before merging.",
55            Self::Improved => "verify the cleanup was intentional and keep the narrower posture.",
56            Self::Unchanged => "no source exception posture change detected.",
57        }
58    }
59}
60
61pub(crate) fn diff_structural_delta_summary(
62    policy_changes: &[DiffPolicyChange<'_>],
63) -> DiffStructuralDeltaSummary {
64    let mut summary = DiffStructuralDeltaSummary::default();
65    for change in policy_changes {
66        match change.kind {
67            "scope_broadened" => summary.scope_broadened += 1,
68            "scope_changed" => summary.scope_changed += 1,
69            "scope_narrowed" => summary.scope_narrowed += 1,
70            "selector_changed" => summary.selector_changed += 1,
71            "selector_precision_decreased" => summary.selector_precision_decreased += 1,
72            "selector_precision_increased" => summary.selector_precision_increased += 1,
73            _ => {}
74        }
75    }
76    summary
77}
78
79pub(crate) fn diff_evidence_delta_summary(
80    policy_changes: &[DiffPolicyChange<'_>],
81) -> DiffEvidenceDeltaSummary {
82    let mut summary = DiffEvidenceDeltaSummary::default();
83    for change in policy_changes {
84        match change.kind {
85            "evidence_added" => {
86                summary.evidence_added += 1;
87                match change.severity {
88                    "review" => summary.weak_evidence_added += 1,
89                    "fail" => summary.broken_evidence_added += 1,
90                    _ => {}
91                }
92            }
93            "evidence_removed" => {
94                summary.evidence_removed += 1;
95                match change.severity {
96                    "fail" => summary.evidence_removal_failures += 1,
97                    "review" => summary.evidence_removal_review_items += 1,
98                    "improvement" => summary.evidence_removal_improvements += 1,
99                    _ => {}
100                }
101            }
102            "link_added" => {
103                summary.link_added += 1;
104                match change.severity {
105                    "review" => summary.weak_link_added += 1,
106                    "fail" => summary.broken_link_added += 1,
107                    _ => {}
108                }
109            }
110            "link_removed" => {
111                summary.link_removed += 1;
112                match change.severity {
113                    "fail" => summary.link_removal_failures += 1,
114                    "review" => summary.link_removal_review_items += 1,
115                    "improvement" => summary.link_removal_improvements += 1,
116                    _ => {}
117                }
118            }
119            _ => {}
120        }
121    }
122    summary
123}
124
125pub fn diff_posture_summary(
126    current_failures: usize,
127    finding_changes: &[DiffFindingChange<'_>],
128    policy_changes: &[DiffPolicyChange<'_>],
129) -> DiffPostureSummary {
130    DiffPostureSummary {
131        current_failures,
132        new_findings: finding_changes
133            .iter()
134            .filter(|change| change.change == "new")
135            .count(),
136        removed_findings: finding_changes
137            .iter()
138            .filter(|change| change.change == "removed")
139            .count(),
140        policy_failures: policy_changes
141            .iter()
142            .filter(|change| change.severity == "fail")
143            .count(),
144        policy_review_items: policy_changes
145            .iter()
146            .filter(|change| change.severity == "review")
147            .count(),
148        policy_improvements: policy_changes
149            .iter()
150            .filter(|change| change.severity == "improvement")
151            .count(),
152    }
153}
154
155pub fn diff_net_posture(summary: DiffPostureSummary) -> DiffNetPosture {
156    if summary.current_failures > 0 || summary.policy_failures > 0 {
157        return DiffNetPosture::Worse;
158    }
159    if summary.new_findings > 0 || summary.policy_review_items > 0 {
160        return DiffNetPosture::ReviewRequired;
161    }
162    if summary.removed_findings > 0 || summary.policy_improvements > 0 {
163        return DiffNetPosture::Improved;
164    }
165    DiffNetPosture::Unchanged
166}