diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-1333-ReDoS/ReDoS.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-1333-ReDoS/ReDoS.qlref index 945149dec70..7f6d41509c4 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-1333-ReDoS/ReDoS.qlref +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-1333-ReDoS/ReDoS.qlref @@ -1 +1,2 @@ query: Security/CWE/CWE-1333/ReDoS.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-1333-ReDoS/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-1333-ReDoS/test.cpp index 92debfdd28d..de849f27e1b 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-1333-ReDoS/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-1333-ReDoS/test.cpp @@ -71,58 +71,58 @@ int main(int argc, char** argv) { // ------------------------------------------------------------------------- // BAD: (a*)*b - { std::regex re("(a*)*b"); run(re, input); } + { std::regex re("(a*)*b"); run(re, input); } // $ Alert // BAD: (a+)*b - { std::regex re("(a+)*b"); run(re, input); } + { std::regex re("(a+)*b"); run(re, input); } // $ Alert // BAD: (a*)+b - { std::regex re("(a*)+b"); run(re, input); } + { std::regex re("(a*)+b"); run(re, input); } // $ Alert // BAD: (a+)+b - { std::regex re("(a+)+b"); run(re, input); } + { std::regex re("(a+)+b"); run(re, input); } // $ Alert // BAD: (a*)+ - { std::regex re("^(a*)+$"); run(re, input); } + { std::regex re("^(a*)+$"); run(re, input); } // $ Alert // BAD: ((a+a?)*)+b+ (Ruby bad48) - { std::regex re("(((a+a?)*)+b+)"); run(re, input); } + { std::regex re("(((a+a?)*)+b+)"); run(re, input); } // $ Alert // BAD: (a?a?)*b (Ruby bad71) - { std::regex re("(a?a?)*b"); run(re, input); } + { std::regex re("(a?a?)*b"); run(re, input); } // $ Alert // BAD: (a?)+b via non-capturing alt (Ruby bad73) - { std::regex re("(?:a|a?)+b"); run(re, input); } + { std::regex re("(?:a|a?)+b"); run(re, input); } // $ Alert // BAD: foo([\w-]*)+bar (Ruby bad69) - { std::regex re("foo([\\w-]*)+bar"); run(re, input); } + { std::regex re("foo([\\w-]*)+bar"); run(re, input); } // $ Alert // BAD: ((ab)*)+c (Ruby bad70) - { std::regex re("((ab)*)+c"); run(re, input); } + { std::regex re("((ab)*)+c"); run(re, input); } // $ Alert // ------------------------------------------------------------------------- // 2. Anchored nested-quantifier patterns (Ruby bad7..bad10, bad77, bad78). // ------------------------------------------------------------------------- // BAD: ^([a-z]+)+$ - { std::regex re("^([a-z]+)+$"); run(re, input); } + { std::regex re("^([a-z]+)+$"); run(re, input); } // $ Alert // BAD: ^([a-z]*)*$ - { std::regex re("^([a-z]*)*$"); run(re, input); } + { std::regex re("^([a-z]*)*$"); run(re, input); } // $ Alert // BAD: e-mail-like regex - { std::regex re("^([a-zA-Z0-9])(([\\.-]|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})|([a-z]{2,3}[.]{1}[a-z]{2,3}))$"); + { std::regex re("^([a-zA-Z0-9])(([\\.-]|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})|([a-z]{2,3}[.]{1}[a-z]{2,3}))$"); // $ Alert run(re, input); } // BAD: ^(([a-z])+.)+[A-Z]([a-z])+$ - { std::regex re("^(([a-z])+.)+[A-Z]([a-z])+$"); run(re, input); } + { std::regex re("^(([a-z])+.)+[A-Z]([a-z])+$"); run(re, input); } // $ Alert // BAD: ^((a)+\w)+$ - { std::regex re("^((a)+\\w)+$"); run(re, input); } + { std::regex re("^((a)+\\w)+$"); run(re, input); } // $ Alert // BAD: ^(b+.)+$ - { std::regex re("^(b+.)+$"); run(re, input); } + { std::regex re("^(b+.)+$"); run(re, input); } // $ Alert // BAD: ^ab(c+)+$ (Ruby bad66) - { std::regex re("^ab(c+)+$"); run(re, input); } + { std::regex re("^ab(c+)+$"); run(re, input); } // $ Alert // ------------------------------------------------------------------------- // 3. Alternations with overlapping / identical branches inside a repetition. // ------------------------------------------------------------------------- // BAD: (a|a)* - { std::regex re("^(a|a)*$"); run(re, input); } + { std::regex re("^(a|a)*$"); run(re, input); } // $ Alert // BAD: (a|aa?)*b (Ruby bad15) - { std::regex re("(a|aa?)*b"); run(re, input); } + { std::regex re("(a|aa?)*b"); run(re, input); } // $ Alert // BAD: (b|a?b)*c (Ruby bad13) - { std::regex re("(b|a?b)*c"); run(re, input); } + { std::regex re("(b|a?b)*c"); run(re, input); } // $ Alert // BAD: (a+|b+|c+)*c (Ruby bad47) - { std::regex re("(a+|b+|c+)*c"); run(re, input); } + { std::regex re("(a+|b+|c+)*c"); run(re, input); } // $ Alert // ------------------------------------------------------------------------- // 4. Character-class / complement ambiguity (Ruby bad52, bad53, bad18, @@ -130,19 +130,19 @@ int main(int argc, char** argv) { // ------------------------------------------------------------------------- // BAD: ([^X]+)*$ - { std::regex re("([^X]+)*$"); run(re, input); } + { std::regex re("([^X]+)*$"); run(re, input); } // $ Alert // BAD: (([^X]b)+)*$ - { std::regex re("(([^X]b)+)*$"); run(re, input); } + { std::regex re("(([^X]b)+)*$"); run(re, input); } // $ Alert // BAD: (([\S\s]|[^a])*)" - { std::regex re("(([\\S\\s]|[^a])*)\""); run(re, input); } + { std::regex re("(([\\S\\s]|[^a])*)\""); run(re, input); } // $ Alert // BAD: ((.|[^a])*)" - { std::regex re("((.|[^a])*)\""); run(re, input); } + { std::regex re("((.|[^a])*)\""); run(re, input); } // $ Alert // BAD: ((b|[^a])*)" - { std::regex re("((b|[^a])*)\""); run(re, input); } + { std::regex re("((b|[^a])*)\""); run(re, input); } // $ Alert // BAD: (([0-9]|[^a])*)" - { std::regex re("(([0-9]|[^a])*)\""); run(re, input); } + { std::regex re("(([0-9]|[^a])*)\""); run(re, input); } // $ Alert // BAD: ^([^>a]+)*(>|$) - { std::regex re("^([^>a]+)*(>|$)"); run(re, input); } + { std::regex re("^([^>a]+)*(>|$)"); run(re, input); } // $ Alert // ------------------------------------------------------------------------- // 5. Anchored-vs-unanchored GOOD/BAD pairs (Ruby good16/bad50, @@ -154,17 +154,17 @@ int main(int argc, char** argv) { // GOOD: (a+)+aaaaa*a+ -- Ruby good16 (no rejecting suffix) { std::regex re("(a+)+aaaaa*a+"); run(re, input); } // BAD: (a+)+aaaaa$ -- Ruby bad50 - { std::regex re("(a+)+aaaaa$"); run(re, input); } + { std::regex re("(a+)+aaaaa$"); run(re, input); } // $ Alert // GOOD: (\n+)+\n\n -- Ruby good17 { std::regex re("(\\n+)+\\n\\n"); run(re, input); } // BAD: (\n+)+\n\n$ -- Ruby bad51 - { std::regex re("(\\n+)+\\n\\n$"); run(re, input); } + { std::regex re("(\\n+)+\\n\\n$"); run(re, input); } // $ Alert // GOOD: (([^X]b)+)*($|[^X]b) -- Ruby good18 { std::regex re("(([^X]b)+)*($|[^X]b)"); run(re, input); } // BAD: (([^X]b)+)*($|[^X]c) -- Ruby bad54 - { std::regex re("(([^X]b)+)*($|[^X]c)"); run(re, input); } + { std::regex re("(([^X]b)+)*($|[^X]c)"); run(re, input); } // $ Alert // GOOD: ((ab)+)*ababab -- Ruby good20 { std::regex re("((ab)+)*ababab"); run(re, input); } @@ -173,7 +173,7 @@ int main(int argc, char** argv) { // GOOD: ((ab)+)* -- Ruby good22 { std::regex re("((ab)+)*"); run(re, input); } // BAD: ((ab)+)*$ -- Ruby bad55 - { std::regex re("((ab)+)*$"); run(re, input); } + { std::regex re("((ab)+)*$"); run(re, input); } // $ Alert // ------------------------------------------------------------------------- // 6. GOOD patterns that specifically guard against false positives. @@ -226,7 +226,7 @@ int main(int argc, char** argv) { // ------------------------------------------------------------------------- // BAD: exponential regex with icase - case-insensitivity does not suppress the alert. - { std::regex re("^([a-z]+)+$", std::regex_constants::icase); run(re, input); } + { std::regex re("^([a-z]+)+$", std::regex_constants::icase); run(re, input); } // $ Alert // BAD: BRE grammar (`basic`) is modeled by the parser (`BreRegExp`, // Phase D) and is backtracking-eligible, so the nested-quantifier is // reported as exponential. Note that `(...)` in BRE is *literal* - the @@ -238,7 +238,7 @@ int main(int argc, char** argv) { // BAD: extended selects the ERE grammar (modeled by `EreRegExp` since // Phase C) and is backtracking-eligible, so the shared engine reports // the nested-quantifier as exponential - same as the ECMAScript case. - { std::regex re("^(a+)+$", std::regex_constants::extended); run(re, input); } + { std::regex re("^(a+)+$", std::regex_constants::extended); run(re, input); } // $ Alert // GOOD: awk parses as ERE (same as `extended`) but is excluded from the // ReDoS queries by `isBacktrackingEngine` (POSIX tool-style engines are // treated as linear-time), *not* by any parsing/grammar difference. @@ -262,9 +262,9 @@ int main(int argc, char** argv) { // ------------------------------------------------------------------------- // BAD: nested-quantifier exponential regex using a POSIX character class. - { std::regex re("^([[:alpha:]]+)+$"); run(re, input); } + { std::regex re("^([[:alpha:]]+)+$"); run(re, input); } // $ Alert // BAD: same pattern under icase - case-folding must not suppress the alert. - { std::regex re("^([[:alpha:]]+)+$", std::regex_constants::icase); run(re, input); } + { std::regex re("^([[:alpha:]]+)+$", std::regex_constants::icase); run(re, input); } // $ Alert // Opaque POSIX character class: `[:punct:]` has no clean `\d`/`\s`/`\w` // equivalent (see the discussion in RegexTreeView.qll::isEscapeClass). // It is therefore left as an opaque atom in the shared engine's view; @@ -328,7 +328,7 @@ int main(int argc, char** argv) { // ------------------------------------------------------------------------- // BAD: ERE grammar via `extended` - parsed as ERE and backtracking-eligible. - { std::regex re("^([a-z]+)+$", std::regex_constants::extended); run(re, input); } + { std::regex re("^([a-z]+)+$", std::regex_constants::extended); run(re, input); } // $ Alert // GOOD: same pattern under `egrep` - parses identically as ERE but // excluded by `isBacktrackingEngine`. { std::regex re("^([a-z]+)+$", std::regex_constants::egrep); run(re, input); } @@ -372,7 +372,7 @@ int main(int argc, char** argv) { // exponentially many ways). BRE has no `+`, so this is the BRE // analogue of the ECMAScript `^(a*)*$` (or the more familiar // `^(a+)+$`) shape. - { std::regex re("^\\(a*\\)*$", std::regex_constants::basic); run(re, input); } + { std::regex re("^\\(a*\\)*$", std::regex_constants::basic); run(re, input); } // $ Alert // GOOD: same pattern under `grep` - parses identically as BRE but // excluded by `isBacktrackingEngine`. { std::regex re("^\\(a*\\)*$", std::regex_constants::grep); run(re, input); } diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-1333/PolynomialReDoS.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-1333/PolynomialReDoS.qlref index 52aff65d1b3..6b9a90569e6 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-1333/PolynomialReDoS.qlref +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-1333/PolynomialReDoS.qlref @@ -1 +1,2 @@ query: Security/CWE/CWE-1333/PolynomialReDoS.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-1333/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-1333/test.cpp index 68a4a1c76fc..bd4b7564aa1 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-1333/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-1333/test.cpp @@ -104,7 +104,7 @@ struct Widget { // ----------------------------------------------------------------------------- // argv is a LocalFlowSource (and hence a FlowSource). -int main(int argc, char** argv) { +int main(int argc, char** argv) { // $ Source std::string input(argv[1]); // ------------------------------------------------------------------------- @@ -114,7 +114,7 @@ int main(int argc, char** argv) { // BAD: polynomial-backtracking pattern applied to user input. { std::regex re("^\\s+|\\s+$"); - std::regex_replace(input, re, std::string("")); + std::regex_replace(input, re, std::string("")); // $ Alert } // Note: passing `argv[1]` (a raw `char*`) inline to `regex_search` @@ -129,7 +129,7 @@ int main(int argc, char** argv) { // BAD: quadratic \\d+E?\\d+ pattern applied to user input. { std::regex re("^0\\.\\d+E?\\d+$"); - std::regex_search(input, re); + std::regex_search(input, re); // $ Alert } // GOOD: pattern is linear (a fixed prefix, no ambiguous repetition). @@ -166,7 +166,7 @@ int main(int argc, char** argv) { // BAD: \s+X? repeated - quadratic on strings of whitespace. { std::regex re("\\s+X?\\s+$"); - std::regex_search(input, re); + std::regex_search(input, re); // $ Alert } // ------------------------------------------------------------------------- @@ -176,20 +176,20 @@ int main(int argc, char** argv) { // BAD: std::regex_match with user input. { std::regex re("^\\s+|\\s+$"); - std::regex_match(input, re); + std::regex_match(input, re); // $ Alert } // BAD: std::regex_search with user input. { std::regex re("^\\s+|\\s+$"); - std::regex_search(input, re); + std::regex_search(input, re); // $ Alert } // BAD: std::regex_replace with user input (already covered above; kept for // symmetry with the other APIs). { std::regex re("^\\s+|\\s+$"); - std::regex_replace(input, re, std::string("")); + std::regex_replace(input, re, std::string("")); // $ Alert } // BAD: std::regex_iterator constructed with a superlinear regex - the @@ -276,7 +276,7 @@ int main(int argc, char** argv) { int n = argc; // integer, small fixed size (void)n; std::regex re("^\\s+|\\s+$"); - std::regex_search(input, re); // BAD (already reported above) + std::regex_search(input, re); // $ Alert // BAD (already reported above) } // ------------------------------------------------------------------------- @@ -305,7 +305,7 @@ int main(int argc, char** argv) { // BAD: icase does not suppress the polynomial alert. { std::regex re("^\\s+|\\s+$", std::regex_constants::icase); - std::regex_replace(input, re, std::string("")); + std::regex_replace(input, re, std::string("")); // $ Alert } // GOOD: BRE grammar (`basic`) is now modeled by the parser @@ -325,7 +325,7 @@ int main(int argc, char** argv) { // ECMAScript case. { std::regex re("^\\s+|\\s+$", std::regex_constants::extended); - std::regex_replace(input, re, std::string("")); + std::regex_replace(input, re, std::string("")); // $ Alert } // GOOD: awk parses as ERE (same as `extended`) but is excluded from // the ReDoS queries by `isBacktrackingEngine`, *not* by any @@ -357,14 +357,14 @@ int main(int argc, char** argv) { // digit-heavy input (Java polynomial-ReDoS test `Test.java`). { std::regex re("(\\d+)*$"); - std::regex_search(input, re); + std::regex_search(input, re); // $ Alert } // BAD: .*.*=.* - classic polynomial pattern, cf. JS // `polynomial-redos/tst.js`. { std::regex re(".*.*=.*"); - std::regex_search(input, re); + std::regex_search(input, re); // $ Alert } // GOOD (observed): `^(\w+\s?)*$` - a "trim-and-split" style pattern that @@ -384,10 +384,10 @@ int main(int argc, char** argv) { // BAD: `std::getenv` is modeled as a LocalFlowSource. { - const char* env = std::getenv("USER_REGEX_INPUT"); + const char* env = std::getenv("USER_REGEX_INPUT"); // $ Source std::string s(env); std::regex re("^\\s+|\\s+$"); - std::regex_replace(s, re, std::string("")); + std::regex_replace(s, re, std::string("")); // $ Alert } // BAD: `std::fread` is modeled as a RemoteFlowSource (bytes read from a @@ -395,10 +395,10 @@ int main(int argc, char** argv) { // `FlowSource` union used by `PolynomialRedosConfig::isSource`. { char buf[256]; - std::fread(buf, 1, sizeof(buf), (std::FILE*)0); + std::fread(buf, 1, sizeof(buf), (std::FILE*)0); // $ Source std::string s(buf); std::regex re("^\\s+|\\s+$"); - std::regex_replace(s, re, std::string("")); + std::regex_replace(s, re, std::string("")); // $ Alert } // ------------------------------------------------------------------------- @@ -436,7 +436,7 @@ int main(int argc, char** argv) { // backtracking term on user input. { std::regex re("(a+)+b"); - std::regex_match(input, re); + std::regex_match(input, re); // $ Alert } // ------------------------------------------------------------------------- @@ -499,7 +499,7 @@ int main(int argc, char** argv) { // BAD: ERE grammar via `extended` - parsed as ERE and backtracking-eligible. { std::regex re("^\\s+|\\s+$", std::regex_constants::extended); - std::regex_replace(input, re, std::string("")); + std::regex_replace(input, re, std::string("")); // $ Alert } // GOOD: same pattern/flow under `egrep` - parses identically as ERE // but excluded by `isBacktrackingEngine`. @@ -547,7 +547,7 @@ int main(int argc, char** argv) { { std::regex re("[[:space:]]\\{1,\\}.*[[:space:]]\\{1,\\}$", std::regex_constants::basic); - std::regex_replace(input, re, std::string("")); + std::regex_replace(input, re, std::string("")); // $ Alert } // GOOD: same pattern/flow under `grep` - parses identically as BRE // but excluded by `isBacktrackingEngine`.