Skip to content

Commit 4be7ff0

Browse files
authored
Convert ReDoS query tests to inline test expectations
1 parent 09938f0 commit 4be7ff0

4 files changed

Lines changed: 59 additions & 57 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
query: Security/CWE/CWE-1333/ReDoS.ql
2+
postprocess: utils/test/InlineExpectationsTestQuery.ql

cpp/ql/test/query-tests/Security/CWE/CWE-1333-ReDoS/test.cpp

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -71,78 +71,78 @@ int main(int argc, char** argv) {
7171
// -------------------------------------------------------------------------
7272

7373
// BAD: (a*)*b
74-
{ std::regex re("(a*)*b"); run(re, input); }
74+
{ std::regex re("(a*)*b"); run(re, input); } // $ Alert
7575
// BAD: (a+)*b
76-
{ std::regex re("(a+)*b"); run(re, input); }
76+
{ std::regex re("(a+)*b"); run(re, input); } // $ Alert
7777
// BAD: (a*)+b
78-
{ std::regex re("(a*)+b"); run(re, input); }
78+
{ std::regex re("(a*)+b"); run(re, input); } // $ Alert
7979
// BAD: (a+)+b
80-
{ std::regex re("(a+)+b"); run(re, input); }
80+
{ std::regex re("(a+)+b"); run(re, input); } // $ Alert
8181
// BAD: (a*)+
82-
{ std::regex re("^(a*)+$"); run(re, input); }
82+
{ std::regex re("^(a*)+$"); run(re, input); } // $ Alert
8383
// BAD: ((a+a?)*)+b+ (Ruby bad48)
84-
{ std::regex re("(((a+a?)*)+b+)"); run(re, input); }
84+
{ std::regex re("(((a+a?)*)+b+)"); run(re, input); } // $ Alert
8585
// BAD: (a?a?)*b (Ruby bad71)
86-
{ std::regex re("(a?a?)*b"); run(re, input); }
86+
{ std::regex re("(a?a?)*b"); run(re, input); } // $ Alert
8787
// BAD: (a?)+b via non-capturing alt (Ruby bad73)
88-
{ std::regex re("(?:a|a?)+b"); run(re, input); }
88+
{ std::regex re("(?:a|a?)+b"); run(re, input); } // $ Alert
8989
// BAD: foo([\w-]*)+bar (Ruby bad69)
90-
{ std::regex re("foo([\\w-]*)+bar"); run(re, input); }
90+
{ std::regex re("foo([\\w-]*)+bar"); run(re, input); } // $ Alert
9191
// BAD: ((ab)*)+c (Ruby bad70)
92-
{ std::regex re("((ab)*)+c"); run(re, input); }
92+
{ std::regex re("((ab)*)+c"); run(re, input); } // $ Alert
9393

9494
// -------------------------------------------------------------------------
9595
// 2. Anchored nested-quantifier patterns (Ruby bad7..bad10, bad77, bad78).
9696
// -------------------------------------------------------------------------
9797

9898
// BAD: ^([a-z]+)+$
99-
{ std::regex re("^([a-z]+)+$"); run(re, input); }
99+
{ std::regex re("^([a-z]+)+$"); run(re, input); } // $ Alert
100100
// BAD: ^([a-z]*)*$
101-
{ std::regex re("^([a-z]*)*$"); run(re, input); }
101+
{ std::regex re("^([a-z]*)*$"); run(re, input); } // $ Alert
102102
// BAD: e-mail-like regex
103-
{ 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}))$");
103+
{ 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
104104
run(re, input); }
105105
// BAD: ^(([a-z])+.)+[A-Z]([a-z])+$
106-
{ std::regex re("^(([a-z])+.)+[A-Z]([a-z])+$"); run(re, input); }
106+
{ std::regex re("^(([a-z])+.)+[A-Z]([a-z])+$"); run(re, input); } // $ Alert
107107
// BAD: ^((a)+\w)+$
108-
{ std::regex re("^((a)+\\w)+$"); run(re, input); }
108+
{ std::regex re("^((a)+\\w)+$"); run(re, input); } // $ Alert
109109
// BAD: ^(b+.)+$
110-
{ std::regex re("^(b+.)+$"); run(re, input); }
110+
{ std::regex re("^(b+.)+$"); run(re, input); } // $ Alert
111111
// BAD: ^ab(c+)+$ (Ruby bad66)
112-
{ std::regex re("^ab(c+)+$"); run(re, input); }
112+
{ std::regex re("^ab(c+)+$"); run(re, input); } // $ Alert
113113

