Skip to content

Commit 2d76d6d

Browse files
committed
Swift: Tests for CWE-95.
1 parent bada5bf commit 2d76d6d

File tree

3 files changed

+208
-0
lines changed

3 files changed

+208
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
| TODO |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
queries/Security/CWE-095/UnsafeWebviewFetch.ql
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
2+
// --- stubs ---
3+
4+
class NSObject
5+
{
6+
}
7+
8+
class URL
9+
{
10+
init?(string: String) {}
11+
init?(string: String, relativeTo: URL?) {}
12+
}
13+
14+
extension String {
15+
init(contentsOf: URL) throws {
16+
var data = ""
17+
18+
// ...
19+
20+
self.init(data)
21+
}
22+
}
23+
24+
class NSURLRequest : NSObject
25+
{
26+
enum CachePolicy : UInt
27+
{
28+
case useProtocolCachePolicy
29+
}
30+
}
31+
32+
typealias TimeInterval = Double
33+
34+
class URLRequest
35+
{
36+
typealias CachePolicy = NSURLRequest.CachePolicy
37+
38+
init(url: URL, cachePolicy: URLRequest.CachePolicy = .useProtocolCachePolicy, timeoutInterval: TimeInterval = 60.0) {}
39+
}
40+
41+
class Data
42+
{
43+
init<S>(_ elements: S) {}
44+
}
45+
46+
class WKNavigation : NSObject
47+
{
48+
}
49+
50+
class UIResponder : NSObject
51+
{
52+
}
53+
54+
class UIView : UIResponder
55+
{
56+
}
57+
58+
class UIWebView : UIView
59+
{
60+
func loadRequest(_ request: URLRequest) {} // deprecated
61+
62+
func load(_ data: Data, mimeType MIMEType: String, textEncodingName: String, baseURL: URL) {} // deprecated
63+
64+
func loadHTMLString(_ string: String, baseURL: URL?) {} // deprecated
65+
}
66+
67+
class WKWebView : UIView
68+
{
69+
func load(_ request: URLRequest) -> WKNavigation? {
70+
// ...
71+
72+
return WKNavigation()
73+
}
74+
75+
func load(_ data: Data, mimeType MIMEType: String, characterEncodingName: String, baseURL: URL) -> WKNavigation? {
76+
// ...
77+
78+
return WKNavigation()
79+
}
80+
81+
func loadHTMLString(_ string: String, baseURL: URL?) -> WKNavigation? {
82+
// ...
83+
84+
return WKNavigation()
85+
}
86+
}
87+
88+
// --- tests ---
89+
90+
func getRemoteData() -> String {
91+
let url = URL(string: "http://example.com/")
92+
do
93+
{
94+
return try String(contentsOf: url!)
95+
} catch {
96+
return ""
97+
}
98+
}
99+
100+
func testSimpleFlows() {
101+
let webview = UIWebView()
102+
103+
webview.loadHTMLString(try! String(contentsOf: URL(string: "http://example.com/")!), baseURL: nil) // BAD [NOT DETECTED]
104+
105+
let data = try! String(contentsOf: URL(string: "http://example.com/")!)
106+
webview.loadHTMLString(data, baseURL: nil) // BAD [NOT DETECTED]
107+
108+
let url = URL(string: "http://example.com/")
109+
webview.loadHTMLString(try! String(contentsOf: url!), baseURL: nil) // BAD [NOT DETECTED]
110+
}
111+
112+
func testUIWebView() {
113+
let webview = UIWebView()
114+
115+
let localString = "<html><body><p>Local HTML</p></body></html>"
116+
let localStringFragment = "<body><p>Local HTML</p></body>"
117+
let remoteString = getRemoteData()
118+
119+
webview.loadHTMLString(localString, baseURL: nil) // GOOD: the HTML data is local
120+
webview.loadHTMLString(getRemoteData(), baseURL: nil) // BAD: HTML contains remote input, may access local secrets [NOT DETECTED]
121+
webview.loadHTMLString(remoteString, baseURL: nil) // BAD [NOT DETECTED]
122+
123+
webview.loadHTMLString("<html>" + localStringFragment + "</html>", baseURL: nil) // GOOD: the HTML data is local
124+
webview.loadHTMLString("<html>" + remoteString + "</html>", baseURL: nil) // BAD [NOT DETECTED]
125+
126+
webview.loadHTMLString("<html>\(localStringFragment)</html>", baseURL: nil) // GOOD: the HTML data is local
127+
webview.loadHTMLString("<html>\(remoteString)</html>", baseURL: nil) // BAD [NOT DETECTED]
128+
129+
let localSafeURL = URL(string: "about:blank")
130+
let localURL = URL(string: "http://example.com/")
131+
let remoteURL = URL(string: remoteString)
132+
let remoteURL2 = URL(string: "/path", relativeTo: remoteURL)
133+
134+
webview.loadHTMLString(localString, baseURL: localSafeURL!) // GOOD: a safe baseURL is specified
135+
webview.loadHTMLString(remoteString, baseURL: localSafeURL!) // GOOD: a safe baseURL is specified
136+
webview.loadHTMLString(localString, baseURL: localURL!) // GOOD: a presumed safe baseURL is specified
137+
webview.loadHTMLString(remoteString, baseURL: localURL!) // GOOD: a presumed safe baseURL is specified
138+
webview.loadHTMLString(localString, baseURL: remoteURL!) // GOOD: the HTML data is local
139+
webview.loadHTMLString(remoteString, baseURL: remoteURL!) // BAD [NOT DETECTED]
140+
webview.loadHTMLString(localString, baseURL: remoteURL2!) // GOOD: the HTML data is local
141+
webview.loadHTMLString(remoteString, baseURL: remoteURL2!) // BAD [NOT DETECTED]
142+
143+
let localRequest = URLRequest(url: localURL!)
144+
let remoteRequest = URLRequest(url: remoteURL!)
145+
146+
webview.loadRequest(localRequest) // GOOD: loadRequest is out of scope as it has no baseURL
147+
webview.loadRequest(remoteRequest) // GOOD: loadRequest is out of scope as it has no baseURL
148+
149+
let localData = Data(localString.utf8)
150+
let remoteData = Data(remoteString.utf8)
151+
webview.load(localData, mimeType: "text/html", textEncodingName: "utf-8", baseURL: localSafeURL!) // GOOD: the data is local
152+
webview.load(remoteData, mimeType: "text/html", textEncodingName: "utf-8", baseURL: localSafeURL!) // GOOD: a safe baseURL is specified
153+
webview.load(localData, mimeType: "text/html", textEncodingName: "utf-8", baseURL: remoteURL!) // GOOD: the HTML data is local
154+
webview.load(remoteData, mimeType: "text/html", textEncodingName: "utf-8", baseURL: remoteURL!) // BAD [NOT DETECTED]
155+
}
156+
157+
func testWKWebView() {
158+
let webview = WKWebView()
159+
// note: `WKWebView` is safer than `UIWebView` as it has better security configuration options
160+
// and is more locked down by default.
161+
162+
let localString = "<html><body><p>Local HTML</p></body></html>"
163+
let localStringFragment = "<body><p>Local HTML</p></body>"
164+
let remoteString = getRemoteData()
165+
166+
webview.loadHTMLString(localString, baseURL: nil) // GOOD: the HTML data is local
167+
webview.loadHTMLString(getRemoteData(), baseURL: nil) // BAD [NOT DETECTED]
168+
webview.loadHTMLString(remoteString, baseURL: nil) // BAD [NOT DETECTED]
169+
170+
webview.loadHTMLString("<html>" + localStringFragment + "</html>", baseURL: nil) // GOOD: the HTML data is local
171+
webview.loadHTMLString("<html>" + remoteString + "</html>", baseURL: nil) // BAD [NOT DETECTED]
172+
173+
webview.loadHTMLString("<html>\(localStringFragment)</html>", baseURL: nil) // GOOD: the HTML data is local
174+
webview.loadHTMLString("<html>\(remoteString)</html>", baseURL: nil) // BAD [NOT DETECTED]
175+
176+
let localSafeURL = URL(string: "about:blank")
177+
let localURL = URL(string: "http://example.com/")
178+
let remoteURL = URL(string: remoteString)
179+
let remoteURL2 = URL(string: "/path", relativeTo: remoteURL)
180+
181+
webview.loadHTMLString(localString, baseURL: localSafeURL!) // GOOD: a safe baseURL is specified
182+
webview.loadHTMLString(remoteString, baseURL: localSafeURL!) // GOOD: a safe baseURL is specified
183+
webview.loadHTMLString(localString, baseURL: localURL!) // GOOD: a presumed safe baseURL is specified
184+
webview.loadHTMLString(remoteString, baseURL: localURL!) // GOOD: a presumed safe baseURL is specified
185+
webview.loadHTMLString(localString, baseURL: remoteURL!) // GOOD: the HTML data is local
186+
webview.loadHTMLString(remoteString, baseURL: remoteURL!) // BAD [NOT DETECTED]
187+
webview.loadHTMLString(localString, baseURL: remoteURL2!) // GOOD: the HTML data is local
188+
webview.loadHTMLString(remoteString, baseURL: remoteURL2!) // BAD [NOT DETECTED]
189+
190+
let localRequest = URLRequest(url: localURL!)
191+
let remoteRequest = URLRequest(url: remoteURL!)
192+
193+
webview.load(localRequest) // GOOD: loadRequest is out of scope as it has no baseURL
194+
webview.load(remoteRequest) // GOOD: loadRequest is out of scope as it has no baseURL
195+
196+
let localData = Data(localString.utf8)
197+
let remoteData = Data(remoteString.utf8)
198+
webview.load(localData, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: localSafeURL!) // GOOD: the data is local
199+
webview.load(remoteData, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: localSafeURL!) // GOOD: a safe baseURL is specified
200+
webview.load(localData, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: remoteURL!) // GOOD: the HTML data is local
201+
webview.load(remoteData, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: remoteURL!) // BAD [NOT DETECTED]
202+
}
203+
204+
testSimpleFlows()
205+
testUIWebView()
206+
testWKWebView()

0 commit comments

Comments
 (0)