Skip to content

Commit 4561770

Browse files
committed
Swift: Post-processing query for inline test expectations
1 parent 5b5ca05 commit 4561770

File tree

5 files changed

+77
-54
lines changed

5 files changed

+77
-54
lines changed

swift/ql/test/TestUtilities/InlineExpectationsTest.qll

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,6 @@
33
* See `shared/util/codeql/util/test/InlineExpectationsTest.qll`
44
*/
55

6-
private import swift as S
76
private import codeql.util.test.InlineExpectationsTest
8-
9-
private module Impl implements InlineExpectationsTestSig {
10-
private newtype TExpectationComment = MkExpectationComment(S::SingleLineComment c)
11-
12-
/**
13-
* A class representing a line comment.
14-
* Unlike the `SingleLineComment` class, however, the string returned by `getContents` does _not_
15-
* include the preceding comment marker (`//`).
16-
*/
17-
class ExpectationComment extends TExpectationComment {
18-
S::SingleLineComment comment;
19-
20-
ExpectationComment() { this = MkExpectationComment(comment) }
21-
22-
/** Returns the contents of the given comment, _without_ the preceding comment marker (`//`). */
23-
string getContents() { result = comment.getText().suffix(2) }
24-
25-
/** Gets a textual representation of this element. */
26-
string toString() { result = comment.toString() }
27-
28-
/** Gets the location of this comment. */
29-
Location getLocation() { result = comment.getLocation() }
30-
}
31-
32-
class Location = S::Location;
33-
}
34-
7+
private import internal.InlineExpectationsTestImpl
358
import Make<Impl>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* @kind test-postprocess
3+
*/
4+
5+
private import swift
6+
private import codeql.util.test.InlineExpectationsTest as T
7+
private import internal.InlineExpectationsTestImpl
8+
import T::TestPostProcessing
9+
import T::TestPostProcessing::Make<Impl, Input>
10+
11+
private module Input implements T::TestPostProcessing::InputSig<Impl> {
12+
string getRelativeUrl(Location location) {
13+
exists(File f, int startline, int startcolumn, int endline, int endcolumn |
14+
location.hasLocationInfo(_, startline, startcolumn, endline, endcolumn) and
15+
f = location.getFile()
16+
|
17+
result =
18+
f.getRelativePath() + ":" + startline + ":" + startcolumn + ":" + endline + ":" + endcolumn
19+
)
20+
}
21+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
private import swift as S
2+
private import codeql.util.test.InlineExpectationsTest
3+
4+
module Impl implements InlineExpectationsTestSig {
5+
private newtype TExpectationComment = MkExpectationComment(S::SingleLineComment c)
6+
7+
/**
8+
* A class representing a line comment.
9+
* Unlike the `SingleLineComment` class, however, the string returned by `getContents` does _not_
10+
* include the preceding comment marker (`//`).
11+
*/
12+
class ExpectationComment extends TExpectationComment {
13+
S::SingleLineComment comment;
14+
15+
ExpectationComment() { this = MkExpectationComment(comment) }
16+
17+
/** Returns the contents of the given comment, _without_ the preceding comment marker (`//`). */
18+
string getContents() { result = comment.getText().suffix(2) }
19+
20+
/** Gets a textual representation of this element. */
21+
string toString() { result = comment.toString() }
22+
23+
/** Gets the location of this comment. */
24+
Location getLocation() { result = comment.getLocation() }
25+
}
26+
27+
class Location = S::Location;
28+
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
queries/Security/CWE-094/UnsafeJsEval.ql
1+
query: queries/Security/CWE-094/UnsafeJsEval.ql
2+
postprocess: TestUtilities/InlineExpectationsTestQuery.ql

swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.swift

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -175,17 +175,17 @@ func testAsync(_ sink: @escaping (String) async throws -> ()) {
175175
let url = URL(string: "http://example.com/")
176176

177177
try! await sink(localString) // GOOD: the HTML data is local
178-
try! await sink(try String(contentsOf: URL(string: "http://example.com/")!)) // BAD [NOT DETECTED - TODO]: HTML contains remote input, may access local secrets
179-
try! await sink(try! String(contentsOf: url!)) // BAD [NOT DETECTED - TODO]
178+
try! await sink(try String(contentsOf: URL(string: "http://example.com/")!)) // $ MISSING: Alert (HTML contains remote input, may access local secrets)
179+
try! await sink(try! String(contentsOf: url!)) // $ MISSING: Alert
180180

181181
try! await sink("console.log(" + localStringFragment + ")") // GOOD: the HTML data is local
182-
try! await sink("console.log(" + (try! String(contentsOf: url!)) + ")") // BAD [NOT DETECTED - TODO]
182+
try! await sink("console.log(" + (try! String(contentsOf: url!)) + ")") // $ MISSING: Alert
183183

184184
let localData = Data(localString.utf8)
185185
let remoteData = Data((try! String(contentsOf: url!)).utf8)
186186

187187
try! await sink(String(decoding: localData, as: UTF8.self)) // GOOD: the data is local
188-
try! await sink(String(decoding: remoteData, as: UTF8.self)) // BAD [NOT DETECTED - TODO]: the data is remote
188+
try! await sink(String(decoding: remoteData, as: UTF8.self)) // $ MISSING: Alert the data is remote
189189

190190
try! await sink("console.log(" + String(Int(localStringFragment) ?? 0) + ")") // GOOD: Primitive conversion
191191
try! await sink("console.log(" + String(Int(try! String(contentsOf: url!)) ?? 0) + ")") // GOOD: Primitive conversion
@@ -201,17 +201,17 @@ func testSync(_ sink: @escaping (String) -> ()) {
201201
let url = URL(string: "http://example.com/")
202202

203203
sink(localString) // GOOD: the HTML data is local
204-
sink(try! String(contentsOf: URL(string: "http://example.com/")!)) // BAD: HTML contains remote input, may access local secrets
205-
sink(try! String(contentsOf: url!)) // BAD
204+
sink(try! String(contentsOf: URL(string: "http://example.com/")!)) // $ Source=source1 $ MISSING: Alert HTML contains remote input, may access local secrets
205+
sink(try! String(contentsOf: url!)) // $ Source=source2 $ MISSING: Alert
206206

207207
sink("console.log(" + localStringFragment + ")") // GOOD: the HTML data is local
208-
sink("console.log(" + (try! String(contentsOf: url!)) + ")") // BAD
208+
sink("console.log(" + (try! String(contentsOf: url!)) + ")") // $ Source=source3 $ MISSING: Alert
209209

210210
let localData = Data(localString.utf8)
211-
let remoteData = Data((try! String(contentsOf: url!)).utf8)
211+
let remoteData = Data((try! String(contentsOf: url!)).utf8) // $ Source=source4
212212

213213
sink(String(decoding: localData, as: UTF8.self)) // GOOD: the data is local
214-
sink(String(decoding: remoteData, as: UTF8.self)) // BAD: the data is remote
214+
sink(String(decoding: remoteData, as: UTF8.self)) // $ MISSING: Alert the data is remote
215215

216216
sink("console.log(" + String(Int(localStringFragment) ?? 0) + ")") // GOOD: Primitive conversion
217217
sink("console.log(" + String(Int(try! String(contentsOf: url!)) ?? 0) + ")") // GOOD: Primitive conversion
@@ -224,60 +224,60 @@ func testUIWebView() {
224224
let webview = UIWebView()
225225

226226
testAsync { string in
227-
_ = await webview.stringByEvaluatingJavaScript(from: string) // BAD [NOT DETECTED]
227+
_ = await webview.stringByEvaluatingJavaScript(from: string) // $ MISSING: Alert
228228
}
229229
}
230230

231231
func testWebView() {
232232
let webview = WebView()
233233

234234
testAsync { string in
235-
_ = await webview.stringByEvaluatingJavaScript(from: string) // BAD [NOT DETECTED]
235+
_ = await webview.stringByEvaluatingJavaScript(from: string) // $ MISSING: Alert
236236
}
237237
}
238238

239239
func testWKWebView() {
240240
let webview = WKWebView()
241241

242242
testAsync { string in
243-
_ = try await webview.evaluateJavaScript(string) // BAD [NOT DETECTED]
243+
_ = try await webview.evaluateJavaScript(string) // $ MISSING: Alert
244244
}
245245
testAsync { string in
246-
await webview.evaluateJavaScript(string) { _, _ in } // BAD [NOT DETECTED]
246+
await webview.evaluateJavaScript(string) { _, _ in } // $ MISSING: Alert
247247
}
248248
testAsync { string in
249-
await webview.evaluateJavaScript(string, in: nil, in: WKContentWorld.defaultClient) { _ in } // BAD [NOT DETECTED]
249+
await webview.evaluateJavaScript(string, in: nil, in: WKContentWorld.defaultClient) { _ in } // $ MISSING: Alert
250250
}
251251
testAsync { string in
252-
_ = try await webview.evaluateJavaScript(string, contentWorld: .defaultClient) // BAD [NOT DETECTED]
252+
_ = try await webview.evaluateJavaScript(string, contentWorld: .defaultClient) // $ MISSING: Alert
253253
}
254254
testAsync { string in
255-
await webview.callAsyncJavaScript(string, in: nil, in: .defaultClient) { _ in () } // BAD [NOT DETECTED]
255+
await webview.callAsyncJavaScript(string, in: nil, in: .defaultClient) { _ in () } // $ MISSING: Alert
256256
}
257257
testAsync { string in
258-
_ = try await webview.callAsyncJavaScript(string, contentWorld: WKContentWorld.defaultClient) // BAD [NOT DETECTED]
258+
_ = try await webview.callAsyncJavaScript(string, contentWorld: WKContentWorld.defaultClient) // $ MISSING: Alert
259259
}
260260
}
261261

262262
func testWKUserContentController() {
263263
let ctrl = WKUserContentController()
264264

265265
testSync { string in
266-
ctrl.addUserScript(WKUserScript(source: string, injectionTime: .atDocumentStart, forMainFrameOnly: false)) // BAD (multiple sources)
266+
ctrl.addUserScript(WKUserScript(source: string, injectionTime: .atDocumentStart, forMainFrameOnly: false)) // $ Alert=source1 $ Alert=source2 $ Alert=source3 $ Alert=source4
267267
}
268268
testSync { string in
269-
ctrl.addUserScript(WKUserScript(source: string, injectionTime: .atDocumentEnd, forMainFrameOnly: true, in: .defaultClient)) // BAD (multiple sources)
269+
ctrl.addUserScript(WKUserScript(source: string, injectionTime: .atDocumentEnd, forMainFrameOnly: true, in: .defaultClient)) // $ Alert=source1 $ Alert=source2 $ Alert=source3 $ Alert=source4
270270
}
271271
}
272272

273273
func testJSContext() {
274274
let ctx = JSContext()
275275

276276
testSync { string in
277-
_ = ctx.evaluateScript(string) // BAD (multiple sources)
277+
_ = ctx.evaluateScript(string) // $ Alert=source1 $ Alert=source2 $ Alert=source3 $ Alert=source4
278278
}
279279
testSync { string in
280-
_ = ctx.evaluateScript(string, withSourceURL: URL(string: "https://example.com")) // BAD (multiple sources)
280+
_ = ctx.evaluateScript(string, withSourceURL: URL(string: "https://example.com")) // $ Alert=source1 $ Alert=source2 $ Alert=source3 $ Alert=source4
281281
}
282282
}
283283

@@ -288,7 +288,7 @@ func testJSEvaluateScript() {
288288
defer { JSStringRelease(jsstr) }
289289
_ = JSEvaluateScript(
290290
/*ctx:*/ OpaquePointer(bitPattern: 0),
291-
/*script:*/ jsstr, // BAD (multiple sources)
291+
/*script:*/ jsstr, // $ Alert=source1 $ Alert=source2 $ Alert=source3 $ Alert=source4
292292
/*thisObject:*/ OpaquePointer(bitPattern: 0),
293293
/*sourceURL:*/ OpaquePointer(bitPattern: 0),
294294
/*startingLineNumber:*/ 0,
@@ -302,7 +302,7 @@ func testJSEvaluateScript() {
302302
defer { JSStringRelease(jsstr) }
303303
_ = JSEvaluateScript(
304304
/*ctx:*/ OpaquePointer(bitPattern: 0),
305-
/*script:*/ jsstr, // BAD (multiple sources)
305+
/*script:*/ jsstr, // $ Alert=source1 $ Alert=source2 $ Alert=source3 $ Alert=source4
306306
/*thisObject:*/ OpaquePointer(bitPattern: 0),
307307
/*sourceURL:*/ OpaquePointer(bitPattern: 0),
308308
/*startingLineNumber:*/ 0,
@@ -315,9 +315,9 @@ func testJSEvaluateScript() {
315315
func testQHelpExamples() {
316316
Task {
317317
let webview = WKWebView()
318-
let remoteData = try String(contentsOf: URL(string: "http://example.com/evil.json")!)
318+
let remoteData = try String(contentsOf: URL(string: "http://example.com/evil.json")!) // $ Source=source5
319319

320-
_ = try await webview.evaluateJavaScript("console.log(" + remoteData + ")") // BAD
320+
_ = try await webview.evaluateJavaScript("console.log(" + remoteData + ")") // $ Alert=source5
321321

322322
_ = try await webview.callAsyncJavaScript(
323323
"console.log(data)",

0 commit comments

Comments
 (0)