114114
// -------------------------------------------------------------------------
115115
// 3. Alternations with overlapping / identical branches inside a repetition.
116116
// -------------------------------------------------------------------------
117117

118118
// BAD: (a|a)*
119-
{ std::regex re("^(a|a)*$"); run(re, input); }
119+
{ std::regex re("^(a|a)*$"); run(re, input); } // $ Alert
120120
// BAD: (a|aa?)*b (Ruby bad15)
121-
{ std::regex re("(a|aa?)*b"); run(re, input); }
121+
{ std::regex re("(a|aa?)*b"); run(re, input); } // $ Alert
122122
// BAD: (b|a?b)*c (Ruby bad13)
123-
{ std::regex re("(b|a?b)*c"); run(re, input); }
123+
{ std::regex re("(b|a?b)*c"); run(re, input); } // $ Alert
124124
// BAD: (a+|b+|c+)*c (Ruby bad47)
125-
{ std::regex re("(a+|b+|c+)*c"); run(re, input); }
125+
{ std::regex re("(a+|b+|c+)*c"); run(re, input); } // $ Alert
126126

127127
// -------------------------------------------------------------------------
128128
// 4. Character-class / complement ambiguity (Ruby bad52, bad53, bad18,
129129
// bad20, bad21, bad22, bad23, bad43).
130130
// -------------------------------------------------------------------------
131131

132132
// BAD: ([^X]+)*$
133-
{ std::regex re("([^X]+)*$"); run(re, input); }
133+
{ std::regex re("([^X]+)*$"); run(re, input); } // $ Alert
134134
// BAD: (([^X]b)+)*$
135-
{ std::regex re("(([^X]b)+)*$"); run(re, input); }
135+
{ std::regex re("(([^X]b)+)*$"); run(re, input); } // $ Alert
136136
// BAD: (([\S\s]|[^a])*)"
137-
{ std::regex re("(([\\S\\s]|[^a])*)\""); run(re, input); }
137+
{ std::regex re("(([\\S\\s]|[^a])*)\""); run(re, input); } // $ Alert
138138
// BAD: ((.|[^a])*)"
139-
{ std::regex re("((.|[^a])*)\""); run(re, input); }
139+
{ std::regex re("((.|[^a])*)\""); run(re, input); } // $ Alert
140140
// BAD: ((b|[^a])*)"
141-
{ std::regex re("((b|[^a])*)\""); run(re, input); }
141+
{ std::regex re("((b|[^a])*)\""); run(re, input); } // $ Alert
142142
// BAD: (([0-9]|[^a])*)"
143-
{ std::regex re("(([0-9]|[^a])*)\""); run(re, input); }
143+
{ std::regex re("(([0-9]|[^a])*)\""); run(re, input); } // $ Alert
144144
// BAD: ^([^>a]+)*(>|$)
145-
{ std::regex re("^([^>a]+)*(>|$)"); run(re, input); }
145+
{ std::regex re("^([^>a]+)*(>|$)"); run(re, input); } // $ Alert
146146

