allow_report/
diff_posture.rs1use 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}