Authorization analysis logic improvements (#61)

This commit is contained in:
Eli Peter 2026-05-02 16:44:49 -04:00 committed by GitHub
parent 3c89bddbf2
commit 40995e45e7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
55 changed files with 4193 additions and 134 deletions

View file

@ -325,6 +325,28 @@ pub struct SsaFuncSummary {
/// can be joined by ordinal at call-graph build time.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub typed_call_receivers: Vec<(u32, String)>,
/// Parameter indices whose taint flow to the return value is fully
/// validated by a dominating predicate (regex allowlist, type check,
/// validation call, etc.) on every return path inside the function.
///
/// At a call site, each tainted argument passed to a position in
/// this list — and the call's own return value — are marked
/// `validated_must` / `validated_may` in the caller's SSA taint
/// state, the same way an inline `if (!regex.test(x)) throw` would
/// validate the surviving branch. Sound because the call only
/// returns normally on the validating arm; if validation failed,
/// control would not reach the post-call instruction.
///
/// Populated by
/// [`crate::taint::ssa_transfer::summary_extract::extract_ssa_func_summary`]
/// when a per-parameter probe shows the parameter's `var_name` in
/// `validated_must` at every return block of the helper. Empty
/// (the default) for helpers that do not validate any parameter.
/// Closes the validated-flow propagation gap that left
/// CVE-2026-25544 (Payload `sanitizeValue` SQL injection) detecting
/// on both vulnerable and patched code.
#[serde(default, skip_serializing_if = "SmallVec::is_empty")]
pub validated_params_to_return: SmallVec<[usize; 2]>,
}
/// A per-return-path [`PathFact`] entry.

View file

@ -441,6 +441,7 @@ fn ssa_summary_serde_round_trip_identity() {
field_points_to: Default::default(),
return_path_facts: smallvec::SmallVec::new(),
typed_call_receivers: vec![],
validated_params_to_return: smallvec::SmallVec::new(),
param_to_gate_filters: vec![],
};
let json = serde_json::to_string(&summary).unwrap();
@ -474,6 +475,7 @@ fn ssa_summary_serde_round_trip_strip_bits() {
field_points_to: Default::default(),
return_path_facts: smallvec::SmallVec::new(),
typed_call_receivers: vec![],
validated_params_to_return: smallvec::SmallVec::new(),
param_to_gate_filters: vec![],
};
let json = serde_json::to_string(&summary).unwrap();
@ -504,6 +506,7 @@ fn ssa_summary_serde_round_trip_add_bits() {
field_points_to: Default::default(),
return_path_facts: smallvec::SmallVec::new(),
typed_call_receivers: vec![],
validated_params_to_return: smallvec::SmallVec::new(),
param_to_gate_filters: vec![],
};
let json = serde_json::to_string(&summary).unwrap();
@ -541,6 +544,7 @@ fn ssa_summary_serde_round_trip_all_variants() {
field_points_to: Default::default(),
return_path_facts: smallvec::SmallVec::new(),
typed_call_receivers: vec![],
validated_params_to_return: smallvec::SmallVec::new(),
param_to_gate_filters: vec![],
};
let json = serde_json::to_string(&summary).unwrap();
@ -580,6 +584,7 @@ fn global_summaries_insert_ssa_exact_key_replacement() {
field_points_to: Default::default(),
return_path_facts: smallvec::SmallVec::new(),
typed_call_receivers: vec![],
validated_params_to_return: smallvec::SmallVec::new(),
param_to_gate_filters: vec![],
};
gs.insert_ssa(key.clone(), v1.clone());
@ -607,6 +612,7 @@ fn global_summaries_insert_ssa_exact_key_replacement() {
field_points_to: Default::default(),
return_path_facts: smallvec::SmallVec::new(),
typed_call_receivers: vec![],
validated_params_to_return: smallvec::SmallVec::new(),
param_to_gate_filters: vec![],
};
gs.insert_ssa(key.clone(), v2.clone());
@ -654,6 +660,7 @@ fn global_summaries_merge_with_ssa_entries() {
field_points_to: Default::default(),
return_path_facts: smallvec::SmallVec::new(),
typed_call_receivers: vec![],
validated_params_to_return: smallvec::SmallVec::new(),
param_to_gate_filters: vec![],
};
let sum_b = SsaFuncSummary {
@ -677,6 +684,7 @@ fn global_summaries_merge_with_ssa_entries() {
field_points_to: Default::default(),
return_path_facts: smallvec::SmallVec::new(),
typed_call_receivers: vec![],
validated_params_to_return: smallvec::SmallVec::new(),
param_to_gate_filters: vec![],
};
@ -724,6 +732,7 @@ fn global_summaries_is_empty_considers_ssa() {
field_points_to: Default::default(),
return_path_facts: smallvec::SmallVec::new(),
typed_call_receivers: vec![],
validated_params_to_return: smallvec::SmallVec::new(),
param_to_gate_filters: vec![],
},
);
@ -754,6 +763,7 @@ fn ssa_summary_serde_round_trip_param_to_sink_param() {
field_points_to: Default::default(),
return_path_facts: smallvec::SmallVec::new(),
typed_call_receivers: vec![],
validated_params_to_return: smallvec::SmallVec::new(),
param_to_gate_filters: vec![],
};
let json = serde_json::to_string(&summary).unwrap();
@ -799,6 +809,7 @@ fn ssa_summary_serde_round_trip_container_fields() {
field_points_to: Default::default(),
return_path_facts: smallvec::SmallVec::new(),
typed_call_receivers: vec![],
validated_params_to_return: smallvec::SmallVec::new(),
param_to_gate_filters: vec![],
};
let json = serde_json::to_string(&summary).unwrap();
@ -854,6 +865,7 @@ fn ssa_summary_serde_round_trip_return_abstract() {
field_points_to: Default::default(),
return_path_facts: smallvec::SmallVec::new(),
typed_call_receivers: vec![],
validated_params_to_return: smallvec::SmallVec::new(),
param_to_gate_filters: vec![],
};
let json = serde_json::to_string(&summary).unwrap();
@ -1375,6 +1387,7 @@ fn global_summaries_resolve_body_requires_body_present() {
field_points_to: Default::default(),
return_path_facts: smallvec::SmallVec::new(),
typed_call_receivers: vec![],
validated_params_to_return: smallvec::SmallVec::new(),
param_to_gate_filters: vec![],
},
);
@ -3519,6 +3532,7 @@ fn cf4_return_path_transform_serde_round_trip() {
field_points_to: Default::default(),
return_path_facts: smallvec::SmallVec::new(),
typed_call_receivers: vec![],
validated_params_to_return: smallvec::SmallVec::new(),
param_to_gate_filters: vec![],
};
let json = serde_json::to_string(&summary).unwrap();