147147
// -------------------------------------------------------------------------
148148
// 5. Anchored-vs-unanchored GOOD/BAD pairs (Ruby good16/bad50,
@@ -154,17 +154,17 @@ int main(int argc, char** argv) {
154154
// GOOD: (a+)+aaaaa*a+ -- Ruby good16 (no rejecting suffix)
155155
{ std::regex re("(a+)+aaaaa*a+"); run(re, input); }
156156
// BAD: (a+)+aaaaa$ -- Ruby bad50
157-
{ std::regex re("(a+)+aaaaa$"); run(re, input); }
157+
{ std::regex re("(a+)+aaaaa$"); run(re, input); } // $ Alert
158158

159159
// GOOD: (\n+)+\n\n -- Ruby good17
160160
{ std::regex re("(\\n+)+\\n\\n"); run(re, input); }
161161
// BAD: (\n+)+\n\n$ -- Ruby bad51
162-
{ std::regex re("(\\n+)+\\n\\n$"); run(re, input); }
162+
{ std::regex re("(\\n+)+\\n\\n$"); run(re, input); } // $ Alert
163163

164164
// GOOD: (([^X]b)+)*($|[^X]b) -- Ruby good18
165165
{ std::regex re("(([^X]b)+)*($|[^X]b)"); run(re, input); }
166166
// BAD: (([^X]b)+)*($|[^X]c) -- Ruby bad54
167-
{ std::regex re("(([^X]b)+)*($|[^X]c)"); run(re, input); }
167+
{ std::regex re("(([^X]b)+)*($|[^X]c)"); run(re, input); } // $ Alert
168168

169169
// GOOD: ((ab)+)*ababab -- Ruby good20
170170
{ std::regex re("((ab)+)*ababab"); run(re, input); }
@@ -173,7 +173,7 @@ int main(int argc, char** argv) {
173173
// GOOD: ((ab)+)* -- Ruby good22
174174
{ std::regex re("((ab)+)*"); run(re, input); }
175175
// BAD: ((ab)+)*$ -- Ruby bad55
176-
{ std::regex re("((ab)+)*$"); run(re, input); }
176+
{ std::regex re("((ab)+)*$"); run(re, input); } // $ Alert
177177

178178
// -------------------------------------------------------------------------
179179
// 6. GOOD patterns that specifically guard against false positives.
@@ -226,7 +226,7 @@ int main(int argc, char** argv) {
226226
// -------------------------------------------------------------------------
227227

228228
// BAD: exponential regex with icase - case-insensitivity does not suppress the alert.
229-
{ std::regex re("^([a-z]+)+$", std::regex_constants::icase); run(re, input); }
229+
{ std::regex re("^([a-z]+)+$", std::regex_constants::icase); run(re, input); } // $ Alert
230230
// BAD: BRE grammar (`basic`) is modeled by the parser (`BreRegExp`,
231231
// Phase D) and is backtracking-eligible, so the nested-quantifier is
232232
// reported as exponential. Note that `(...)` in BRE is *literal* - the
@@ -238,7 +238,7 @@ int main(int argc, char** argv) {
238238
// BAD: extended selects the ERE grammar (modeled by `EreRegExp` since
239239
// Phase C) and is backtracking-eligible, so the shared engine reports
240240
// the nested-quantifier as exponential - same as the ECMAScript case.
241-
{ std::regex re("^(a+)+$", std::regex_constants::extended); run(re, input); }
241+
{ std::regex re("^(a+)+$", std::regex_constants::extended); run(re, input); } // $ Alert
242242
// GOOD: awk parses as ERE (same as `extended`) but is excluded from the
243243
// ReDoS queries by `isBacktrackingEngine` (POSIX tool-style engines are
244244
// treated as linear-time), *not* by any parsing/grammar difference.
@@ -262,9 +262,9 @@ int main(int argc, char** argv) {
262262
// -------------------------------------------------------------------------
263263

264264
// BAD: nested-quantifier exponential regex using a POSIX character class.
265-
{ std::regex re("^([[:alpha:]]+)+$"); run(re, input); }
265+
{ std::regex re("^([[:alpha:]]+)+$"); run(re, input); } // $ Alert
266266
// BAD: same pattern under icase - case-folding must not suppress the alert.
267-
{ std::regex re("^([[:alpha:]]+)+$", std::regex_constants::icase); run(re, input); }
267+
{ std::regex re("^([[:alpha:]]+)+$", std::regex_constants::icase); run(re, input); } // $ Alert
268268
// Opaque POSIX character class: `[:punct:]` has no clean `\d`/`\s`/`\w`
269269
// equivalent (see the discussion in RegexTreeView.qll::isEscapeClass).
270270
// It is therefore left as an opaque atom in the shared engine's view;
@@ -328,7 +328,7 @@ int main(int argc, char** argv) {
328328
// -------------------------------------------------------------------------
329329

330330
// BAD: ERE grammar via `extended` - parsed as ERE and backtracking-eligible.
331-
{ std::regex re("^([a-z]+)+$", std::regex_constants::extended); run(re, input); }
331+
{ std::regex re("^([a-z]+)+$", std::regex_constants::extended); run(re, input); } // $ Alert
332332
// GOOD: same pattern under `egrep` - parses identically as ERE but
333333
// excluded by `isBacktrackingEngine`.
334334
{ std::regex re("^([a-z]+)+$", std::regex_constants::egrep); run(re, input); }
@@ -372,7 +372,7 @@ int main(int argc, char** argv) {
372372
// exponentially many ways). BRE has no `+`, so this is the BRE
373373
// analogue of the ECMAScript `^(a*)*$` (or the more familiar
374374
// `^(a+)+$`) shape.
375-
{ std::regex re("^\\(a*\\)*$", std::regex_constants::basic); run(re, input); }
375+
{ std::regex re("^\\(a*\\)*$", std::regex_constants::basic); run(re, input); } // $ Alert
376376
// GOOD: same pattern under `grep` - parses identically as BRE but
377377
// excluded by `isBacktrackingEngine`.
378378
{ std::regex re("^\\(a*\\)*$", std::regex_constants::grep); run(re, input); }
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
query: Security/CWE/CWE-1333/PolynomialReDoS.ql
2+
postprocess: utils/test/InlineExpectationsTestQuery.ql

cpp/ql/test/query-tests/Security/CWE/CWE-1333/test.cpp

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ struct Widget {
104104
// -----------------------------------------------------------------------------
105105

106106
// argv is a LocalFlowSource (and hence a FlowSource).
107-
int main(int argc, char** argv) {
107+
int main(int argc, char** argv) { // $ Source
108108
std::string input(argv[1]);
109109

110110
// -------------------------------------------------------------------------
@@ -114,7 +114,7 @@ int main(int argc, char** argv) {
114114
// BAD: polynomial-backtracking pattern applied to user input.
115115
{
116116
std::regex re("^\\s+|\\s+$");
117-
std::regex_replace(input, re, std::string(""));
117+
std::regex_replace(input, re, std::string("")); // $ Alert
118118
}
119119

120120
// Note: passing `argv[1]` (a raw `char*`) inline to `regex_search`
@@ -129,7 +129,7 @@ int main(int argc, char** argv) {
129129
// BAD: quadratic \\d+E?\\d+ pattern applied to user input.
130130
{
131131
std::regex re("^0\\.\\d+E?\\d+$");
132-
std::regex_search(input, re);
132+
std::regex_search(input, re); // $ Alert
133133
}
134134

135135
// GOOD: pattern is linear (a fixed prefix, no ambiguous repetition).
@@ -166,7 +166,7 @@ int main(int argc, char** argv) {
166166
// BAD: \s+X? repeated - quadratic on strings of whitespace.
167167
{
168168
std::regex re("\\s+X?\\s+$");
169-
std::regex_search(input, re);
169+
std::regex_search(input, re); // $ Alert
170170
}
171171

172172
// -------------------------------------------------------------------------
@@ -176,20 +176,20 @@ int main(int argc, char** argv) {
176176
// BAD: std::regex_match with user input.
177177
{
178178
std::regex re("^\\s+|\\s+$");
179-
std::regex_match(input, re);
179+
std::regex_match(input, re); // $ Alert
180180
}
181181

182182
// BAD: std::regex_search with user input.
183183
{
184184
std::regex re("^\\s+|\\s+$");
185-
std::regex_search(input, re);
185+
std::regex_search(input, re); // $ Alert
186186
}
187187

188188
// BAD: std::regex_replace with user input (already covered above; kept for
189189
// symmetry with the other APIs).
190190
{
191191
std::regex re("^\\s+|\\s+$");
192-
std::regex_replace(input, re, std::string(""));
192+
std::regex_replace(input, re, std::string("")); // $ Alert
193193
}
194194

195195
// BAD: std::regex_iterator constructed with a superlinear regex - the
@@ -276,7 +276,7 @@ int main(int argc, char** argv) {
276276
int n = argc; // integer, small fixed size
277277
(void)n;
278278
std::regex re("^\\s+|\\s+$");
279-
std::regex_search(input, re); // BAD (already reported above)
279+
std::regex_search(input, re); // $ Alert // BAD (already reported above)
280280
}
281281

282282
// -------------------------------------------------------------------------
@@ -305,7 +305,7 @@ int main(int argc, char** argv) {
305305
// BAD: icase does not suppress the polynomial alert.
306306
{
307307
std::regex re("^\\s+|\\s+$", std::regex_constants::icase);
308-
std::regex_replace(input, re, std::string(""));
308+
std::regex_replace(input, re, std::string("")); // $ Alert
309309
}
310310

311311
// GOOD: BRE grammar (`basic`) is now modeled by the parser
@@ -325,7 +325,7 @@ int main(int argc, char** argv) {
325325
// ECMAScript case.
326326
{
327327
std::regex re("^\\s+|\\s+$", std::regex_constants::extended);
328-
std::regex_replace(input, re, std::string(""));
328+
std::regex_replace(input, re, std::string("")); // $ Alert
329329
}
330330
// GOOD: awk parses as ERE (same as `extended`) but is excluded from
331331
// the ReDoS queries by `isBacktrackingEngine`, *not* by any
@@ -357,14 +357,14 @@ int main(int argc, char** argv) {
357357
// digit-heavy input (Java polynomial-ReDoS test `Test.java`).
358358
{
359359
std::regex re("(\\d+)*$");
360-
std::regex_search(input, re);
360+
std::regex_search(input, re); // $ Alert
361361
}
362362

363363
// BAD: .*.*=.* - classic polynomial pattern, cf. JS
364364
// `polynomial-redos/tst.js`.
365365
{
366366
std::regex re(".*.*=.*");
367-
std::regex_search(input, re);
367+
std::regex_search(input, re); // $ Alert
368368
}
369369

370370
// GOOD (observed): `^(\w+\s?)*$` - a "trim-and-split" style pattern that
@@ -384,21 +384,21 @@ int main(int argc, char** argv) {
384384

385385
// BAD: `std::getenv` is modeled as a LocalFlowSource.
386386
{
387-
const char* env = std::getenv("USER_REGEX_INPUT");
387+
const char* env = std::getenv("USER_REGEX_INPUT"); // $ Source
388388
std::string s(env);
389389
std::regex re("^\\s+|\\s+$");
390-
std::regex_replace(s, re, std::string(""));
390+
std::regex_replace(s, re, std::string("")); // $ Alert
391391
}
392392

393393
// BAD: `std::fread` is modeled as a RemoteFlowSource (bytes read from a
394394
// stream / file). This exercises the RemoteFlowSource half of the
395395
// `FlowSource` union used by `PolynomialRedosConfig::isSource`.
396396
{
397397
char buf[256];
398-
std::fread(buf, 1, sizeof(buf), (std::FILE*)0);
398+
std::fread(buf, 1, sizeof(buf), (std::FILE*)0); // $ Source
399399
std::string s(buf);
400400
std::regex re("^\\s+|\\s+$");
401-
std::regex_replace(s, re, std::string(""));
401+
std::regex_replace(s, re, std::string("")); // $ Alert
402402
}
403403

404404
// -------------------------------------------------------------------------
@@ -436,7 +436,7 @@ int main(int argc, char** argv) {
436436
// backtracking term on user input.
437437
{
438438
std::regex re("(a+)+b");
439-
std::regex_match(input, re);
439+
std::regex_match(input, re); // $ Alert
440440
}
441441

442442
// -------------------------------------------------------------------------
@@ -499,7 +499,7 @@ int main(int argc, char** argv) {
499499
// BAD: ERE grammar via `extended` - parsed as ERE and backtracking-eligible.
500500
{
501501
std::regex re("^\\s+|\\s+$", std::regex_constants::extended);
502-
std::regex_replace(input, re, std::string(""));
502+
std::regex_replace(input, re, std::string("")); // $ Alert
503503
}
504504
// GOOD: same pattern/flow under `egrep` - parses identically as ERE
505505
// but excluded by `isBacktrackingEngine`.
@@ -547,7 +547,7 @@ int main(int argc, char** argv) {
547547
{
548548
std::regex re("[[:space:]]\\{1,\\}.*[[:space:]]\\{1,\\}$",
549549
std::regex_constants::basic);
550-
std::regex_replace(input, re, std::string(""));
550+
std::regex_replace(input, re, std::string("")); // $ Alert
551551
}
552552
// GOOD: same pattern/flow under `grep` - parses identically as BRE
553553
// but excluded by `isBacktrackingEngine`.

0 commit comments

Comments
 (0)