mirror of
https://github.com/elicpeter/nyx.git
synced 2026-07-06 20:42:10 +02:00
new capacity bits (#67)
This commit is contained in:
parent
afaffc0df6
commit
7d0e7320e2
261 changed files with 10591 additions and 231 deletions
|
|
@ -67,6 +67,30 @@ pub static RULES: &[LabelRule] = &[
|
|||
label: DataLabel::Sink(Cap::SSRF),
|
||||
case_sensitive: false,
|
||||
},
|
||||
// ─── LDAP injection sinks ───
|
||||
//
|
||||
// OpenLDAP / libldap surface: `ldap_search_s(ld, base, scope, filter, ...)`
|
||||
// and the asynchronous variant `ldap_search_ext_s(ld, base, scope, filter,
|
||||
// attrs, attrsonly, serverctrls, clientctrls, timeout, sizelimit, *res)`.
|
||||
// The filter argument (position 3) is the LDAP-injection vector. No
|
||||
// standard libldap escape helper exists in the C surface; sanitisation is
|
||||
// typically caller-implemented (`sanitize_*` covers the developer-named
|
||||
// case via the existing prefix rule above).
|
||||
LabelRule {
|
||||
matchers: &["ldap_search_s", "ldap_search_ext_s"],
|
||||
label: DataLabel::Sink(Cap::LDAP_INJECTION),
|
||||
case_sensitive: false,
|
||||
},
|
||||
// ─── XPath injection sinks ───
|
||||
//
|
||||
// libxml2 evaluation entry points: `xmlXPathEvalExpression(expr, ctx)`,
|
||||
// `xmlXPathEval(expr, ctx)`, `xmlXPathCompile(expr)`. The expression
|
||||
// string is arg 0 and is the canonical XPath-injection vector.
|
||||
LabelRule {
|
||||
matchers: &["xmlXPathEvalExpression", "xmlXPathEval", "xmlXPathCompile"],
|
||||
label: DataLabel::Sink(Cap::XPATH_INJECTION),
|
||||
case_sensitive: false,
|
||||
},
|
||||
];
|
||||
|
||||
/// Gated sinks for C.
|
||||
|
|
|
|||
|
|
@ -89,6 +89,24 @@ pub static RULES: &[LabelRule] = &[
|
|||
label: DataLabel::Sink(Cap::SSRF),
|
||||
case_sensitive: false,
|
||||
},
|
||||
// ─── LDAP injection sinks ───
|
||||
//
|
||||
// OpenLDAP / libldap C interface (also used from C++ wrappers): the filter
|
||||
// argument carries attacker-controlled data unless explicitly escaped.
|
||||
LabelRule {
|
||||
matchers: &["ldap_search_s", "ldap_search_ext_s"],
|
||||
label: DataLabel::Sink(Cap::LDAP_INJECTION),
|
||||
case_sensitive: false,
|
||||
},
|
||||
// ─── XPath injection sinks ───
|
||||
//
|
||||
// libxml2 (the dominant C++ XML parser surface): `xmlXPathEvalExpression`,
|
||||
// `xmlXPathEval`, `xmlXPathCompile` accept the expression string as arg 0.
|
||||
LabelRule {
|
||||
matchers: &["xmlXPathEvalExpression", "xmlXPathEval", "xmlXPathCompile"],
|
||||
label: DataLabel::Sink(Cap::XPATH_INJECTION),
|
||||
case_sensitive: false,
|
||||
},
|
||||
];
|
||||
|
||||
/// Gated sinks for C++.
|
||||
|
|
|
|||
|
|
@ -148,6 +148,97 @@ pub static RULES: &[LabelRule] = &[
|
|||
label: DataLabel::Sink(Cap::CRYPTO),
|
||||
case_sensitive: false,
|
||||
},
|
||||
// ─── LDAP injection sinks ───
|
||||
//
|
||||
// go-ldap (`github.com/go-ldap/ldap/v3`): `conn, _ := ldap.DialURL(url);
|
||||
// req := ldap.NewSearchRequest(base, scope, deref, sizeLimit, timeLimit,
|
||||
// typesOnly, filter, attrs, controls)`. The filter argument (position 6)
|
||||
// is the LDAP-injection vector; passing the request to `conn.Search(req)`
|
||||
// executes the filter. Type-qualified resolution rewrites `conn.Search`
|
||||
// → `LdapClient.Search` when the receiver was returned by
|
||||
// `ldap.DialURL` / `ldap.Dial` / `ldap.DialTLS` (see
|
||||
// [`crate::ssa::type_facts::constructor_type`]). We also tag
|
||||
// `ldap.NewSearchRequest` directly so taint reaching the filter argument
|
||||
// surfaces at the construction call (matches the typical FP-free shape
|
||||
// where the request is built once and passed straight to `Search`).
|
||||
LabelRule {
|
||||
matchers: &[
|
||||
"LdapClient.Search",
|
||||
"LdapClient.SearchWithPaging",
|
||||
"ldap.NewSearchRequest",
|
||||
],
|
||||
label: DataLabel::Sink(Cap::LDAP_INJECTION),
|
||||
case_sensitive: true,
|
||||
},
|
||||
// ─── LDAP-filter sanitizer ───
|
||||
//
|
||||
// go-ldap exposes `ldap.EscapeFilter(s string) string` (RFC 4515 metachar
|
||||
// escaping). Treat any call as clearing the LDAP_INJECTION cap.
|
||||
LabelRule {
|
||||
matchers: &["ldap.EscapeFilter"],
|
||||
label: DataLabel::Sanitizer(Cap::LDAP_INJECTION),
|
||||
case_sensitive: true,
|
||||
},
|
||||
// ─── Header / CRLF injection sinks ───
|
||||
//
|
||||
// `net/http` `ResponseWriter.Header()` returns a `Header` map; calls to
|
||||
// `Set(name, val)` / `Add(name, val)` write a single header value.
|
||||
// After paren-group stripping the chain text becomes
|
||||
// `w.Header.Set` / `w.Header.Add`, so suffix matchers on `Header.Set` /
|
||||
// `Header.Add` cover both the bound-receiver form (`w.Header().Set(...)`)
|
||||
// and the documentation-style class-qualified form (`Header.Set`).
|
||||
// Tainted strings without `\r\n` stripping enable response splitting.
|
||||
LabelRule {
|
||||
matchers: &["Header.Set", "Header.Add"],
|
||||
label: DataLabel::Sink(Cap::HEADER_INJECTION),
|
||||
case_sensitive: true,
|
||||
},
|
||||
// ─── Header / CRLF sanitizers ───
|
||||
//
|
||||
// Project-local `stripCRLF` / `escapeHeader` helpers that strip `\r` and
|
||||
// `\n` from a value before it is written to a response header.
|
||||
LabelRule {
|
||||
matchers: &["stripCRLF", "stripCrlf", "escapeHeader", "sanitizeHeader"],
|
||||
label: DataLabel::Sanitizer(Cap::HEADER_INJECTION),
|
||||
case_sensitive: false,
|
||||
},
|
||||
// ─── Open redirect sinks ───
|
||||
//
|
||||
// `net/http` `http.Redirect(w, r, url, code)` writes a `Location` header
|
||||
// and a 3xx status from the supplied URL. Without an allowlist check,
|
||||
// a tainted `url` is the canonical Go open-redirect vector.
|
||||
LabelRule {
|
||||
matchers: &["http.Redirect"],
|
||||
label: DataLabel::Sink(Cap::OPEN_REDIRECT),
|
||||
case_sensitive: false,
|
||||
},
|
||||
LabelRule {
|
||||
matchers: &[
|
||||
"validateRedirectUrl",
|
||||
"isSafeRedirect",
|
||||
"stripScheme",
|
||||
"ensureRelativeUrl",
|
||||
"assertRelativePath",
|
||||
"isRelativeUrl",
|
||||
],
|
||||
label: DataLabel::Sanitizer(Cap::OPEN_REDIRECT),
|
||||
case_sensitive: false,
|
||||
},
|
||||
// ─── SSTI sinks ───
|
||||
//
|
||||
// `text/template` and `html/template` parse a template source string via
|
||||
// `template.New(name).Parse(src)`. After paren-group stripping the chain
|
||||
// text becomes `template.New.Parse`, so the suffix matcher catches both
|
||||
// packages (`text/template`, `html/template`) regardless of import alias.
|
||||
// `template.ParseFiles` / `ParseGlob` take file paths (path-traversal,
|
||||
// not SSTI) and are intentionally excluded. `html/template`'s auto-
|
||||
// escaping applies during `Execute`, not `Parse`, so a tainted source
|
||||
// string still yields SSTI.
|
||||
LabelRule {
|
||||
matchers: &["template.New.Parse"],
|
||||
label: DataLabel::Sink(Cap::SSTI),
|
||||
case_sensitive: false,
|
||||
},
|
||||
];
|
||||
|
||||
/// Argument-role-aware Go sinks. Two classes coexist on the outbound HTTP
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
use crate::labels::{Cap, DataLabel, Kind, LabelRule, ParamConfig, RuntimeLabelRule};
|
||||
use crate::labels::{
|
||||
Cap, DataLabel, GateActivation, Kind, LabelRule, ParamConfig, RuntimeLabelRule, SinkGate,
|
||||
};
|
||||
use crate::utils::project::{DetectedFramework, FrameworkContext};
|
||||
use phf::{Map, phf_map};
|
||||
|
||||
|
|
@ -265,6 +267,223 @@ pub static RULES: &[LabelRule] = &[
|
|||
label: DataLabel::Sink(Cap::CODE_EXEC),
|
||||
case_sensitive: false,
|
||||
},
|
||||
// ─── LDAP injection sinks ───
|
||||
//
|
||||
// JNDI / Spring LDAP search APIs accept an attacker-influenceable filter
|
||||
// expression as either the second positional argument (`DirContext.search(name,
|
||||
// filter, controls)` / `LdapTemplate.search(base, filter, mapper)`). Without
|
||||
// RFC 4515 escaping the filter can be rewritten to bypass authentication or
|
||||
// exfiltrate directory entries. Type-qualified resolution rewrites
|
||||
// `ctx.search(...)` → `LdapClient.search` when the receiver carries a
|
||||
// `TypeKind::LdapClient` fact (set by `class_name_to_type_kind` for the
|
||||
// declared types `DirContext`, `InitialDirContext`, `LdapContext`,
|
||||
// `LdapTemplate`, or by `constructor_type` for `new InitialDirContext(...)`
|
||||
// / `new InitialLdapContext(...)`). Direct flat matchers cover the
|
||||
// documentation-style class-qualified call forms that bypass receiver
|
||||
// typing.
|
||||
LabelRule {
|
||||
matchers: &[
|
||||
"LdapClient.search",
|
||||
"LdapClient.searchByEntity",
|
||||
"LdapClient.searchForObject",
|
||||
"LdapClient.searchForContext",
|
||||
"DirContext.search",
|
||||
"LdapTemplate.search",
|
||||
"LdapTemplate.searchByEntity",
|
||||
"LdapTemplate.searchForObject",
|
||||
"LdapTemplate.searchForContext",
|
||||
"ctx.search",
|
||||
],
|
||||
label: DataLabel::Sink(Cap::LDAP_INJECTION),
|
||||
case_sensitive: true,
|
||||
},
|
||||
// ─── LDAP-filter sanitizers ───
|
||||
//
|
||||
// Spring LDAP's `LdapEncoder.filterEncode(s)` applies RFC 4515 escaping to
|
||||
// metacharacters (`\`, `*`, `(`, `)`, ` | ||||