Skip to content

Commit d5f4939

Browse files
committed
Swift: Add tests for bad tag filter query.
1 parent ca71d48 commit d5f4939

File tree

3 files changed

+173
-0
lines changed

3 files changed

+173
-0
lines changed

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

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
queries/Security/CWE-116/BadTagFilter.ql
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
2+
// --- stubs ---
3+
4+
struct URL {
5+
init?(string: String) {}
6+
}
7+
8+
extension String {
9+
init(contentsOf: URL) {
10+
let data = ""
11+
self.init(data)
12+
}
13+
}
14+
15+
struct AnyRegexOutput {
16+
}
17+
18+
protocol RegexComponent<RegexOutput> {
19+
associatedtype RegexOutput
20+
}
21+
22+
struct Regex<Output> : RegexComponent {
23+
struct Match {
24+
}
25+
26+
init(_ pattern: String) throws where Output == AnyRegexOutput { }
27+
28+
func ignoresCase(_ ignoresCase: Bool = true) -> Regex<Regex<Output>.RegexOutput> { return self }
29+
func dotMatchesNewlines(_ dotMatchesNewlines: Bool = true) -> Regex<Regex<Output>.RegexOutput> { return self }
30+
31+
func firstMatch(in string: String) throws -> Regex<Output>.Match? { return nil}
32+
33+
typealias RegexOutput = Output
34+
}
35+
36+
extension String : RegexComponent {
37+
typealias Output = Substring
38+
typealias RegexOutput = String.Output
39+
}
40+
41+
class NSObject {
42+
}
43+
44+
struct _NSRange {
45+
init(location: Int, length: Int) { }
46+
}
47+
48+
typealias NSRange = _NSRange
49+
50+
func NSMakeRange(_ loc: Int, _ len: Int) -> NSRange { return NSRange(location: loc, length: len) }
51+
52+
class NSTextCheckingResult : NSObject {
53+
}
54+
55+
class NSRegularExpression : NSObject {
56+
struct Options : OptionSet {
57+
var rawValue: UInt
58+
59+
static var caseInsensitive: NSRegularExpression.Options { get { return Options(rawValue: 1) } }
60+
}
61+
62+
struct MatchingOptions : OptionSet {
63+
var rawValue: UInt
64+
}
65+
66+
init(pattern: String, options: NSRegularExpression.Options = []) throws { }
67+
68+
func matches(in string: String, options: NSRegularExpression.MatchingOptions = [], range: NSRange) -> [NSTextCheckingResult] { return [] }
69+
func firstMatch(in string: String, options: NSRegularExpression.MatchingOptions = [], range: NSRange) -> NSTextCheckingResult? { return nil }
70+
}
71+
72+
// --- tests ---
73+
74+
func myRegexpVariantsTests(myUrl: URL) throws {
75+
let tainted = String(contentsOf: myUrl) // tainted
76+
77+
// BAD - doesn't match newlines or `</script >`
78+
let re1 = try Regex(#"<script.*?>.*?<\/script>"#).ignoresCase(true)
79+
_ = try re1.firstMatch(in: tainted)
80+
81+
// BAD - doesn't match `</script >`
82+
let re2 = try Regex(#"<script.*?>.*?<\/script>/is"#).ignoresCase(true)
83+
_ = try re2.firstMatch(in: tainted)
84+
85+
// GOOD
86+
let re3 = try Regex(#"<script.*?>.*?<\/script[^>]*>"#).ignoresCase(true).dotMatchesNewlines(true)
87+
_ = try re3.firstMatch(in: tainted)
88+
89+
// GOOD - we don't care regexps that only match comments
90+
let re4 = try Regex(#"<!--.*-->"#).ignoresCase(true).dotMatchesNewlines(true)
91+
_ = try re4.firstMatch(in: tainted)
92+
93+
// GOOD
94+
let re5 = try Regex(#"<!--.*--!?>"#).ignoresCase(true).dotMatchesNewlines(true)
95+
_ = try re5.firstMatch(in: tainted)
96+
97+
// BAD, does not match newlines
98+
let re6 = try Regex(#"<!--.*--!?>"#).ignoresCase(true)
99+
_ = try re6.firstMatch(in: tainted)
100+
101+
// BAD - doesn't match inside the script tag
102+
let re7 = try Regex(#"<script.*?>(.|\s)*?<\/script[^>]*>"#).ignoresCase(true)
103+
_ = try re7.firstMatch(in: tainted)
104+
105+
// BAD - doesn't match newlines inside the content
106+
let re8 = try Regex(#"<script[^>]*?>.*?<\/script[^>]*>"#).ignoresCase(true)
107+
_ = try re8.firstMatch(in: tainted)
108+
109+
// BAD - does not match single quotes for attribute values
110+
let re9 = try Regex(#"<script(\s|\w|=|")*?>.*?<\/script[^>]*>"#).ignoresCase(true).dotMatchesNewlines(true)
111+
_ = try re9.firstMatch(in: tainted)
112+
113+
// BAD - does not match double quotes for attribute values
114+
let re10 = try Regex(#"<script(\s|\w|=|')*?>.*?<\/script[^>]*>"#).ignoresCase(true).dotMatchesNewlines(true)
115+
_ = try re10.firstMatch(in: tainted)
116+
117+
// BAD - does not match tabs between attributes
118+
let re11 = try Regex(#"<script( |\n|\w|=|'|")*?>.*?<\/script[^>]*>"#).ignoresCase(true).dotMatchesNewlines(true)
119+
_ = try re11.firstMatch(in: tainted)
120+
121+
// BAD - does not match uppercase SCRIPT tags
122+
let re12 = try Regex(#"<script.*?>.*?<\/script[^>]*>"#).dotMatchesNewlines(true)
123+
_ = try re12.firstMatch(in: tainted)
124+
125+
// BAD - does not match mixed case script tags
126+
let re13 = try Regex(#"<(script|SCRIPT).*?>.*?<\/(script|SCRIPT)[^>]*>"#).dotMatchesNewlines(true)
127+
_ = try re13.firstMatch(in: tainted)
128+
129+
// BAD - doesn't match newlines in the end tag
130+
let re14 = try Regex(#"<script[^>]*?>[\s\S]*?<\/script.*>"#).ignoresCase(true)
131+
_ = try re14.firstMatch(in: tainted)
132+
133+
// GOOD
134+
let re15 = try Regex(#"<script[^>]*?>[\s\S]*?<\/script[^>]*?>"#).ignoresCase(true)
135+
_ = try re15.firstMatch(in: tainted)
136+
137+
// BAD - doesn't match comments with the right capture groups
138+
let re16 = try Regex(#"<(?:!--([\S|\s]*?)-->)|([^\/\s>]+)[\S\s]*?>"#)
139+
_ = try re16.firstMatch(in: tainted)
140+
141+
// BAD - capture groups
142+
let re17 = try Regex(#"<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\/\s>]+)((?:\s+[\w\-:.]+(?:\s*=\s*?(?:(?:"[^"]*")|(?:'[^']*')|[^\s"'\/>]+))?)*)[\S\s]*?(\/?)>))"#)
143+
_ = try re17.firstMatch(in: tainted)
144+
145+
// BAD - too strict matching on the end tag
146+
let ns1 = try NSRegularExpression(pattern: #"<script\b[^>]*>([\s\S]*?)<\/script>"#, options: .caseInsensitive)
147+
_ = ns1.matches(in: tainted, range: NSMakeRange(0, tainted.utf16.count))
148+
149+
// BAD - capture groups
150+
let ns2 = try NSRegularExpression(pattern: #"(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)"#, options: .caseInsensitive)
151+
_ = ns2.matches(in: tainted, range: NSMakeRange(0, tainted.utf16.count))
152+
153+
// BAD - capture groups
154+
let ns3 = try NSRegularExpression(pattern: #"<(?:(?:!--([\w\W]*?)-->)|(?:!\[CDATA\[([\w\W]*?)\]\]>)|(?:!DOCTYPE([\w\W]*?)>)|(?:\?([^\s\/<>]+) ?([\w\W]*?)[?/]>)|(?:\/([A-Za-z][A-Za-z0-9\-_\:\.]*)>)|(?:([A-Za-z][A-Za-z0-9\-_\:\.]*)((?:\s+[^"'>]+(?:(?:"[^"]*")|(?:'[^']*')|[^>]*))*|\/|\s+)>))"#)
155+
_ = ns3.matches(in: tainted, range: NSMakeRange(0, tainted.utf16.count))
156+
157+
// BAD - capture groups
158+
let ns4 = try NSRegularExpression(pattern: #"<!--([\w\W]*?)-->|<([^>]*?)>"#)
159+
_ = ns4.matches(in: tainted, range: NSMakeRange(0, tainted.utf16.count))
160+
161+
// GOOD - it's used with the ignorecase flag
162+
let ns5 = try NSRegularExpression(pattern: #"<script([^>]*)>([\\S\\s]*?)<\/script([^>]*)>"#, options: .caseInsensitive)
163+
_ = ns5.matches(in: tainted, range: NSMakeRange(0, tainted.utf16.count))
164+
165+
// BAD - doesn't match --!>
166+
let ns6 = try NSRegularExpression(pattern: #"-->"#)
167+
_ = ns6.matches(in: tainted, range: NSMakeRange(0, tainted.utf16.count))
168+
169+
// GOOD
170+
let ns7 = try NSRegularExpression(pattern: #"^>|^->|<!--|-->|--!>|<!-$"#)
171+
_ = ns7.matches(in: tainted, range: NSMakeRange(0, tainted.utf16.count))
172+
}

0 commit comments

Comments
 (0)