-
Notifications
You must be signed in to change notification settings - Fork 2k
C++ regex parse tree and RegexTreeViewSig implementation (Phase 1 of ReDoS support) #22200
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
Copilot
wants to merge
34
commits into
main
Choose a base branch
from
copilot/add-cpp-regular-expression-parser
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
34 commits
Select commit
Hold shift + click to select a range
232258d
Initial plan
Copilot a27142d
Add C++ regex parser and RegexTreeView (Phase 1)
Copilot 239ac44
Fix change note category to 'minorAnalysis'
Copilot f72bad7
Add C++ regex flow modeling and construction-site flags (Phase 2)
Copilot 84d7a2b
Add cpp/polynomial-redos query (Phase 3)
Copilot 47969ae
Add cpp/redos exponential ReDoS query (Phase 4)
Copilot d6c76aa
Fix cpp/redos test: swap non-exponential (a|ab)* for (a*)* and popula…
Copilot 8a8590c
Expand C++ ReDoS test coverage (Phase 4 follow-up)
Copilot 00672d6
Expand cpp/polynomial-redos test coverage (Phase 3 follow-up)
Copilot c1103b9
Fix C++ ECMAScript regex parser: recognize POSIX bracket sub-expressi…
Copilot d9f380d
POSIX bracket soundness: opaque punct/cntrl/print/graph; non-recursiv…
Copilot e0cc679
Add isBacktrackingEngine gate excluding awk/grep/egrep from C++ ReDoS…
Copilot e728c71
Refactor C++ regex parser into shared core + EcmaRegExp dialect hooks
Copilot 9a3613e
Add POSIX ERE grammar support to the C++ regex parser
Copilot bc5cecf
Add end-to-end ERE ReDoS query tests
Copilot c1cb146
Add POSIX BRE grammar support: BreRegExp + shared-core refactor
Copilot 8b12f32
Add BRE query test coverage and change note; regenerate .expected
Copilot 918d51c
Add QLdoc to TRegexGrammar branches
Copilot 9695195
Fix "modelled" -> "modeled" spelling in regex PR files
Copilot 4435279
Replace omittable exists variables with don't-care _
Copilot 9758d52
Fix Predicate QLDoc style: reword ecma_named_group_start doc to start…
Copilot b48b22c
Merge branch 'main' into copilot/add-cpp-regular-expression-parser
jketema 6d07e4d
Apply codeql query format to regex library files
Copilot 1c810ed
Replace non-ASCII characters (em/en dash, arrow) with ASCII equivalen…
Copilot cc95e25
Resolve dead-code alerts: export RegexPatternFlowConfig module
Copilot 09938f0
Revert "Resolve dead-code alerts: export RegexPatternFlowConfig module"
Copilot 4be7ff0
Convert ReDoS query tests to inline test expectations
Copilot 2bab816
Remove redundant CompileCheck.ql to resolve dead-code alerts
Copilot c51dcdd
Add CWE-1333 ReDoS queries to cpp query-suite .expected snapshots
Copilot 9fdf8b7
Add location tests for the C++ regex parse-tree view
Copilot db58698
Fix C++ regex term source locations for raw and encoding-prefixed lit…
Copilot 4fab077
Extract C++ regex grammar/flag knobs into flow-free RegexGrammar module
Copilot 645f2fd
Move StdBasicRegex to shared StdRegex module; return ExploitableStrin…
Copilot ebecc19
Remove duplicated ReDoS fast-path filter from RegexGrammar; narrow at…
Copilot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| --- | ||
| category: minorAnalysis | ||
| --- | ||
| * Added an internal C++ regular-expression parse library (`semmle.code.cpp.regex.RegexTreeView`) that implements the shared `RegexTreeViewSig` signature. This is the foundation for future ReDoS analyses for C++ (`cpp/polynomial-redos` and `cpp/redos`). No new queries are added in this release. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| --- | ||
| category: minorAnalysis | ||
| --- | ||
| * Added internal C++ regex usage and flow modeling (`semmle.code.cpp.regex.RegexFlowConfigs`). The `RegexTreeView` `RegExp` class now only matches `StringLiteral`s that flow to a `std::basic_regex` construction/assignment or a `std::regex_match`/`std::regex_search`/`std::regex_replace`/`std::regex_iterator`/`std::regex_token_iterator` call. Construction-site flags in `std::regex_constants` are detected (`icase`, `multiline`, and the grammar flags), including bitwise-`|` combinations. Literals constructed with an explicit non-ECMAScript grammar flag (`basic`, `extended`, `awk`, `grep`, `egrep`) are excluded from the ECMAScript-only parser. No new queries are added in this release. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| category: minorAnalysis | ||
| --- | ||
| * The C++ regular-expression parser (`semmle.code.cpp.regex.internal.ParseRegExp`) now models POSIX Basic Regular Expressions (BRE) via a new concrete `BreRegExp` subclass. BRE is selected by the `std::regex_constants::basic` and `grep` flags. Where BRE inverts the metacharacter/escape convention relative to POSIX ERE and ECMAScript — backslash-prefixed `\(...\)` are capturing groups (bare `(...)` are literals), `\{n,m\}` is the interval quantifier (bare braces are literal text), and `+`, `?`, `|` are literal characters — the parser follows the POSIX specification. Positional rules for `*` (literal at the start of a subexpression), `^` (anchor only at start of subexpression) and `$` (anchor only at end of subexpression) are implemented; `\1`..`\9` numbered back-references are supported. The GNU-BRE extensions `\+`, `\?`, `\|` are not modeled — they are treated as escaped literals. Along with ECMAScript (`EcmaRegExp`) and POSIX ERE (`EreRegExp`), all six of the `std::regex_constants` grammar flags now have a concrete parser subclass and every regex is analyzed. | ||
| * The C++ ReDoS queries (`cpp/redos` and `cpp/polynomial-redos`) now analyze regexes constructed with the `std::regex_constants::basic` grammar flag; `grep` remains excluded via `isBacktrackingEngine` (POSIX tool-style engines are treated as linear-time), mirroring the existing `extended`-vs-`egrep`/`awk` split for ERE. |
4 changes: 4 additions & 0 deletions
4
cpp/ql/lib/change-notes/2026-07-17-cpp-regex-non-backtracking-engine.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| --- | ||
| category: minorAnalysis | ||
| --- | ||
| * The C++ ReDoS queries (`cpp/redos` and `cpp/polynomial-redos`) now treat regexes constructed with the `std::regex_constants::awk`, `grep`, or `egrep` grammar flags as non-backtracking, and exclude them from analysis. These POSIX tool-style grammars correspond to traditionally linear-time (DFA-based) matching semantics, so super-linear-backtracking ReDoS does not apply. The `basic` (BRE) and `extended` (ERE) grammars remain backtracking-eligible. Grammar selection and ReDoS-eligibility are exposed as independent axes via the new `isBacktrackingEngine` predicate in `semmle.code.cpp.regex.RegexFlowConfigs`. |
5 changes: 5 additions & 0 deletions
5
cpp/ql/lib/change-notes/2026-07-17-cpp-regex-posix-brackets.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| category: minorAnalysis | ||
| --- | ||
| * The C++ regular-expression parser (`semmle.code.cpp.regex.RegexTreeView`) now recognises POSIX bracket sub-expressions inside character classes as single character-matching atoms: POSIX character classes (`[[:alpha:]]`, `[[:digit:]]`, `[[:space:]]`, ...), collating symbols (`[[.a.]]`), and equivalence classes (`[[=a=]]`). These are ECMAScript-mode extensions accepted by `std::regex` but not part of ECMA-262 JavaScript, so they were previously mis-tokenized (the class was incorrectly closed at the inner `]`). POSIX character classes whose match set is `\d`, `\s`, or `\w` — or a subset of one (`digit`, `xdigit`, `space`, `blank`, `word`, `alpha`, `alnum`, `upper`, `lower`) — are mapped onto `\d`/`\s`/`\w` for the shared engine's escape-class reasoning. The remaining POSIX classes (`punct`, `cntrl`, `print`, `graph`) as well as collating and equivalence classes are left opaque: they still parse as single character-consuming class-member atoms, but the shared engine treats their character set as unknown rather than unsoundly equating it with `\w`. | ||
| * Documented that `std::regex_constants::multiline` is detected by `RegexFlowConfigs.qll` but not currently modeled in the parse tree — `^` and `$` are always treated as string-start/string-end anchors, matching the conservative choice made by other language ReDoS analyses. Precise multiline modeling would require extending the shared `RegexTreeViewSig` signature and is left as future work. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,332 @@ | ||
| /** | ||
| * Provides classes and predicates for reasoning about C++ standard-library | ||
| * regular-expression **usage** (dataflow layer). | ||
| * | ||
| * This module identifies which `StringLiteral`s flow (via global taint | ||
| * tracking) into a `std::basic_regex` construction or into one of the free | ||
| * matching functions (`std::regex_match`, `std::regex_search`, | ||
| * `std::regex_replace`) or iterators (`std::regex_iterator`, | ||
| * `std::regex_token_iterator`). | ||
| * | ||
| * The grammar dialect and construction-site *flags* of a regex literal | ||
| * (`icase`, `multiline`, `basic`, `extended`, etc.) are independent of the | ||
| * dataflow analysis - they are properties of the parsed literal itself - | ||
| * and live in the flow-free module | ||
| * `semmle.code.cpp.regex.internal.RegexGrammar`. This module re-exports the | ||
| * public predicates from there so existing consumers (`hasIgnoreCaseFlag`, | ||
| * `hasMultilineFlag`, `regexGrammar`, ...) can continue to import them | ||
| * from `RegexFlowConfigs`. | ||
| * | ||
| * The `regexMatchedAgainst` predicate mirrors the intent of the Java | ||
| * `RegexFlowConfigs.qll` library. | ||
| * | ||
| * All standard `std::regex` grammars are now modeled: ECMAScript (default), | ||
| * POSIX BRE (`basic`/`grep`), and POSIX ERE (`extended`/`egrep`/`awk`). | ||
| * Grammar selection and ReDoS-eligibility are independent axes - see | ||
| * `isBacktrackingEngine` for the latter. | ||
| */ | ||
|
|
||
| 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`. They are narrowed here | ||
| // to `ExploitableStringLiteral` — the same source set as the taint-tracking | ||
| // configuration below — so the flag/grammar readers exposed at this layer | ||
| // only report on regexes that are ReDoS-analysis candidates. The | ||
| // underlying flow-free predicates in `RegexGrammar` are unrestricted; any | ||
| // consumer wanting the full set can call them directly. | ||
| // --------------------------------------------------------------------------- | ||
| /** See `RegexGrammar::TRegexGrammar`. */ | ||
| class TRegexGrammar = RG::TRegexGrammar; | ||
|
|
||
| /** See `RegexGrammar::regexGrammar`. */ | ||
| TRegexGrammar regexGrammar(StringLiteral regex) { | ||
| regex instanceof ExploitableStringLiteral and | ||
| result = RG::regexGrammar(regex) | ||
| } | ||
|
|
||
| /** See `RegexGrammar::hasConcreteGrammar`. */ | ||
| predicate hasConcreteGrammar(TRegexGrammar grammar) { RG::hasConcreteGrammar(grammar) } | ||
|
|
||
| /** See `RegexGrammar::hasIgnoreCaseFlag`. */ | ||
| predicate hasIgnoreCaseFlag(StringLiteral regex) { | ||
| regex instanceof ExploitableStringLiteral and | ||
| RG::hasIgnoreCaseFlag(regex) | ||
| } | ||
|
|
||
| /** See `RegexGrammar::hasMultilineFlag`. */ | ||
| predicate hasMultilineFlag(StringLiteral regex) { | ||
| regex instanceof ExploitableStringLiteral and | ||
| RG::hasMultilineFlag(regex) | ||
| } | ||
|
|
||
| /** See `RegexGrammar::hasNonEcmaScriptGrammarFlag`. */ | ||
| predicate hasNonEcmaScriptGrammarFlag(StringLiteral regex) { | ||
| regex instanceof ExploitableStringLiteral and | ||
| RG::hasNonEcmaScriptGrammarFlag(regex) | ||
| } | ||
|
|
||
| /** See `RegexGrammar::hasEcmaScriptGrammarFlag`. */ | ||
| predicate hasEcmaScriptGrammarFlag(StringLiteral regex) { | ||
| regex instanceof ExploitableStringLiteral and | ||
| RG::hasEcmaScriptGrammarFlag(regex) | ||
| } | ||
|
|
||
| // --------------------------------------------------------------------------- | ||
| // std::basic_regex type helpers | ||
| // --------------------------------------------------------------------------- | ||
| /** | ||
| * Holds if `t`, after stripping references, const/volatile, and typedefs, | ||
| * denotes a `std::basic_regex` type. | ||
| */ | ||
| private predicate isBasicRegexType(Type t) { | ||
| exists(Type u | u = t.stripType() | | ||
| u instanceof StdBasicRegex | ||
| or | ||
| // Typedefs (e.g. `std::regex` = `std::basic_regex<char>`) resolve via | ||
| // `getUnderlyingType()`. | ||
| u.(TypedefType).getBaseType().stripType() instanceof StdBasicRegex | ||
| ) | ||
| } | ||
|
|
||
| /** | ||
| * Gets the parameter of `f` whose type is a reference (or plain) to | ||
| * `std::basic_regex`, i.e. the parameter that receives the regex object. | ||
| */ | ||
| private Parameter getRegexObjectParameter(Function f) { | ||
| result = f.getAParameter() and | ||
| isBasicRegexType(result.getType()) | ||
| } | ||
|
|
||
| /** | ||
| * Gets a parameter of `f` whose type is a string-like: `const char*`, | ||
| * `const wchar_t*`, `std::basic_string`, or a `char`/`wchar_t` iterator | ||
| * pair. Used to identify the subject string of match/search/replace calls. | ||
| */ | ||
| private Parameter getStringLikeParameter(Function f) { | ||
| result = f.getAParameter() and | ||
| exists(Type u | u = result.getUnspecifiedType().stripType() | | ||
| // basic_string (by value or reference) | ||
| u.(Class).hasQualifiedName("std", "basic_string") | ||
| or | ||
| u.(ClassTemplateInstantiation).getTemplate().hasQualifiedName("std", "basic_string") | ||
| or | ||
| // C strings: const char* / const wchar_t* | ||
| exists(PointerType p | | ||
| p = u | ||
| or | ||
| p = u.(ReferenceType).getBaseType().stripType() | ||
| | | ||
| p.getBaseType().stripType() instanceof CharType | ||
| or | ||
| p.getBaseType().stripType() instanceof Wchar_t | ||
| ) | ||
| ) | ||
| } | ||
|
|
||
| // --------------------------------------------------------------------------- | ||
| // Match / search / replace / iterator calls | ||
| // --------------------------------------------------------------------------- | ||
| /** | ||
| * A free function in namespace `std` that matches a subject against a regex: | ||
| * one of `regex_match`, `regex_search`, or `regex_replace`. | ||
| */ | ||
| private class StdRegexMatchFunction extends Function { | ||
| StdRegexMatchFunction() { | ||
| this.getNamespace().getName() = "std" and | ||
| this.getName() = ["regex_match", "regex_search", "regex_replace"] | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * A class template instantiation of `std::regex_iterator` or | ||
| * `std::regex_token_iterator`. Their constructors take a range and a regex. | ||
| */ | ||
| private class StdRegexIterator extends Class { | ||
| StdRegexIterator() { | ||
| this.(ClassTemplateInstantiation) | ||
| .getTemplate() | ||
| .hasQualifiedName("std", ["regex_iterator", "regex_token_iterator"]) | ||
| or | ||
| this.hasQualifiedName("std", ["regex_iterator", "regex_token_iterator"]) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Holds if `call` is a call site that matches a subject against a regex, | ||
| * where `regexArg` is the argument holding the regex object and `subjectArg` | ||
| * (if it exists) is the argument holding the subject string. | ||
| */ | ||
| predicate regexMatchCall(Call call, Expr regexArg, Expr subjectArg) { | ||
| exists(Function f, Parameter rp | | ||
| f = call.getTarget() and | ||
| ( | ||
| f instanceof StdRegexMatchFunction | ||
| or | ||
| // Iterator constructors. | ||
| f.(Constructor).getDeclaringType() instanceof StdRegexIterator | ||
| ) and | ||
| rp = getRegexObjectParameter(f) and | ||
| regexArg = call.getArgument(rp.getIndex()) | ||
| | | ||
| // First string-like parameter, if any, is the subject. | ||
| exists(Parameter sp | | ||
| sp = getStringLikeParameter(f) and | ||
| subjectArg = call.getArgument(sp.getIndex()) and | ||
| // Prefer the earliest such parameter (matches the standard argument | ||
| // order for these overloads). | ||
| not exists(Parameter sp2 | sp2 = getStringLikeParameter(f) and sp2.getIndex() < sp.getIndex()) | ||
| ) | ||
| ) | ||
| } | ||
|
|
||
| // --------------------------------------------------------------------------- | ||
| // Regex-flow sinks: places where a pattern (a StringLiteral) is used as a regex | ||
| // --------------------------------------------------------------------------- | ||
| /** | ||
| * A regex-flow sink: an expression at which a value is used as the pattern | ||
| * for a `std::basic_regex` (construction, `.assign(...)`), or as the pattern | ||
| * argument of a free match/search/replace call that takes a raw pattern. | ||
| */ | ||
| class RegexPatternSink extends DataFlow::Node { | ||
| RegexPatternSink() { | ||
| // 1. Constructor argument 0 of std::basic_regex. | ||
| exists(ConstructorCall cc, Constructor c | | ||
| c = cc.getTarget() and | ||
| c.getDeclaringType() instanceof StdBasicRegex and | ||
| this.asExpr() = cc.getArgument(0) | ||
| ) | ||
| or | ||
| // 2. Argument 0 of a `basic_regex::assign(...)` call. | ||
| exists(FunctionCall fc, MemberFunction m | | ||
| m = fc.getTarget() and | ||
| m.getDeclaringType() instanceof StdBasicRegex and | ||
| m.hasName("assign") and | ||
| this.asExpr() = fc.getArgument(0) | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| // --------------------------------------------------------------------------- | ||
| // Fast-path: only track literals that look regex-y. | ||
| // --------------------------------------------------------------------------- | ||
| /** | ||
| * 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 | ||
| * pattern construction/assignment site. | ||
| */ | ||
| private module RegexPatternFlowConfig implements DataFlow::ConfigSig { | ||
| predicate isSource(DataFlow::Node node) { node.asExpr() instanceof ExploitableStringLiteral } | ||
|
|
||
| predicate isSink(DataFlow::Node node) { node instanceof RegexPatternSink } | ||
| } | ||
|
|
||
| private module RegexPatternFlow = TaintTracking::Global<RegexPatternFlowConfig>; | ||
|
|
||
| // --------------------------------------------------------------------------- | ||
| // Public predicates | ||
| // --------------------------------------------------------------------------- | ||
| /** | ||
| * Holds if the `StringLiteral` `regex` flows to a modeled `std::regex` | ||
| * construction or usage site. | ||
| * | ||
| * As an optimisation, only regexes containing an unbounded-repetition | ||
| * quantifier (`+`, `*`, or `{n,}`) are considered. | ||
| */ | ||
| predicate usedAsRegex(StringLiteral regex) { | ||
| regex instanceof ExploitableStringLiteral and | ||
| RegexPatternFlow::flowFromExpr(regex) | ||
| } | ||
|
|
||
| /** | ||
| * Holds if `regex` is a string literal used as a regular expression that is | ||
| * matched against the expression `str`. | ||
| * | ||
| * As an optimisation, only regexes containing an unbounded-repetition | ||
| * quantifier (`+`, `*`, or `{n,}`) are considered. | ||
| */ | ||
| predicate regexMatchedAgainst(StringLiteral regex, Expr str) { | ||
| exists(RegexPatternSink patternSink, Variable v | | ||
| RegexPatternFlow::flow(DataFlow::exprNode(regex), patternSink) and | ||
| // Recover the variable that is being constructed / assigned to. | ||
| ( | ||
| exists(ConstructorCall cc | | ||
| patternSink.asExpr() = cc.getArgument(0) and | ||
| cc.getEnclosingElement() = v.getInitializer() | ||
| ) | ||
| or | ||
| exists(FunctionCall fc | | ||
| patternSink.asExpr() = fc.getArgument(0) and | ||
| fc.getQualifier() = v.getAnAccess() | ||
| ) | ||
| ) and | ||
| // The regex variable is used as the regex argument to a match call. | ||
| exists(Expr regexArg | | ||
| regexMatchCall(_, regexArg, str) and | ||
| regexArg = v.getAnAccess() | ||
| ) | ||
| ) | ||
| or | ||
| // Also handle the pattern being passed inline to a match call (no named | ||
| // variable): rare in practice for std::regex, but supported for | ||
| // completeness. | ||
| exists(Expr regexArg | | ||
| regexMatchCall(_, regexArg, str) and | ||
| // The regex argument is a temporary `basic_regex(pattern)`. | ||
| exists(ConstructorCall cc | | ||
| cc.getTarget().getDeclaringType() instanceof StdBasicRegex and | ||
| cc.getParent*() = regexArg and | ||
| cc.getArgument(0) = regex.getFullyConverted() | ||
| or | ||
| cc.getTarget().getDeclaringType() instanceof StdBasicRegex and | ||
| cc.getParent*() = regexArg and | ||
| cc.getArgument(0) = regex | ||
| ) | ||
| ) | ||
| } | ||
|
|
||
| // --------------------------------------------------------------------------- | ||
| // ReDoS engine gating | ||
| // --------------------------------------------------------------------------- | ||
| /** | ||
| * Holds if `regex` is used with a `std::regex` matching engine that performs | ||
| * backtracking, so that super-linear-backtracking ReDoS is possible. | ||
| * | ||
| * `std::regex` backtracks for the ECMAScript, `basic` (BRE), and `extended` | ||
| * (ERE) grammars. The POSIX tool-style grammars `awk`, `grep`, and `egrep` | ||
| * are treated as linear-time (non-backtracking) matching semantics for the | ||
| * purposes of the ReDoS queries and are excluded here. | ||
| * | ||
| * Grammar selection and ReDoS-eligibility are independent axes: two flags | ||
| * can select the same grammar yet differ in ReDoS-eligibility (for example | ||
| * `extended` and `egrep` both parse as ERE, but only `extended` is | ||
| * backtracking-eligible). | ||
| */ | ||
| predicate isBacktrackingEngine(StringLiteral regex) { | ||
| usedAsRegex(regex) and | ||
| not RG::hasNonBacktrackingGrammarFlag(regex) | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.