1use fallow_config::{ResolvedConfig, RulesConfig, Severity};
4use fallow_types::results::{SecurityFinding, SecurityFindingKind};
5
6pub fn enable_security_rules(config: &mut ResolvedConfig) {
12 if config.rules.security_client_server_leak == Severity::Off {
13 config.rules.security_client_server_leak = Severity::Warn;
14 }
15 if config.rules.security_sink == Severity::Off {
16 config.rules.security_sink = Severity::Warn;
17 }
18}
19
20#[must_use]
22const fn security_rule_severity(rules: &RulesConfig, kind: SecurityFindingKind) -> Severity {
23 match kind {
24 SecurityFindingKind::ClientServerLeak => rules.security_client_server_leak,
25 SecurityFindingKind::TaintedSink => rules.security_sink,
26 }
27}
28
29#[must_use]
34pub fn resolve_security_finding_severity(
35 config: &ResolvedConfig,
36 finding: &SecurityFinding,
37) -> Severity {
38 let path = config.root.join(&finding.path);
39 security_rule_severity(&config.resolve_rules_for_path(&path), finding.kind)
40}
41
42pub fn retain_enabled_security_findings(
48 findings: &mut Vec<SecurityFinding>,
49 config: &ResolvedConfig,
50) {
51 if config.overrides.is_empty() {
52 return;
53 }
54 findings.retain(|finding| resolve_security_finding_severity(config, finding) != Severity::Off);
55}
56
57#[must_use]
59pub fn security_rules_can_error(config: &ResolvedConfig) -> bool {
60 config.rules.security_client_server_leak == Severity::Error
61 || config.rules.security_sink == Severity::Error
62 || config.overrides.iter().any(|entry| {
63 entry.rules.security_client_server_leak == Some(Severity::Error)
64 || entry.rules.security_sink == Some(Severity::Error)
65 })
66}
67
68#[cfg(test)]
69#[allow(
70 clippy::expect_used,
71 reason = "fixture setup asserts its own invariants directly"
72)]
73mod tests {
74 use super::{enable_security_rules, security_rules_can_error};
75
76 fn resolve(config: fallow_config::FallowConfig) -> fallow_config::ResolvedConfig {
77 let project = tempfile::tempdir().expect("temp dir");
78 config.resolve(
79 project.path().to_path_buf(),
80 fallow_config::OutputFormat::Human,
81 1,
82 true,
83 true,
84 None,
85 )
86 }
87
88 fn sink_override(severity: fallow_config::Severity) -> fallow_config::FallowConfig {
89 fallow_config::FallowConfig {
90 overrides: vec![fallow_config::ConfigOverride {
91 files: vec!["src/generated/**".to_owned()],
92 rules: fallow_config::PartialRulesConfig {
93 security_sink: Some(severity),
94 ..Default::default()
95 },
96 }],
97 ..Default::default()
98 }
99 }
100
101 #[test]
102 fn override_error_makes_security_rules_able_to_error() {
103 let mut config = resolve(sink_override(fallow_config::Severity::Error));
104 enable_security_rules(&mut config);
105
106 assert!(security_rules_can_error(&config));
107 }
108
109 #[test]
110 fn override_off_or_warn_keeps_security_rules_advisory() {
111 for severity in [fallow_config::Severity::Off, fallow_config::Severity::Warn] {
112 let mut config = resolve(sink_override(severity));
113 enable_security_rules(&mut config);
114
115 assert!(!security_rules_can_error(&config));
116 }
117 }
118
119 #[test]
120 fn enables_only_default_off_security_rules() {
121 let project = tempfile::tempdir().expect("temp dir");
122 let mut config = fallow_config::FallowConfig::default().resolve(
123 project.path().to_path_buf(),
124 fallow_config::OutputFormat::Human,
125 1,
126 true,
127 true,
128 None,
129 );
130 config.rules.security_client_server_leak = fallow_config::Severity::Off;
131 config.rules.security_sink = fallow_config::Severity::Error;
132
133 enable_security_rules(&mut config);
134
135 assert_eq!(
136 config.rules.security_client_server_leak,
137 fallow_config::Severity::Warn
138 );
139 assert_eq!(config.rules.security_sink, fallow_config::Severity::Error);
140 }
141}