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

@ -134,6 +134,9 @@ pub static RULES: &[LabelRule] = &[
label: DataLabel::Sink(Cap::CODE_EXEC),
case_sensitive: false,
},
// (Lodash `_.template` is modeled as a gated sink in `GATED_SINKS`
// below — the gate inspects arg 1's options object so the patched
// `{ evaluate: false }` form is suppressed.)
LabelRule {
matchers: &["innerHTML", "dangerouslySetInnerHTML"],
label: DataLabel::Sink(Cap::HTML_ESCAPE),
@ -377,6 +380,46 @@ pub static GATED_SINKS: &[SinkGate] = &[
dangerous_kwargs: &[],
activation: GateActivation::ValueMatch,
},
// Lodash `_.template(template, options?)` — server-side template
// injection sink. Lodash's template parser by default compiles
// `<% ... %>` evaluate blocks into a JavaScript Function via the
// `Function` constructor; when the template string is attacker-
// controlled this is RCE (Strapi CVE-2023-22621 et al.).
//
// Gate: activate on arg 0 (the template string). Inspect arg 1's
// options object for `evaluate: false`; when present as a literal
// the evaluate-block compiler is disabled and the call is safe.
// Missing arg 1, missing `evaluate` key, or a dynamic value all
// fall through `ValueMatch`'s `None` branch and fire conservatively.
//
// The `keyword_name`-based activation reads the property value via
// the JS-side closure augmentation in `cfg/mod.rs`, which falls
// back to walking the call's arg-1 object literal when the
// language-default `keyword_argument` extraction yields nothing.
SinkGate {
callee_matcher: "_.template",
arg_index: 0,
dangerous_values: &["true"],
dangerous_prefixes: &[],
label: DataLabel::Sink(Cap::CODE_EXEC),
case_sensitive: true,
payload_args: &[0],
keyword_name: Some("evaluate"),
dangerous_kwargs: &[],
activation: GateActivation::ValueMatch,
},
SinkGate {
callee_matcher: "lodash.template",
arg_index: 0,
dangerous_values: &["true"],
dangerous_prefixes: &[],
label: DataLabel::Sink(Cap::CODE_EXEC),
case_sensitive: true,
payload_args: &[0],
keyword_name: Some("evaluate"),
dangerous_kwargs: &[],
activation: GateActivation::ValueMatch,
},
// ── Outbound HTTP clients (SSRF) ──────────────────────────────────────
//
// Policy: SSRF fires only when taint reaches the destination-bearing
@ -810,7 +853,21 @@ pub static KINDS: Map<&'static str, Kind> = phf_map! {
pub static PARAM_CONFIG: ParamConfig = ParamConfig {
params_field: "parameters",
param_node_kinds: &["identifier"],
// `identifier` covers bare params (`a`); `assignment_pattern` covers
// default-value params (`a = {}`). Without `assignment_pattern`,
// tree-sitter wraps the identifier in a node the param walker
// doesn't recognize, and `extract_param_meta` produces a
// parameter-less summary for any function whose params have
// defaults — breaking cross-function `param_to_sink` propagation
// for shapes like `(emailOptions = {}, emailTemplate = {}, data = {}) => …`.
// `object_pattern` covers destructured object formals (`({ a, b })`),
// which tree-sitter-javascript exposes as a direct child of
// `formal_parameters` (no `required_parameter` wrapper as in TS).
// Without it the per-parameter probe never seeds the destructured
// bindings and summary extraction misses `validated_params_to_return`
// for shapes like `({ value }) => { validate(value); ... }` —
// residual gap behind CVE-2026-25544.
param_node_kinds: &["identifier", "assignment_pattern", "object_pattern"],
self_param_kinds: &[],
ident_fields: &["name", "pattern"],
};