Skip to content

Unified: Add Unsafe JS eval / JavaScript injection query - #22998

Open
geoffw0 wants to merge 6 commits into
github:mainfrom
geoffw0:unifiedjseval
Open

geoffw0 wants to merge 6 commits into
github:mainfrom
geoffw0:unifiedjseval

Conversation

@geoffw0

@geoffw0 geoffw0 commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Adds a unified/swift/unsafe-js-eval query for unified Swift. Done with the same agent skill as before, but a different model to test its versatility of that skill (MAI-Code-2.x1 high effort rather than GPT-6.1 Sol high effort). No problems there.

... and no manual edits this time. We do fairly well on the QL test. There are some newly missing test results (and some that were missing even in the original analysis), addressing those will be follow-up work.

DCA run to follow. DCA LGTM.

geoffw0 and others added 4 commits November 3, 2026 12:39
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@geoffw0 geoffw0 added no-change-note-required This PR does not need a change note Unified labels Nov 3, 2026
@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

QHelp previews:

unified/ql/src/queries/security/CWE-094/UnsafeJsEval.qhelp

JavaScript Injection

Evaluating JavaScript that contains a substring from a remote origin may lead to remote code execution. Code written by an attacker can execute unauthorized actions, including exfiltration of local data through a third party web service.

Recommendation

When loading JavaScript into a web view, evaluate only known, locally-defined source code. If part of the input comes from a remote source, do not inject it into the JavaScript code to be evaluated. Instead, send it to the web view as data using an API such as WKWebView.callAsyncJavaScript with the arguments dictionary to pass remote data objects.

Example

In the following (bad) example, a call to WKWebView.evaluateJavaScript evaluates JavaScript source code that is tainted with remote data, potentially introducing a code injection vulnerability.

let webview: WKWebView
let remoteData = try String(contentsOf: URL(string: "http://example.com/evil.json")!)

...

_ = try await webview.evaluateJavaScript("console.log(" + remoteData + ")") // BAD

In the following (good) example, we sanitize the remote data by passing it using the arguments dictionary of WKWebView.callAsyncJavaScript. This ensures that untrusted data cannot be evaluated as JavaScript source code.

let webview: WKWebView
let remoteData = try String(contentsOf: URL(string: "http://example.com/evil.json")!)

...

_ = try await webview.callAsyncJavaScript(
    "console.log(data)",
    arguments: ["data": remoteData], // GOOD
    contentWorld: .page
)

References

@geoffw0
geoffw0 marked this pull request as ready for review November 5, 2026 18:18
@geoffw0
geoffw0 requested a review from a team as a code owner November 5, 2026 18:19
Copilot AI auto review requested due to automatic review settings November 5, 2026 18:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Incorrect Swift argument-label ordering and missing call-site variants leave code-injection sinks unrecognized, while overly broad source selection can cause false positives.

3 open issues
What changed in this PR

Adds JavaScript injection detection to unified Swift through the unified/swift/unsafe-js-eval query and models for Swift JavaScript execution APIs.

Changes:

  • Introduces a path query and code-injection sink models.
  • Documents unsafe interpolation and safer argument-based data passing.
  • Adds regression fixtures and expected results, with known detection gaps marked inline.
File Description
unified/​ql/​test/​query-tests/​security/​CWE-094/​UnsafeJsEval/​UnsafeJsEval.swift Exercises JavaScript APIs, safe cases, and known gaps.
unified/​ql/​test/​query-tests/​security/​CWE-094/​UnsafeJsEval/​UnsafeJsEval.qlref Connects the query to inline-expectation testing.
unified/​ql/​test/​query-tests/​security/​CWE-094/​UnsafeJsEval/​UnsafeJsEval.expected Records expected alerts and taint paths.
unified/​ql/​test/​query-tests/​security/​CWE-094/​UnsafeJsEval/​CONSISTENCY/​DataFlowConsistency.expected Captures data-flow consistency results.
unified/​ql/​src/​queries/​security/​CWE-094/​UnsafeJsEvalGood.swift Demonstrates passing remote input as data.
unified/​ql/​src/​queries/​security/​CWE-094/​UnsafeJsEvalBad.swift Demonstrates unsafe interpolation of remote input.
unified/​ql/​src/​queries/​security/​CWE-094/​UnsafeJsEval.ql Defines the JavaScript injection path query.
unified/​ql/​src/​queries/​security/​CWE-094/​UnsafeJsEval.qhelp Explains the risk and recommended mitigation.
unified/​ql/​lib/​ext/​legacy-swift.model.yml Adds JavaScript execution sink models.

Review effort: Max (auto)

Comment on lines +257 to +260
- ["", "WKWebView", true, "evaluateJavaScript(_:in:in:completionHandler:)", "", "", "Argument[0]", "code-injection", "manual"]
- ["", "WKWebView", true, "evaluateJavaScript(_:in:contentWorld:)", "", "", "Argument[0]", "code-injection", "manual"]
- ["", "WKWebView", true, "callAsyncJavaScript(_:arguments:in:in:completionHandler:)", "", "", "Argument[0]", "code-injection", "manual"]
- ["", "WKWebView", true, "callAsyncJavaScript(_:arguments:in:contentWorld:)", "", "", "Argument[0]", "code-injection", "manual"]
- ["", "WKWebView", true, "evaluateJavaScript(_:in:contentWorld:)", "", "", "Argument[0]", "code-injection", "manual"]
- ["", "WKWebView", true, "callAsyncJavaScript(_:arguments:in:in:completionHandler:)", "", "", "Argument[0]", "code-injection", "manual"]
- ["", "WKWebView", true, "callAsyncJavaScript(_:arguments:in:contentWorld:)", "", "", "Argument[0]", "code-injection", "manual"]
- ["", "WKUserContentController", true, "addUserScript(_:)", "", "", "Argument[0]", "code-injection", "manual"]
* A taint configuration from taint sources to sinks for this query.
*/
module UnsafeJsEvalConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node node) { Models::isSource(node, _) }

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation no-change-note-required This PR does not need a change note Unified

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants