Skip to content

Commit 49d47a3

Browse files
authored
Merge pull request github#14209 from geoffw0/regexport
Swift: Port regex mode flag fix from Python to Swift
2 parents e6d832c + eb78d40 commit 49d47a3

File tree

10 files changed

+96
-57
lines changed

10 files changed

+96
-57
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: fix
3+
---
4+
* The regular expressions library no longer incorrectly matches mode flag characters against the input.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* The regular expressions library now accepts a wider range of mode flags in a regular expression mode flag group (such as `(?u)`). The `(?w`) flag has been renamed from "UNICODE" to "UNICODEBOUNDARY", and the `(?u)` flag is called "UNICODE" in the libraries.

swift/ql/lib/codeql/swift/regex/Regex.qll

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ private newtype TRegexParseMode =
120120
MkVerbose() or // ignores whitespace and `#` comments within patterns
121121
MkDotAll() or // dot matches all characters, including line terminators
122122
MkMultiLine() or // `^` and `$` also match beginning and end of lines
123-
MkUnicode() // Unicode UAX 29 word boundary mode
123+
MkUnicodeBoundary() or // Unicode UAX 29 word boundary mode
124+
MkUnicode() // Unicode matching
124125

125126
/**
126127
* A regular expression parse mode flag.
@@ -138,6 +139,8 @@ class RegexParseMode extends TRegexParseMode {
138139
or
139140
this = MkMultiLine() and result = "MULTILINE"
140141
or
142+
this = MkUnicodeBoundary() and result = "UNICODEBOUNDARY"
143+
or
141144
this = MkUnicode() and result = "UNICODE"
142145
}
143146

@@ -249,7 +252,7 @@ class NSRegularExpressionRegexAdditionalFlowStep extends RegexAdditionalFlowStep
249252
.getMember()
250253
.(FieldDecl)
251254
.hasQualifiedName("NSRegularExpression.Options", "useUnicodeWordBoundaries") and
252-
mode = MkUnicode() and
255+
mode = MkUnicodeBoundary() and
253256
isSet = true
254257
}
255258
}

swift/ql/lib/codeql/swift/regex/internal/ParseRegex.qll

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
/**
22
* Library for parsing Swift regular expressions.
33
*
4+
* See https://developer.apple.com/documentation/foundation/nsregularexpression
5+
* for the regular expression syntax we aim to support.
6+
*
47
* N.B. does not yet handle stripping whitespace and comments in regexes with
58
* the `x` (free-spacing) flag.
69
*/
@@ -9,6 +12,17 @@ import swift
912
private import RegexTracking
1013
private import codeql.swift.regex.Regex
1114

