Move StdBasicRegex to shared StdRegex module; return ExploitableStringLiteral to flow layer

This commit is contained in:
copilot-swe-agent[bot]
2026-07-20 12:49:45 +00:00
committed by GitHub
parent 4fab077bbe
commit 645f2fdc3d
3 changed files with 59 additions and 41 deletions

View File

@@ -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

View File

@@ -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`

View 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")
}
}