use std::collections::BTreeSet;
use pedant_types::{Capability, CapabilityFinding, FindingOrigin};
use crate::check_config::GateConfig;
use crate::ir::{DataFlowFact, DataFlowKind};
use super::catalog::{CAPABILITY_RULES, resolve_severity};
use super::verdict::GateVerdict;
pub struct GateInputSummary {
all_capabilities: BTreeSet<Capability>,
build_hook_capabilities: BTreeSet<Capability>,
has_key_material: bool,
flow_kinds: BTreeSet<DataFlowKind>,
taint_pairs: BTreeSet<(Capability, Capability)>,
}
fn is_key_material(f: &CapabilityFinding) -> bool {
match (f.capability, f.origin) {
(Capability::Crypto, Some(FindingOrigin::StringLiteral)) => true,
(Capability::Crypto, None) => !f.evidence.contains("::"),
_ => false,
}
}
impl GateInputSummary {
pub fn from_analysis(findings: &[CapabilityFinding], flows: &[DataFlowFact]) -> Self {
Self::build(findings, flows)
}
pub fn from_refs(findings: &[&CapabilityFinding], flows: &[DataFlowFact]) -> Self {
Self::build(findings.iter().copied(), flows)
}
fn build<'f>(
findings: impl IntoIterator<Item = &'f CapabilityFinding>,
flows: &[DataFlowFact],
) -> Self {
let mut all_capabilities = BTreeSet::new();
let mut build_hook_capabilities = BTreeSet::new();
let mut has_key_material = false;
for finding in findings {
all_capabilities.insert(finding.capability);
if finding.is_build_hook() {
build_hook_capabilities.insert(finding.capability);
}
has_key_material |= is_key_material(finding);
}
let mut flow_kinds = BTreeSet::new();
let mut taint_pairs = BTreeSet::new();
for flow in flows {
flow_kinds.insert(flow.kind);
if let (Some(src), Some(sink)) = (flow.source_capability, flow.sink_capability) {
taint_pairs.insert((src, sink));
}
}
Self {
all_capabilities,
build_hook_capabilities,
has_key_material,
flow_kinds,
taint_pairs,
}
}
pub(super) fn has_capability(&self, cap: Capability) -> bool {
self.all_capabilities.contains(&cap)
}
pub(super) fn has_build_hook_capability(&self, cap: Capability) -> bool {
self.build_hook_capabilities.contains(&cap)
}
pub(super) fn has_key_material(&self) -> bool {
self.has_key_material
}
pub(super) fn has_flow(&self, source: Capability, sink: Capability) -> bool {
self.taint_pairs.contains(&(source, sink))
}
pub(super) fn has_kind(&self, kind: DataFlowKind) -> bool {
self.flow_kinds.contains(&kind)
}
}
pub fn evaluate_gate_rules(summary: &GateInputSummary, config: &GateConfig) -> Box<[GateVerdict]> {
if !config.enabled {
return Box::new([]);
}
CAPABILITY_RULES
.iter()
.filter_map(|rule| {
let severity = resolve_severity(rule.name, rule.default_severity, &config.overrides)?;
(rule.predicate)(summary).then(|| rule.verdict(severity, None))
})
.collect()
}