mirror of
https://github.com/github/codeql.git
synced 2026-07-21 19:22:02 +02:00
Move StdBasicRegex to shared StdRegex module; return ExploitableStringLiteral to flow layer
This commit is contained in:
committed by
GitHub
parent
4fab077bbe
commit
645f2fdc3d
@@ -30,18 +30,13 @@ import cpp
|
||||
private import semmle.code.cpp.dataflow.new.DataFlow
|
||||
private import semmle.code.cpp.dataflow.new.TaintTracking
|
||||
private import semmle.code.cpp.regex.internal.RegexGrammar as RG
|
||||
import semmle.code.cpp.regex.internal.StdRegex
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Re-exports from the flow-free grammar/flag module.
|
||||
// These are kept in scope for backward compatibility so that consumers can
|
||||
// continue to import them from `RegexFlowConfigs`.
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* A `std::basic_regex` class type (or instantiation thereof, e.g. `std::regex`,
|
||||
* `std::wregex`). Defined in `RegexGrammar`; re-exported here.
|
||||
*/
|
||||
class StdBasicRegex = RG::StdBasicRegex;
|
||||
|
||||
/** See `RegexGrammar::TRegexGrammar`. */
|
||||
class TRegexGrammar = RG::TRegexGrammar;
|
||||
|
||||
@@ -202,10 +197,24 @@ class RegexPatternSink extends DataFlow::Node {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Fast-path: only track literals that look regex-y.
|
||||
// `ExploitableStringLiteral` is defined in the flow-free `RegexGrammar`
|
||||
// module; re-exported here for backward compatibility.
|
||||
// ---------------------------------------------------------------------------
|
||||
class ExploitableStringLiteral = RG::ExploitableStringLiteral;
|
||||
/**
|
||||
* A string literal that is a plausible ReDoS candidate: it contains at least
|
||||
* one unbounded-repetition quantifier (`+`, `*`, or `{n,}`).
|
||||
*
|
||||
* This is used as the source set of the taint-tracking configuration below:
|
||||
* regexes without such a quantifier are not interesting for the
|
||||
* polynomial-ReDoS analysis, so filtering them out here is a significant
|
||||
* optimisation.
|
||||
*/
|
||||
class ExploitableStringLiteral extends StringLiteral {
|
||||
ExploitableStringLiteral() {
|
||||
exists(string s | s = this.getValue() |
|
||||
s.regexpMatch(".*[+*].*") or
|
||||
s.regexpMatch(".*\\{[0-9]+,[0-9]*\\}.*")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A dataflow configuration tracking string literals that reach a regex
|
||||
|
||||
@@ -21,42 +21,27 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
private import semmle.code.cpp.regex.internal.StdRegex
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Fast-path filter: only consider literals that *look* regex-y
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* A string literal that is a plausible ReDoS candidate: it contains at least
|
||||
* one unbounded-repetition quantifier (`+`, `*`, or `{n,}`).
|
||||
* Holds if `s` is a plausible ReDoS-candidate string literal value: it
|
||||
* contains at least one unbounded-repetition quantifier (`+`, `*`, or
|
||||
* `{n,}`).
|
||||
*
|
||||
* This filter is applied by the flag readers below as an optimisation:
|
||||
* regexes without such a quantifier are not interesting for the
|
||||
* polynomial-ReDoS analysis, and skipping them here mirrors the source
|
||||
* restriction of the dataflow configuration in `RegexFlowConfigs`,
|
||||
* preserving analysis results exactly.
|
||||
* This mirrors the source restriction of the dataflow configuration in
|
||||
* `RegexFlowConfigs` (whose sources are the `ExploitableStringLiteral`s),
|
||||
* so that the flag readers below consider exactly the same set of regexes
|
||||
* as the taint-tracking configuration. Inlined here as a syntactic check
|
||||
* to keep this module flow-free.
|
||||
*/
|
||||
class ExploitableStringLiteral extends StringLiteral {
|
||||
ExploitableStringLiteral() {
|
||||
exists(string s | s = this.getValue() |
|
||||
s.regexpMatch(".*[+*].*") or
|
||||
s.regexpMatch(".*\\{[0-9]+,[0-9]*\\}.*")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// std::basic_regex identification
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* A `std::basic_regex` class type (or instantiation thereof, e.g. `std::regex`,
|
||||
* `std::wregex`).
|
||||
*/
|
||||
class StdBasicRegex extends Class {
|
||||
StdBasicRegex() {
|
||||
this.hasQualifiedName("std", "basic_regex")
|
||||
or
|
||||
this.(ClassTemplateInstantiation).getTemplate().hasQualifiedName("std", "basic_regex")
|
||||
}
|
||||
private predicate isExploitableStringLiteralValue(StringLiteral s) {
|
||||
exists(string v | v = s.getValue() |
|
||||
v.regexpMatch(".*[+*].*") or
|
||||
v.regexpMatch(".*\\{[0-9]+,[0-9]*\\}.*")
|
||||
)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -124,13 +109,14 @@ private predicate isPatternLiteralOf(StringLiteral regex, Expr patternArg) {
|
||||
* This association is purely syntactic (no dataflow): the pattern literal
|
||||
* must appear inside the constructor/assign call's first argument.
|
||||
*
|
||||
* Restricted to `ExploitableStringLiteral` to preserve the exact set of
|
||||
* Restricted to string literals that look like plausible ReDoS candidates
|
||||
* (see `isExploitableStringLiteralValue`) to preserve the exact set of
|
||||
* regexes considered by the flag readers when they were routed through
|
||||
* the taint-tracking configuration (whose sources were the same
|
||||
* the taint-tracking configuration (whose sources are the same
|
||||
* `ExploitableStringLiteral` set).
|
||||
*/
|
||||
private Expr getConstructionFlagArg(StringLiteral regex) {
|
||||
regex instanceof ExploitableStringLiteral and
|
||||
isExploitableStringLiteralValue(regex) and
|
||||
(
|
||||
// basic_regex(pattern, flags) constructor - both named-variable and
|
||||
// temporary constructions are covered because the search for `regex`
|
||||
|
||||
23
cpp/ql/lib/semmle/code/cpp/regex/internal/StdRegex.qll
Normal file
23
cpp/ql/lib/semmle/code/cpp/regex/internal/StdRegex.qll
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Provides identification of the `std::basic_regex` standard-library API type.
|
||||
*
|
||||
* This is shared infrastructure used by both the flow-free grammar/flag
|
||||
* module (`RegexGrammar`) and the dataflow layer (`RegexFlowConfigs`). It
|
||||
* lives in its own module so that importing it does not pull in the
|
||||
* dataflow libraries: both `RegexGrammar.qll` and `RegexFlowConfigs.qll`
|
||||
* can import it, keeping the parser/tree-view layer flow-free.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* A `std::basic_regex` class type (or instantiation thereof, e.g. `std::regex`,
|
||||
* `std::wregex`).
|
||||
*/
|
||||
class StdBasicRegex extends Class {
|
||||
StdBasicRegex() {
|
||||
this.hasQualifiedName("std", "basic_regex")
|
||||
or
|
||||
this.(ClassTemplateInstantiation).getTemplate().hasQualifiedName("std", "basic_regex")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user