15+
/**
16+
* A mode character that can be used in a regular expression.
17+
* ```
18+
* NSRegularExpression accepts: dim suwxDPSUW
19+
* Regex accepts: imns x
20+
* ```
21+
*/
22+
private predicate availableRegexModeCharacter(string char) {
23+
char = ["d", "i", "m", "n", "s", "u", "w", "x", "D", "P", "S", "U", "W"]
24+
}
25+
1226
/**
1327
* A `Expr` containing a regular expression term, that is, either
1428
* a regular expression literal, or a string literal used in a context where
@@ -277,35 +291,54 @@ abstract class RegExp extends Expr {
277291
private predicate isGroupStart(int i) { this.nonEscapedCharAt(i) = "(" and not this.inCharSet(i) }
278292

279293
/**
280-
* Holds if a parse mode starts between `start` and `end`.
294+
* Holds if the initial part of a parse mode, not containing any
295+
* mode characters is between `start` and `end`.
281296
*/
282-
private predicate flagGroupStart(int start, int end) {
297+
private predicate flagGroupStartNoModes(int start, int end) {
283298
this.isGroupStart(start) and
284299
this.getChar(start + 1) = "?" and
285-
this.getChar(start + 2) in ["i", "x", "s", "m", "w"] and
300+
availableRegexModeCharacter(this.getChar(start + 2)) and
286301
end = start + 2
287302
}
288303

289304
/**
290-
* Holds if a parse mode group is between `start` and `end`, and includes the
291-
* mode flag `c`. For example the following span, with mode flag `i`:
305+
* Holds if `pos` contains a mode character from the
306+
* flag group starting at `start`.
307+
*/
308+
private predicate modeCharacter(int start, int pos) {
309+
this.flagGroupStartNoModes(start, pos)
310+
or
311+
this.modeCharacter(start, pos - 1) and
312+
availableRegexModeCharacter(this.getChar(pos))
313+
}
314+
315+
/**
316+
* Holds if a parse mode group is between `start` and `end`.
317+
*/
318+
private predicate flagGroupStart(int start, int end) {
319+
this.flagGroupStartNoModes(start, _) and
320+
end = max(int i | this.modeCharacter(start, i) | i + 1)
321+
}
322+
323+
/**
324+
* Holds if a parse mode group of this regex includes the mode flag `c`.
325+
* For example the following parse mode group, with mode flag `"i"`:
292326
* ```
293327
* (?i)
294328
* ```
295329
*/
296-
private predicate flagGroup(int start, int end, string c) {
297-
exists(int inStart, int inEnd |
298-
this.flagGroupStart(start, inStart) and
299-
this.groupContents(start, end, inStart, inEnd) and
300-
this.getChar([inStart .. inEnd - 1]) = c
330+
private predicate flag(string c) {
331+
exists(int pos |
332+
this.modeCharacter(_, pos) and
333+
this.getChar(pos) = c
301334
)
302335
}
303336

304337
/**
305338
* Gets a mode of this regular expression string if it is defined by a mode prefix.
306339
*/
307340
string getModeFromPrefix() {
308-
exists(string c | this.flagGroup(_, _, c) |
341+
exists(string c | this.flag(c) |
309342
c = "i" and result = "IGNORECASE" // case insensitive
310343
or
311344
c = "x" and result = "VERBOSE" // ignores whitespace and `#` comments within patterns
@@ -314,7 +347,10 @@ abstract class RegExp extends Expr {
314347
or
315348
c = "m" and result = "MULTILINE" // `^` and `$` also match beginning and end of lines
316349
or
317-
c = "w" and result = "UNICODE" // Unicode UAX 29 word boundary mode
350+
c = "w" and result = "UNICODEBOUNDARY" // Unicode UAX 29 word boundary mode
351+
or
352+
c = "u" and result = "UNICODE" // Unicode matching
353+
// (other flags exist that are not translated here)
318354
)
319355
}
320356

@@ -325,6 +361,7 @@ abstract class RegExp extends Expr {
325361
* VERBOSE
326362
* DOTALL
327363
* MULTILINE
364+
* UNICODEBOUNDARY
328365
* UNICODE
329366
*/
330367
string getAMode() {

swift/ql/test/library-tests/regex/parse.expected

Lines changed: 15 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1618,16 +1618,13 @@ redos_variants.swift:
16181618

16191619
# 142| [RegExpConstant, RegExpNormalChar] !
16201620

1621-
# 146| [RegExpGroup] (?s)
1622-
#-----| 0 -> [RegExpConstant, RegExpNormalChar] s
1621+
# 146| [RegExpZeroWidthMatch] (?s)
16231622

16241623
# 146| [RegExpSequence] (?s)(.|\n)*!
1625-
#-----| 0 -> [RegExpGroup] (?s)
1624+
#-----| 0 -> [RegExpZeroWidthMatch] (?s)
16261625
#-----| 1 -> [RegExpStar] (.|\n)*
16271626
#-----| 2 -> [RegExpConstant, RegExpNormalChar] !
16281627

1629-
# 146| [RegExpConstant, RegExpNormalChar] s
1630-
16311628
# 146| [RegExpGroup] (.|\n)
16321629
#-----| 0 -> [RegExpAlt] .|\n
16331630

@@ -6492,61 +6489,49 @@ regex.swift:
64926489

64936490
# 206| [RegExpNamedCharacterProperty] [:aaaaa:]
64946491

6495-
# 211| [RegExpGroup] (?i)
6496-
#-----| 0 -> [RegExpConstant, RegExpNormalChar] i
6492+
# 211| [RegExpZeroWidthMatch] (?i)
64976493

64986494
# 211| [RegExpSequence] (?i)abc
6499-
#-----| 0 -> [RegExpGroup] (?i)
6495+
#-----| 0 -> [RegExpZeroWidthMatch] (?i)
65006496
#-----| 1 -> [RegExpConstant, RegExpNormalChar] abc
65016497

6502-
# 211| [RegExpConstant, RegExpNormalChar] i
6503-
65046498
# 211| [RegExpConstant, RegExpNormalChar] abc
65056499

6506-
# 212| [RegExpGroup] (?s)
6507-
#-----| 0 -> [RegExpConstant, RegExpNormalChar] s
6500+
# 212| [RegExpZeroWidthMatch] (?s)
65086501

65096502
# 212| [RegExpSequence] (?s)abc
6510-
#-----| 0 -> [RegExpGroup] (?s)
6503+
#-----| 0 -> [RegExpZeroWidthMatch] (?s)
65116504
#-----| 1 -> [RegExpConstant, RegExpNormalChar] abc
65126505

6513-
# 212| [RegExpConstant, RegExpNormalChar] s
6514-
65156506
# 212| [RegExpConstant, RegExpNormalChar] abc
65166507

6517-
# 213| [RegExpGroup] (?is)
6518-
#-----| 0 -> [RegExpConstant, RegExpNormalChar] is
6508+
# 213| [RegExpZeroWidthMatch] (?is)
65196509

65206510
# 213| [RegExpSequence] (?is)abc
6521-
#-----| 0 -> [RegExpGroup] (?is)
6511+
#-----| 0 -> [RegExpZeroWidthMatch] (?is)
65226512
#-----| 1 -> [RegExpConstant, RegExpNormalChar] abc
65236513

6524-
# 213| [RegExpConstant, RegExpNormalChar] is
6525-
65266514
# 213| [RegExpConstant, RegExpNormalChar] abc
65276515

65286516
# 214| [RegExpGroup] (?i-s)
6529-
#-----| 0 -> [RegExpConstant, RegExpNormalChar] i-s
6517+
#-----| 0 -> [RegExpConstant, RegExpNormalChar] -s
65306518

65316519
# 214| [RegExpSequence] (?i-s)abc
65326520
#-----| 0 -> [RegExpGroup] (?i-s)
65336521
#-----| 1 -> [RegExpConstant, RegExpNormalChar] abc
65346522

6535-
# 214| [RegExpConstant, RegExpNormalChar] i-s
6523+
# 214| [RegExpConstant, RegExpNormalChar] -s
65366524

65376525
# 214| [RegExpConstant, RegExpNormalChar] abc
65386526

65396527
# 217| [RegExpConstant, RegExpNormalChar] abc
65406528

65416529
# 217| [RegExpSequence] abc(?i)def
65426530
#-----| 0 -> [RegExpConstant, RegExpNormalChar] abc
6543-
#-----| 1 -> [RegExpGroup] (?i)
6531+
#-----| 1 -> [RegExpZeroWidthMatch] (?i)
65446532
#-----| 2 -> [RegExpConstant, RegExpNormalChar] def
65456533

6546-
# 217| [RegExpGroup] (?i)
6547-
#-----| 0 -> [RegExpConstant, RegExpNormalChar] i
6548-
6549-
# 217| [RegExpConstant, RegExpNormalChar] i
6534+
# 217| [RegExpZeroWidthMatch] (?i)
65506535

65516536
# 217| [RegExpConstant, RegExpNormalChar] def
65526537

@@ -6558,16 +6543,13 @@ regex.swift:
65586543
#-----| 2 -> [RegExpConstant, RegExpNormalChar] ghi
65596544

65606545
# 218| [RegExpGroup] (?i:def)
6561-
#-----| 0 -> [RegExpConstant, RegExpNormalChar] i:def
6546+
#-----| 0 -> [RegExpConstant, RegExpNormalChar] :def
65626547

6563-
# 218| [RegExpConstant, RegExpNormalChar] i:def
6548+
# 218| [RegExpConstant, RegExpNormalChar] :def
65646549

65656550
# 218| [RegExpConstant, RegExpNormalChar] ghi
65666551

6567-
# 219| [RegExpGroup] (?i)
6568-
#-----| 0 -> [RegExpConstant, RegExpNormalChar] i
6569-
6570-
# 219| [RegExpConstant, RegExpNormalChar] i
6552+
# 219| [RegExpZeroWidthMatch] (?i)
65716553

65726554
# 219| [RegExpConstant, RegExpNormalChar] abc
65736555

swift/ql/test/library-tests/regex/regex.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ func myRegexpMethodsTests(b: Bool, str_unknown: String) throws {
211211
_ = try Regex("(?i)abc").firstMatch(in: input) // $ input=input modes=IGNORECASE regex=(?i)abc
212212
_ = try Regex("(?s)abc").firstMatch(in: input) // $ input=input modes=DOTALL regex=(?s)abc
213213
_ = try Regex("(?is)abc").firstMatch(in: input) // $ input=input modes="DOTALL | IGNORECASE" regex=(?is)abc
214-
_ = try Regex("(?i-s)abc").firstMatch(in: input) // $ input=input regex=(?i-s)abc MISSING: modes=IGNORECASE SPURIOUS: modes="DOTALL | IGNORECASE"
214+
_ = try Regex("(?i-s)abc").firstMatch(in: input) // $ input=input regex=(?i-s)abc modes=IGNORECASE
215215

216216
// these cases use parse modes on localized areas of the regex, which we don't currently support
217217
_ = try Regex("abc(?i)def").firstMatch(in: input) // $ input=input modes=IGNORECASE regex=abc(?i)def

swift/ql/test/query-tests/Security/CWE-116/BadTagFilter.expected

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
11
| test.swift:79:26:79:48 | <script.*?>.*?<\\/script> | This regular expression does not match script end tags like </script >. |
2+
| test.swift:83:27:83:54 | (?is)<script.*?>.*?<\\/script> | This regular expression does not match script end tags like </script >. |
23
| test.swift:86:27:86:49 | <script.*?>.*?<\\/script> | This regular expression does not match script end tags like </script >. |
34
| test.swift:90:50:90:72 | <script.*?>.*?<\\/script> | This regular expression does not match script end tags like </script >. |
45
| test.swift:113:26:113:35 | <!--.*--!?> | This regular expression does not match comments containing newlines. |
56
| test.swift:117:26:117:58 | <script.*?>(.\|\\s)*?<\\/script[^>]*> | This regular expression matches <script></script>, but not <script \\n></script> |
67
| test.swift:121:26:121:56 | <script[^>]*?>.*?<\\/script[^>]*> | This regular expression matches <script>...</script>, but not <script >...\\n</script> |
78
| test.swift:125:26:125:63 | <script(\\s\|\\w\|=\|")*?>.*?<\\/script[^>]*> | This regular expression does not match script tags where the attribute uses single-quotes. |
9+
| test.swift:129:28:129:70 | (?is)<script(\\s\|\\w\|=\|')*?>.*?<\\/script[^>]*> | This regular expression does not match script tags where the attribute uses double-quotes. |
810
| test.swift:132:28:132:65 | <script(\\s\|\\w\|=\|')*?>.*?<\\/script[^>]*> | This regular expression does not match script tags where the attribute uses double-quotes. |
911
| test.swift:136:50:136:87 | <script(\\s\|\\w\|=\|')*?>.*?<\\/script[^>]*> | This regular expression does not match script tags where the attribute uses double-quotes. |
12+
| test.swift:140:28:140:74 | (?is)<script( \|\\n\|\\w\|=\|'\|")*?>.*?<\\/script[^>]*> | This regular expression does not match script tags where tabs are used between attributes. |
1013
| test.swift:143:28:143:69 | <script( \|\\n\|\\w\|=\|'\|")*?>.*?<\\/script[^>]*> | This regular expression does not match script tags where tabs are used between attributes. |
1114
| test.swift:147:50:147:91 | <script( \|\\n\|\\w\|=\|'\|")*?>.*?<\\/script[^>]*> | This regular expression does not match script tags where tabs are used between attributes. |
15+
| test.swift:151:28:151:59 | (?s)<script.*?>.*?<\\/script[^>]*> | This regular expression does not match upper case <SCRIPT> tags. |
1216
| test.swift:154:28:154:55 | <script.*?>.*?<\\/script[^>]*> | This regular expression does not match upper case <SCRIPT> tags. |
1317
| test.swift:157:50:157:77 | <script.*?>.*?<\\/script[^>]*> | This regular expression does not match upper case <SCRIPT> tags. |
18+
| test.swift:161:28:161:77 | (?s)<(script\|SCRIPT).*?>.*?<\\/(script\|SCRIPT)[^>]*> | This regular expression does not match mixed case <sCrIpT> tags. |
1419
| test.swift:164:28:164:73 | <(script\|SCRIPT).*?>.*?<\\/(script\|SCRIPT)[^>]*> | This regular expression does not match mixed case <sCrIpT> tags. |
1520
| test.swift:167:50:167:95 | <(script\|SCRIPT).*?>.*?<\\/(script\|SCRIPT)[^>]*> | This regular expression does not match mixed case <sCrIpT> tags. |
21+
| test.swift:171:28:171:64 | (?i)<script[^>]*?>[\\s\\S]*?<\\/script.*> | This regular expression does not match script end tags like </script\\t\\n bar>. |
1622
| test.swift:174:28:174:60 | <script[^>]*?>[\\s\\S]*?<\\/script.*> | This regular expression does not match script end tags like </script\\t\\n bar>. |
1723
| test.swift:177:50:177:82 | <script[^>]*?>[\\s\\S]*?<\\/script.*> | This regular expression does not match script end tags like </script\\t\\n bar>. |
1824
| test.swift:191:27:191:68 | <(?:!--([\\S\|\\s]*?)-->)\|([^\\/\\s>]+)[\\S\\s]*?> | Comments ending with --> are matched differently from comments ending with --!>. The first is matched with capture group 1 and comments ending with --!> are matched with capture group 2. |

swift/ql/test/query-tests/Security/CWE-116/test.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func myRegexpVariantsTests(myUrl: URL) throws {
7979
let re1 = try Regex(#"<script.*?>.*?<\/script>"#).ignoresCase(true)
8080
_ = try re1.firstMatch(in: tainted)
8181

82-
// BAD - doesn't match `</script >` [NOT DETECTED - all regexs with mode flags are currently missed by the query]
82+
// BAD - doesn't match `</script >`
8383
let re2a = try Regex(#"(?is)<script.*?>.*?<\/script>"#)
8484
_ = try re2a.firstMatch(in: tainted)
8585
// BAD - doesn't match `</script >`
@@ -125,7 +125,7 @@ func myRegexpVariantsTests(myUrl: URL) throws {
125125
let re9 = try Regex(#"<script(\s|\w|=|")*?>.*?<\/script[^>]*>"#).ignoresCase(true).dotMatchesNewlines(true)
126126
_ = try re9.firstMatch(in: tainted)
127127

128-
// BAD - does not match double quotes for attribute values [NOT DETECTED]
128+
// BAD - does not match double quotes for attribute values
129129
let re10a = try Regex(#"(?is)<script(\s|\w|=|')*?>.*?<\/script[^>]*>"#)
130130
_ = try re10a.firstMatch(in: tainted)
131131
// BAD - does not match double quotes for attribute values
@@ -136,7 +136,7 @@ func myRegexpVariantsTests(myUrl: URL) throws {
136136
let ns10 = try NSRegularExpression(pattern: #"<script(\s|\w|=|')*?>.*?<\/script[^>]*>"#, options: options10)
137137
_ = ns10.firstMatch(in: tainted, range: NSMakeRange(0, tainted.utf16.count))
138138

139-
// BAD - does not match tabs between attributes [NOT DETECTED]
139+
// BAD - does not match tabs between attributes
140140
let re11a = try Regex(#"(?is)<script( |\n|\w|=|'|")*?>.*?<\/script[^>]*>"#)
141141
_ = try re11a.firstMatch(in: tainted)
142142
// BAD - does not match tabs between attributes
@@ -147,7 +147,7 @@ func myRegexpVariantsTests(myUrl: URL) throws {
147147
let ns11 = try NSRegularExpression(pattern: #"<script( |\n|\w|=|'|")*?>.*?<\/script[^>]*>"#, options: options11)
148148
_ = ns11.firstMatch(in: tainted, range: NSMakeRange(0, tainted.utf16.count))
149149

150-
// BAD - does not match uppercase SCRIPT tags [NOT DETECTED]
150+
// BAD - does not match uppercase SCRIPT tags
151151
let re12a = try Regex(#"(?s)<script.*?>.*?<\/script[^>]*>"#)
152152
_ = try re12a.firstMatch(in: tainted)
153153
// BAD - does not match uppercase SCRIPT tags
@@ -157,7 +157,7 @@ func myRegexpVariantsTests(myUrl: URL) throws {
157157
let ns12 = try NSRegularExpression(pattern: #"<script.*?>.*?<\/script[^>]*>"#, options: .dotMatchesLineSeparators)
158158
_ = ns12.firstMatch(in: tainted, range: NSMakeRange(0, tainted.utf16.count))
159159

160-
// BAD - does not match mixed case script tags [NOT DETECTED]
160+
// BAD - does not match mixed case script tags
161161
let re13a = try Regex(#"(?s)<(script|SCRIPT).*?>.*?<\/(script|SCRIPT)[^>]*>"#)
162162
_ = try re13a.firstMatch(in: tainted)
163163
// BAD - does not match mixed case script tags
@@ -167,7 +167,7 @@ func myRegexpVariantsTests(myUrl: URL) throws {
167167
let ns13 = try NSRegularExpression(pattern: #"<(script|SCRIPT).*?>.*?<\/(script|SCRIPT)[^>]*>"#, options: .dotMatchesLineSeparators)
168168
_ = ns13.firstMatch(in: tainted, range: NSMakeRange(0, tainted.utf16.count))
169169

170-
// BAD - doesn't match newlines in the end tag [NOT DETECTED]
170+
// BAD - doesn't match newlines in the end tag
171171
let re14a = try Regex(#"(?i)<script[^>]*?>[\s\S]*?<\/script.*>"#)
172172
_ = try re14a.firstMatch(in: tainted)
173173
// BAD - doesn't match newlines in the end tag

swift/ql/test/query-tests/Security/CWE-1333/ReDoS.expected

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
| ReDoS.swift:65:22:65:22 | a* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. |
33
| ReDoS.swift:66:22:66:22 | a* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. |
44
| ReDoS.swift:69:18:69:18 | a* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. |
5-
| ReDoS.swift:75:46:75:46 | a* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. |
6-
| ReDoS.swift:77:57:77:57 | a* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. |
7-
| ReDoS.swift:80:57:80:57 | a* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. |
5+
| ReDoS.swift:73:26:73:33 | (?:.\|\\n)* | This part of the regular expression may cause exponential backtracking on strings starting with 'x' and containing many repetitions of '\\n'. |
6+
| ReDoS.swift:77:46:77:46 | a* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. |
7+
| ReDoS.swift:79:57:79:57 | a* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. |
8+
| ReDoS.swift:82:57:82:57 | a* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. |

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ func myRegexpTests(myUrl: URL) throws {
7070
let regex = try Regex(str)
7171
_ = try regex.firstMatch(in: tainted)
7272

73+
_ = try Regex(#"(?is)X(?:.|\n)*Y"#) // BAD - suggested attack should begin with 'x' or 'X', *not* 'isx' or 'isX'
74+
7375
// NSRegularExpression
7476

7577
_ = try? NSRegularExpression(pattern: "((a*)*b)") // DUBIOUS (never used) [FLAGGED]

0 commit comments

Comments
 (0)