From dcdf0a0587b5860e6516e3ad41e61cdcf871f649 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 5 Oct 2026 11:50:29 +0200 Subject: [PATCH 01/18] unified: Add test with flow through enums --- .../test/library-tests/dataflow/enums.swift | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 unified/ql/test/library-tests/dataflow/enums.swift diff --git a/unified/ql/test/library-tests/dataflow/enums.swift b/unified/ql/test/library-tests/dataflow/enums.swift new file mode 100644 index 000000000000..82c84852c48f --- /dev/null +++ b/unified/ql/test/library-tests/dataflow/enums.swift @@ -0,0 +1,83 @@ +enum E { + case case1(String) + case case2(String) +} + +func t1() { + let e = E.case1(source("t1.1")) + sink(e) // no flow + switch e { + case E.case1(let x): + sink(x) // $ MISSING: hasValueFlow=t1.1 + default: + break + } +} + +func t2() { + let e = E.case1(source("t2.1")) + sink(e) // no flow + switch e { + case .case1(let x): // use leading-dot syntax + sink(x) // $ MISSING: hasValueFlow=t2.1 + default: + break + } +} + +func t3() { + let e = E.case1(source("t3.1")) + guard let E.case1(x) = e else { return } + sink(x) // $ MISSING: hasValueFlow=t3.1 +} + +func t4() { + let e = E.case2(source("t4.1")) + switch e { + case E.case1(let x): + sink(x) // no flow + case E.case2(let x): + sink(x) // $ MISSING: hasValueFlow=t4.1 + } + // same but in opposite match order + switch e { + case E.case2(let x): + sink(x) // $ MISSING: hasValueFlow=t4.1 + case E.case1(let x): + sink(x) // no flow + } +} + +func t5() { + let opt_x = Optional.some(source("t5.1")) + guard let x = opt_x else { return } + sink(x) // $ MISSING: hasValueFlow=t5.1 +} + +func t6() { + let opt_x = Optional.some(source("t6.1")) + guard let opt_x else { return } + sink(opt_x) // $ MISSING: hasValueFlow=t6.1 +} + +enum OptionalLabel { + case foo(x: String) +} + +func t7() { + let e = OptionalLabel.foo(x: source("t7.1")) + switch e { + case .foo(let x): + sink(x) // $ MISSING: hasValueFlow=t7.1 + default: + break + } + // Note: swift-format will try to remove the 'x:' label in the call below + // swift-format-ignore + switch e { + case .foo(x: let x): + sink(x) // $ MISSING: hasValueFlow=t7.1 + default: + break + } +} From 7aae4bc025eabca8fe2703294afbf30f8df9d2f3 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 5 Oct 2026 12:53:22 +0200 Subject: [PATCH 02/18] unified: Support flow through enum constructors --- .../unified/internal/dataflow/AllDataFlow.qll | 1 + .../internal/dataflow/ConstructorPatterns.qll | 73 +++++++++++ .../unified/internal/dataflow/Content.qll | 2 + .../internal/dataflow/DataFlowGraph.qll | 31 +++++ .../test/library-tests/dataflow/enums.swift | 16 +-- .../test/library-tests/dataflow/test.expected | 118 ++++++++++++++++++ 6 files changed, 233 insertions(+), 8 deletions(-) create mode 100644 unified/ql/lib/codeql/unified/internal/dataflow/ConstructorPatterns.qll diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/AllDataFlow.qll b/unified/ql/lib/codeql/unified/internal/dataflow/AllDataFlow.qll index 0d44eea6cb1e..55b38af1c8cb 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/AllDataFlow.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/AllDataFlow.qll @@ -2,6 +2,7 @@ import CallGraph import Content +import ConstructorPatterns import DataFlowCall import DataFlowCallable import DataFlowGraph diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/ConstructorPatterns.qll b/unified/ql/lib/codeql/unified/internal/dataflow/ConstructorPatterns.qll new file mode 100644 index 000000000000..58bcfed8e1cb --- /dev/null +++ b/unified/ql/lib/codeql/unified/internal/dataflow/ConstructorPatterns.qll @@ -0,0 +1,73 @@ +/** + * Provides data-flow modelling of constructor patterns / enum-case constructors. + */ + +private import unified +private import AllDataFlow +private import codeql.unified.internal.ExprPositions +private import codeql.unified.internal.NameBinding as NameBinding +private import codeql.unified.internal.typeinference.TypeInference as T + +/** + * A constructor pattern, such as `Optional.some(let x)`. + */ +class ConstructorPattern extends CallExpr { + ConstructorPattern() { isInBindingContext(this, _) } +} + +/** + * Gets the unqualified name of the enum-case contructor that might be referenced by `call`. + */ +private string getShortConstructorName(CallExpr call) { + result = call.getCallee().(MemberAccessExpr).getMemberName() + // note: enum constructors can only be accessed qualified (possibly with leading-dot syntax) + // so do not do this for Identifiers +} + +/** + * Holds if a constructor pattern has the given short `name` and `arity`. + */ +pragma[nomagic] +private predicate isSignatureUsedInConstructorPattern(string name, int arity) { + exists(ConstructorPattern ctor | + name = getShortConstructorName(ctor) and + arity = ctor.getNumberOfArguments() + ) +} + +/** Holds if `callable` is an enum-case constructor */ +private predicate isEnumCaseConstructor(ConstructorDeclaration callable) { + callable = any(ClassLikeDeclaration cls | cls.hasModifier("enum_case")).getAMember() +} + +/** + * Holds if `call` resolves to a known enum-case constructor, or is assumed to resolve to an unseen enum-case constructor. + */ +pragma[nomagic] +private predicate assumeResolvesToEnumCaseConstructor(CallExpr call) { + call instanceof ConstructorPattern + or + isEnumCaseConstructor(T::resolveCallTarget(call)) + or + // If the `E` in `E.foo(...)` could not be resolved, check if the name `foo` matches a constructor pattern. + exists(MemberAccessExpr callee, Expr base | + callee = call.getCallee() and + base = callee.getBase() and + not exists(NameBinding::getStaticBindingTargetFromRef(base)) and + not exists(T::inferType(base)) and + isSignatureUsedInConstructorPattern(callee.getMemberName(), call.getNumberOfArguments()) + ) +} + +/** + * Gets the field name for the enum-case data parameter corresponding to the given argument. + */ +string getEnumCaseParameterFieldFromArgument(CallExpr call, Argument arg) { + assumeResolvesToEnumCaseConstructor(call) and + exists(int i | + // Note: The label name is optional when calling an enum-case constructor, but the arguments + // must occur in declaration order, so use the raw argument index to handle both the labelled and unlabelled cases. + arg = call.getArgument(i) and + result = getShortConstructorName(call) + "." + i + ) +} diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/Content.qll b/unified/ql/lib/codeql/unified/internal/dataflow/Content.qll index 73a3d7da2c9d..77a138484138 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/Content.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/Content.qll @@ -8,6 +8,8 @@ private newtype TContent = // Tuple elements can be accessed as named members, e.g. `tuple.0`, `tuple.1`, etc, // so just model their elements as named members. name = [0 .. 20].toString() + or + name = getEnumCaseParameterFieldFromArgument(_, _) } class Content extends TContent { diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll index 91111f7f8c0d..fd478feccd2f 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll @@ -103,6 +103,37 @@ predicate step(Node node1, Step step, Node node2) { node2.isPostUpdate(expr.getBase()) ) or + // Calls and constructor-patterns targeting an enum-case constructor. + exists(CallExpr call, Argument arg, string field | + field = getEnumCaseParameterFieldFromArgument(call, arg) + | + node1.isResultValue(arg.getValue()) and + step.storeName(field) and + node2.isResultValue(call) + or + node1.isIncomingValue(call) and + step.readName(field) and + node2.isIncomingValue(arg.getValue()) + ) + or + exists(SwitchExpr expr | + node1.isResultValue(expr.getValue()) and + step.value() and + node2.isIncomingValue(expr.getACase().getPattern()) + ) + or + exists(PatternGuardExpr expr | + node1.isResultValue(expr.getValue()) and + step.value() and + node2.isIncomingValue(expr.getPattern()) + ) + or + exists(ExprPattern expr | + node1.isIncomingValue(expr) and + step.value() and + node2.isIncomingValue(expr.getExpr()) + ) + or none() // Temporarily disable compilation errors from unsatisfiable types } diff --git a/unified/ql/test/library-tests/dataflow/enums.swift b/unified/ql/test/library-tests/dataflow/enums.swift index 82c84852c48f..b2082c36709b 100644 --- a/unified/ql/test/library-tests/dataflow/enums.swift +++ b/unified/ql/test/library-tests/dataflow/enums.swift @@ -8,7 +8,7 @@ func t1() { sink(e) // no flow switch e { case E.case1(let x): - sink(x) // $ MISSING: hasValueFlow=t1.1 + sink(x) // $ hasValueFlow=t1.1 default: break } @@ -19,7 +19,7 @@ func t2() { sink(e) // no flow switch e { case .case1(let x): // use leading-dot syntax - sink(x) // $ MISSING: hasValueFlow=t2.1 + sink(x) // $ hasValueFlow=t2.1 default: break } @@ -37,12 +37,12 @@ func t4() { case E.case1(let x): sink(x) // no flow case E.case2(let x): - sink(x) // $ MISSING: hasValueFlow=t4.1 + sink(x) // $ hasValueFlow=t4.1 } // same but in opposite match order switch e { case E.case2(let x): - sink(x) // $ MISSING: hasValueFlow=t4.1 + sink(x) // $ hasValueFlow=t4.1 case E.case1(let x): sink(x) // no flow } @@ -51,13 +51,13 @@ func t4() { func t5() { let opt_x = Optional.some(source("t5.1")) guard let x = opt_x else { return } - sink(x) // $ MISSING: hasValueFlow=t5.1 + sink(x) // $ hasValueFlow=t5.1 } func t6() { let opt_x = Optional.some(source("t6.1")) guard let opt_x else { return } - sink(opt_x) // $ MISSING: hasValueFlow=t6.1 + sink(opt_x) // $ hasValueFlow=t6.1 } enum OptionalLabel { @@ -68,7 +68,7 @@ func t7() { let e = OptionalLabel.foo(x: source("t7.1")) switch e { case .foo(let x): - sink(x) // $ MISSING: hasValueFlow=t7.1 + sink(x) // $ hasValueFlow=t7.1 default: break } @@ -76,7 +76,7 @@ func t7() { // swift-format-ignore switch e { case .foo(x: let x): - sink(x) // $ MISSING: hasValueFlow=t7.1 + sink(x) // $ hasValueFlow=t7.1 default: break } diff --git a/unified/ql/test/library-tests/dataflow/test.expected b/unified/ql/test/library-tests/dataflow/test.expected index 478e0e3edfa3..03ff2e596e8c 100644 --- a/unified/ql/test/library-tests/dataflow/test.expected +++ b/unified/ql/test/library-tests/dataflow/test.expected @@ -38,6 +38,58 @@ edges | calls.swift:99:18:99:21 | self [field] | calls.swift:99:18:99:27 | ... .field | provenance | | | calls.swift:111:18:111:21 | self [field] | calls.swift:111:18:111:27 | ... .field | provenance | | | calls.swift:123:18:123:21 | self [field] | calls.swift:123:18:123:27 | ... .field | provenance | | +| enums.swift:7:9:7:9 | e [case1.0] | enums.swift:9:12:9:12 | e [case1.0] | provenance | | +| enums.swift:7:13:7:35 | ... .case1(...) [case1.0] | enums.swift:7:9:7:9 | e [case1.0] | provenance | | +| enums.swift:7:21:7:34 | source(...) | enums.swift:7:13:7:35 | ... .case1(...) [case1.0] | provenance | | +| enums.swift:9:12:9:12 | e [case1.0] | enums.swift:10:10:10:23 | ... .case1(...) [case1.0] | provenance | | +| enums.swift:10:10:10:23 | ... .case1(...) [case1.0] | enums.swift:10:18:10:22 | ExprPattern | provenance | | +| enums.swift:10:18:10:22 | ExprPattern | enums.swift:10:22:10:22 | x | provenance | | +| enums.swift:10:22:10:22 | x | enums.swift:11:14:11:14 | x | provenance | | +| enums.swift:18:9:18:9 | e [case1.0] | enums.swift:20:12:20:12 | e [case1.0] | provenance | | +| enums.swift:18:13:18:35 | ... .case1(...) [case1.0] | enums.swift:18:9:18:9 | e [case1.0] | provenance | | +| enums.swift:18:21:18:34 | source(...) | enums.swift:18:13:18:35 | ... .case1(...) [case1.0] | provenance | | +| enums.swift:20:12:20:12 | e [case1.0] | enums.swift:21:10:21:22 | ... .case1(...) [case1.0] | provenance | | +| enums.swift:21:10:21:22 | ... .case1(...) [case1.0] | enums.swift:21:17:21:21 | ExprPattern | provenance | | +| enums.swift:21:17:21:21 | ExprPattern | enums.swift:21:21:21:21 | x | provenance | | +| enums.swift:21:21:21:21 | x | enums.swift:22:14:22:14 | x | provenance | | +| enums.swift:35:9:35:9 | e [case2.0] | enums.swift:36:12:36:12 | e [case2.0] | provenance | | +| enums.swift:35:9:35:9 | e [case2.0] | enums.swift:43:12:43:12 | e [case2.0] | provenance | | +| enums.swift:35:13:35:35 | ... .case2(...) [case2.0] | enums.swift:35:9:35:9 | e [case2.0] | provenance | | +| enums.swift:35:21:35:34 | source(...) | enums.swift:35:13:35:35 | ... .case2(...) [case2.0] | provenance | | +| enums.swift:36:12:36:12 | e [case2.0] | enums.swift:39:10:39:23 | ... .case2(...) [case2.0] | provenance | | +| enums.swift:39:10:39:23 | ... .case2(...) [case2.0] | enums.swift:39:18:39:22 | ExprPattern | provenance | | +| enums.swift:39:18:39:22 | ExprPattern | enums.swift:39:22:39:22 | x | provenance | | +| enums.swift:39:22:39:22 | x | enums.swift:40:14:40:14 | x | provenance | | +| enums.swift:43:12:43:12 | e [case2.0] | enums.swift:44:10:44:23 | ... .case2(...) [case2.0] | provenance | | +| enums.swift:44:10:44:23 | ... .case2(...) [case2.0] | enums.swift:44:18:44:22 | ExprPattern | provenance | | +| enums.swift:44:18:44:22 | ExprPattern | enums.swift:44:22:44:22 | x | provenance | | +| enums.swift:44:22:44:22 | x | enums.swift:45:14:45:14 | x | provenance | | +| enums.swift:52:9:52:13 | opt_x [some.0] | enums.swift:53:19:53:23 | opt_x [some.0] | provenance | | +| enums.swift:52:17:52:45 | ... .some(...) [some.0] | enums.swift:52:9:52:13 | opt_x [some.0] | provenance | | +| enums.swift:52:31:52:44 | source(...) | enums.swift:52:17:52:45 | ... .some(...) [some.0] | provenance | | +| enums.swift:53:11:53:15 | ... .some(...) [some.0] | enums.swift:53:11:53:15 | ExprPattern | provenance | | +| enums.swift:53:11:53:15 | ExprPattern | enums.swift:53:15:53:15 | x | provenance | | +| enums.swift:53:15:53:15 | x | enums.swift:54:10:54:10 | x | provenance | | +| enums.swift:53:19:53:23 | opt_x [some.0] | enums.swift:53:11:53:15 | ... .some(...) [some.0] | provenance | | +| enums.swift:58:9:58:13 | opt_x [some.0] | enums.swift:59:15:59:19 | opt_x [some.0] | provenance | | +| enums.swift:58:17:58:45 | ... .some(...) [some.0] | enums.swift:58:9:58:13 | opt_x [some.0] | provenance | | +| enums.swift:58:31:58:44 | source(...) | enums.swift:58:17:58:45 | ... .some(...) [some.0] | provenance | | +| enums.swift:59:11:59:19 | ... .some(...) [some.0] | enums.swift:59:11:59:19 | ExprPattern | provenance | | +| enums.swift:59:11:59:19 | ExprPattern | enums.swift:59:15:59:19 | opt_x | provenance | | +| enums.swift:59:15:59:19 | opt_x | enums.swift:60:10:60:14 | opt_x | provenance | | +| enums.swift:59:15:59:19 | opt_x [some.0] | enums.swift:59:11:59:19 | ... .some(...) [some.0] | provenance | | +| enums.swift:68:9:68:9 | e [foo.0] | enums.swift:69:12:69:12 | e [foo.0] | provenance | | +| enums.swift:68:9:68:9 | e [foo.0] | enums.swift:77:12:77:12 | e [foo.0] | provenance | | +| enums.swift:68:13:68:48 | ... .foo(...) [foo.0] | enums.swift:68:9:68:9 | e [foo.0] | provenance | | +| enums.swift:68:34:68:47 | source(...) | enums.swift:68:13:68:48 | ... .foo(...) [foo.0] | provenance | | +| enums.swift:69:12:69:12 | e [foo.0] | enums.swift:70:10:70:20 | ... .foo(...) [foo.0] | provenance | | +| enums.swift:70:10:70:20 | ... .foo(...) [foo.0] | enums.swift:70:15:70:19 | ExprPattern | provenance | | +| enums.swift:70:15:70:19 | ExprPattern | enums.swift:70:19:70:19 | x | provenance | | +| enums.swift:70:19:70:19 | x | enums.swift:71:14:71:14 | x | provenance | | +| enums.swift:77:12:77:12 | e [foo.0] | enums.swift:78:10:78:23 | ... .foo(...) [foo.0] | provenance | | +| enums.swift:78:10:78:23 | ... .foo(...) [foo.0] | enums.swift:78:18:78:22 | ExprPattern | provenance | | +| enums.swift:78:18:78:22 | ExprPattern | enums.swift:78:22:78:22 | x | provenance | | +| enums.swift:78:22:78:22 | x | enums.swift:79:14:79:14 | x | provenance | | | implicit-self.swift:16:9:16:12 | [post] self [x] | implicit-self.swift:17:14:17:17 | self [x] | provenance | | | implicit-self.swift:16:9:16:14 | ... .x | implicit-self.swift:16:9:16:12 | [post] self [x] | provenance | | | implicit-self.swift:16:18:16:31 | source(...) | implicit-self.swift:16:9:16:14 | ... .x | provenance | | @@ -231,6 +283,64 @@ nodes | calls.swift:117:18:117:22 | field | semmle.label | field | | calls.swift:123:18:123:21 | self [field] | semmle.label | self [field] | | calls.swift:123:18:123:27 | ... .field | semmle.label | ... .field | +| enums.swift:7:9:7:9 | e [case1.0] | semmle.label | e [case1.0] | +| enums.swift:7:13:7:35 | ... .case1(...) [case1.0] | semmle.label | ... .case1(...) [case1.0] | +| enums.swift:7:21:7:34 | source(...) | semmle.label | source(...) | +| enums.swift:9:12:9:12 | e [case1.0] | semmle.label | e [case1.0] | +| enums.swift:10:10:10:23 | ... .case1(...) [case1.0] | semmle.label | ... .case1(...) [case1.0] | +| enums.swift:10:18:10:22 | ExprPattern | semmle.label | ExprPattern | +| enums.swift:10:22:10:22 | x | semmle.label | x | +| enums.swift:11:14:11:14 | x | semmle.label | x | +| enums.swift:18:9:18:9 | e [case1.0] | semmle.label | e [case1.0] | +| enums.swift:18:13:18:35 | ... .case1(...) [case1.0] | semmle.label | ... .case1(...) [case1.0] | +| enums.swift:18:21:18:34 | source(...) | semmle.label | source(...) | +| enums.swift:20:12:20:12 | e [case1.0] | semmle.label | e [case1.0] | +| enums.swift:21:10:21:22 | ... .case1(...) [case1.0] | semmle.label | ... .case1(...) [case1.0] | +| enums.swift:21:17:21:21 | ExprPattern | semmle.label | ExprPattern | +| enums.swift:21:21:21:21 | x | semmle.label | x | +| enums.swift:22:14:22:14 | x | semmle.label | x | +| enums.swift:35:9:35:9 | e [case2.0] | semmle.label | e [case2.0] | +| enums.swift:35:13:35:35 | ... .case2(...) [case2.0] | semmle.label | ... .case2(...) [case2.0] | +| enums.swift:35:21:35:34 | source(...) | semmle.label | source(...) | +| enums.swift:36:12:36:12 | e [case2.0] | semmle.label | e [case2.0] | +| enums.swift:39:10:39:23 | ... .case2(...) [case2.0] | semmle.label | ... .case2(...) [case2.0] | +| enums.swift:39:18:39:22 | ExprPattern | semmle.label | ExprPattern | +| enums.swift:39:22:39:22 | x | semmle.label | x | +| enums.swift:40:14:40:14 | x | semmle.label | x | +| enums.swift:43:12:43:12 | e [case2.0] | semmle.label | e [case2.0] | +| enums.swift:44:10:44:23 | ... .case2(...) [case2.0] | semmle.label | ... .case2(...) [case2.0] | +| enums.swift:44:18:44:22 | ExprPattern | semmle.label | ExprPattern | +| enums.swift:44:22:44:22 | x | semmle.label | x | +| enums.swift:45:14:45:14 | x | semmle.label | x | +| enums.swift:52:9:52:13 | opt_x [some.0] | semmle.label | opt_x [some.0] | +| enums.swift:52:17:52:45 | ... .some(...) [some.0] | semmle.label | ... .some(...) [some.0] | +| enums.swift:52:31:52:44 | source(...) | semmle.label | source(...) | +| enums.swift:53:11:53:15 | ... .some(...) [some.0] | semmle.label | ... .some(...) [some.0] | +| enums.swift:53:11:53:15 | ExprPattern | semmle.label | ExprPattern | +| enums.swift:53:15:53:15 | x | semmle.label | x | +| enums.swift:53:19:53:23 | opt_x [some.0] | semmle.label | opt_x [some.0] | +| enums.swift:54:10:54:10 | x | semmle.label | x | +| enums.swift:58:9:58:13 | opt_x [some.0] | semmle.label | opt_x [some.0] | +| enums.swift:58:17:58:45 | ... .some(...) [some.0] | semmle.label | ... .some(...) [some.0] | +| enums.swift:58:31:58:44 | source(...) | semmle.label | source(...) | +| enums.swift:59:11:59:19 | ... .some(...) [some.0] | semmle.label | ... .some(...) [some.0] | +| enums.swift:59:11:59:19 | ExprPattern | semmle.label | ExprPattern | +| enums.swift:59:15:59:19 | opt_x | semmle.label | opt_x | +| enums.swift:59:15:59:19 | opt_x [some.0] | semmle.label | opt_x [some.0] | +| enums.swift:60:10:60:14 | opt_x | semmle.label | opt_x | +| enums.swift:68:9:68:9 | e [foo.0] | semmle.label | e [foo.0] | +| enums.swift:68:13:68:48 | ... .foo(...) [foo.0] | semmle.label | ... .foo(...) [foo.0] | +| enums.swift:68:34:68:47 | source(...) | semmle.label | source(...) | +| enums.swift:69:12:69:12 | e [foo.0] | semmle.label | e [foo.0] | +| enums.swift:70:10:70:20 | ... .foo(...) [foo.0] | semmle.label | ... .foo(...) [foo.0] | +| enums.swift:70:15:70:19 | ExprPattern | semmle.label | ExprPattern | +| enums.swift:70:19:70:19 | x | semmle.label | x | +| enums.swift:71:14:71:14 | x | semmle.label | x | +| enums.swift:77:12:77:12 | e [foo.0] | semmle.label | e [foo.0] | +| enums.swift:78:10:78:23 | ... .foo(...) [foo.0] | semmle.label | ... .foo(...) [foo.0] | +| enums.swift:78:18:78:22 | ExprPattern | semmle.label | ExprPattern | +| enums.swift:78:22:78:22 | x | semmle.label | x | +| enums.swift:79:14:79:14 | x | semmle.label | x | | implicit-self.swift:16:9:16:12 | [post] self [x] | semmle.label | [post] self [x] | | implicit-self.swift:16:9:16:14 | ... .x | semmle.label | ... .x | | implicit-self.swift:16:18:16:31 | source(...) | semmle.label | source(...) | @@ -440,6 +550,14 @@ testFailures | calls.swift:111:18:111:27 | ... .field | calls.swift:75:21:75:34 | source(...) | calls.swift:111:18:111:27 | ... .field | $@ | calls.swift:75:21:75:34 | source(...) | source(...) | | calls.swift:117:18:117:22 | field | calls.swift:75:21:75:34 | source(...) | calls.swift:117:18:117:22 | field | $@ | calls.swift:75:21:75:34 | source(...) | source(...) | | calls.swift:123:18:123:27 | ... .field | calls.swift:75:21:75:34 | source(...) | calls.swift:123:18:123:27 | ... .field | $@ | calls.swift:75:21:75:34 | source(...) | source(...) | +| enums.swift:11:14:11:14 | x | enums.swift:7:21:7:34 | source(...) | enums.swift:11:14:11:14 | x | $@ | enums.swift:7:21:7:34 | source(...) | source(...) | +| enums.swift:22:14:22:14 | x | enums.swift:18:21:18:34 | source(...) | enums.swift:22:14:22:14 | x | $@ | enums.swift:18:21:18:34 | source(...) | source(...) | +| enums.swift:40:14:40:14 | x | enums.swift:35:21:35:34 | source(...) | enums.swift:40:14:40:14 | x | $@ | enums.swift:35:21:35:34 | source(...) | source(...) | +| enums.swift:45:14:45:14 | x | enums.swift:35:21:35:34 | source(...) | enums.swift:45:14:45:14 | x | $@ | enums.swift:35:21:35:34 | source(...) | source(...) | +| enums.swift:54:10:54:10 | x | enums.swift:52:31:52:44 | source(...) | enums.swift:54:10:54:10 | x | $@ | enums.swift:52:31:52:44 | source(...) | source(...) | +| enums.swift:60:10:60:14 | opt_x | enums.swift:58:31:58:44 | source(...) | enums.swift:60:10:60:14 | opt_x | $@ | enums.swift:58:31:58:44 | source(...) | source(...) | +| enums.swift:71:14:71:14 | x | enums.swift:68:34:68:47 | source(...) | enums.swift:71:14:71:14 | x | $@ | enums.swift:68:34:68:47 | source(...) | source(...) | +| enums.swift:79:14:79:14 | x | enums.swift:68:34:68:47 | source(...) | enums.swift:79:14:79:14 | x | $@ | enums.swift:68:34:68:47 | source(...) | source(...) | | implicit-self.swift:17:14:17:19 | ... .x | implicit-self.swift:16:18:16:31 | source(...) | implicit-self.swift:17:14:17:19 | ... .x | $@ | implicit-self.swift:16:18:16:31 | source(...) | source(...) | | implicit-self.swift:23:14:23:14 | x | implicit-self.swift:22:13:22:26 | source(...) | implicit-self.swift:23:14:23:14 | x | $@ | implicit-self.swift:22:13:22:26 | source(...) | source(...) | | implicit-self.swift:29:14:29:19 | ... .x | implicit-self.swift:28:13:28:26 | source(...) | implicit-self.swift:29:14:29:19 | ... .x | $@ | implicit-self.swift:28:13:28:26 | source(...) | source(...) | From ce622dbf04919cc7e1d819ec085c9c47dcd9d403 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 5 Oct 2026 13:12:54 +0200 Subject: [PATCH 03/18] unified: Model postfix "!" as a read step --- .../codeql/unified/internal/dataflow/DataFlowPluginSwift.qll | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll index fdabcc6eee71..1025a2d08ed0 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll @@ -26,11 +26,10 @@ private class SwiftDataFlowPlugin extends DataFlowPlugin { node2.isResultValue(call) ) or - // Taint flow through unary "!" (TODO: model as a read of Optional.some, possibly with implicit taint read) exists(UnaryExpr expr | expr.getOperator().(PostfixOperator).getValue() = "!" and node1.isResultValue(expr.getOperand()) and - step.taint() and + step.readName("some.0") and node2.isResultValue(expr) ) or From a58747a7518c72828343998d515c321af2428344 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 5 Oct 2026 13:13:27 +0200 Subject: [PATCH 04/18] unified: Model more unary and cast operators --- .../internal/dataflow/DataFlowPluginSwift.qll | 24 ++++ .../test/library-tests/dataflow/test.expected | 117 ++++++++++++++++++ .../ql/test/library-tests/dataflow/test.swift | 37 ++++++ 3 files changed, 178 insertions(+) diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll index 1025a2d08ed0..55cc3b001bc7 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll @@ -31,6 +31,30 @@ private class SwiftDataFlowPlugin extends DataFlowPlugin { node1.isResultValue(expr.getOperand()) and step.readName("some.0") and node2.isResultValue(expr) + or + expr.getOperator().(PrefixOperator).getValue() = ["try", "try!", "await"] and + node1.isResultValue(expr.getOperand()) and + step.value() and + node2.isResultValue(expr) + or + expr.getOperator().(PrefixOperator).getValue() = "try?" and + node1.isResultValue(expr.getOperand()) and + step.storeName("some.0") and + node2.isResultValue(expr) + ) + or + exists(TypeCastExpr expr | + // The `as?` type cast boxes the incoming value in Optional depending on whether the type cast succeeded + expr.getOperator().getValue() = "as?" and + node1.isResultValue(expr.getExpr()) and + step.storeName("some.0") and + node2.isResultValue(expr) + or + // Safe upcast conversion ("as") and downcast-or-throw ("as!") propagate the value directly + expr.getOperator().getValue() = ["as", "as!"] and + node1.isResultValue(expr.getExpr()) and + step.value() and + node2.isResultValue(expr) ) or // Taint flow through URL(string: x). TODO: Model with MaD and flow summaries diff --git a/unified/ql/test/library-tests/dataflow/test.expected b/unified/ql/test/library-tests/dataflow/test.expected index 03ff2e596e8c..6f04234517e1 100644 --- a/unified/ql/test/library-tests/dataflow/test.expected +++ b/unified/ql/test/library-tests/dataflow/test.expected @@ -234,6 +234,54 @@ edges | test.swift:182:13:182:27 | source(...) | test.swift:182:9:182:9 | x | provenance | | | test.swift:183:9:183:9 | y | test.swift:186:10:186:10 | y | provenance | | | test.swift:183:13:183:27 | source(...) | test.swift:183:9:183:9 | y | provenance | | +| test.swift:189:22:189:22 | s | test.swift:189:58:189:58 | s | provenance | | +| test.swift:191:25:191:25 | s | test.swift:191:62:191:62 | s | provenance | | +| test.swift:193:30:193:30 | s | test.swift:193:73:193:73 | s | provenance | | +| test.swift:196:16:196:45 | asyncIdentity(...) | test.swift:196:10:196:45 | await ... | provenance | | +| test.swift:196:30:196:44 | source(...) | test.swift:189:22:189:22 | s | provenance | | +| test.swift:196:30:196:44 | source(...) | test.swift:196:16:196:45 | asyncIdentity(...) | provenance | | +| test.swift:200:14:200:46 | throwingIdentity(...) | test.swift:200:10:200:46 | try ... | provenance | | +| test.swift:200:31:200:45 | source(...) | test.swift:191:25:191:25 | s | provenance | | +| test.swift:200:31:200:45 | source(...) | test.swift:200:14:200:46 | throwingIdentity(...) | provenance | | +| test.swift:201:9:201:11 | opt [some.0] | test.swift:202:12:202:14 | opt [some.0] | provenance | | +| test.swift:201:15:201:52 | try? ... [some.0] | test.swift:201:9:201:11 | opt [some.0] | provenance | | +| test.swift:201:20:201:52 | throwingIdentity(...) | test.swift:201:15:201:52 | try? ... [some.0] | provenance | | +| test.swift:201:37:201:51 | source(...) | test.swift:191:25:191:25 | s | provenance | | +| test.swift:201:37:201:51 | source(...) | test.swift:201:20:201:52 | throwingIdentity(...) | provenance | | +| test.swift:202:8:202:14 | ... .some(...) [some.0] | test.swift:202:8:202:14 | ExprPattern | provenance | | +| test.swift:202:8:202:14 | ExprPattern | test.swift:202:12:202:14 | opt | provenance | | +| test.swift:202:12:202:14 | opt | test.swift:203:14:203:16 | opt | provenance | | +| test.swift:202:12:202:14 | opt [some.0] | test.swift:202:8:202:14 | ... .some(...) [some.0] | provenance | | +| test.swift:205:15:205:47 | throwingIdentity(...) | test.swift:205:10:205:47 | try! ... | provenance | | +| test.swift:205:32:205:46 | source(...) | test.swift:191:25:191:25 | s | provenance | | +| test.swift:205:32:205:46 | source(...) | test.swift:205:15:205:47 | throwingIdentity(...) | provenance | | +| test.swift:209:20:209:57 | asyncThrowingIdentity(...) | test.swift:209:10:209:57 | try ... | provenance | | +| test.swift:209:42:209:56 | source(...) | test.swift:193:30:193:30 | s | provenance | | +| test.swift:209:42:209:56 | source(...) | test.swift:209:20:209:57 | asyncThrowingIdentity(...) | provenance | | +| test.swift:210:9:210:11 | opt [some.0] | test.swift:211:12:211:14 | opt [some.0] | provenance | | +| test.swift:210:15:210:63 | try? ... [some.0] | test.swift:210:9:210:11 | opt [some.0] | provenance | | +| test.swift:210:20:210:63 | await ... | test.swift:210:15:210:63 | try? ... [some.0] | provenance | | +| test.swift:210:26:210:63 | asyncThrowingIdentity(...) | test.swift:210:20:210:63 | await ... | provenance | | +| test.swift:210:48:210:62 | source(...) | test.swift:193:30:193:30 | s | provenance | | +| test.swift:210:48:210:62 | source(...) | test.swift:210:26:210:63 | asyncThrowingIdentity(...) | provenance | | +| test.swift:211:8:211:14 | ... .some(...) [some.0] | test.swift:211:8:211:14 | ExprPattern | provenance | | +| test.swift:211:8:211:14 | ExprPattern | test.swift:211:12:211:14 | opt | provenance | | +| test.swift:211:12:211:14 | opt | test.swift:212:14:212:16 | opt | provenance | | +| test.swift:211:12:211:14 | opt [some.0] | test.swift:211:8:211:14 | ... .some(...) [some.0] | provenance | | +| test.swift:214:21:214:58 | asyncThrowingIdentity(...) | test.swift:214:10:214:58 | try! ... | provenance | | +| test.swift:214:43:214:57 | source(...) | test.swift:193:30:193:30 | s | provenance | | +| test.swift:214:43:214:57 | source(...) | test.swift:214:21:214:58 | asyncThrowingIdentity(...) | provenance | | +| test.swift:218:9:218:9 | x | test.swift:219:10:219:10 | x | provenance | | +| test.swift:218:9:218:9 | x | test.swift:220:10:220:10 | x | provenance | | +| test.swift:218:9:218:9 | x | test.swift:221:16:221:16 | x | provenance | | +| test.swift:218:13:218:27 | source(...) | test.swift:218:9:218:9 | x | provenance | | +| test.swift:219:10:219:10 | x | test.swift:219:10:219:20 | TypeCastExpr | provenance | | +| test.swift:220:10:220:10 | x | test.swift:220:10:220:21 | TypeCastExpr | provenance | | +| test.swift:221:8:221:12 | ... .some(...) [some.0] | test.swift:221:8:221:12 | ExprPattern | provenance | | +| test.swift:221:8:221:12 | ExprPattern | test.swift:221:12:221:12 | y | provenance | | +| test.swift:221:12:221:12 | y | test.swift:222:14:222:14 | y | provenance | | +| test.swift:221:16:221:16 | x | test.swift:221:16:221:27 | TypeCastExpr [some.0] | provenance | | +| test.swift:221:16:221:27 | TypeCastExpr [some.0] | test.swift:221:8:221:12 | ... .some(...) [some.0] | provenance | | nodes | calls.swift:7:19:7:19 | x | semmle.label | x | | calls.swift:8:14:8:14 | x | semmle.label | x | @@ -527,9 +575,68 @@ nodes | test.swift:183:13:183:27 | source(...) | semmle.label | source(...) | | test.swift:185:10:185:10 | x | semmle.label | x | | test.swift:186:10:186:10 | y | semmle.label | y | +| test.swift:189:22:189:22 | s | semmle.label | s | +| test.swift:189:58:189:58 | s | semmle.label | s | +| test.swift:191:25:191:25 | s | semmle.label | s | +| test.swift:191:62:191:62 | s | semmle.label | s | +| test.swift:193:30:193:30 | s | semmle.label | s | +| test.swift:193:73:193:73 | s | semmle.label | s | +| test.swift:196:10:196:45 | await ... | semmle.label | await ... | +| test.swift:196:16:196:45 | asyncIdentity(...) | semmle.label | asyncIdentity(...) | +| test.swift:196:30:196:44 | source(...) | semmle.label | source(...) | +| test.swift:200:10:200:46 | try ... | semmle.label | try ... | +| test.swift:200:14:200:46 | throwingIdentity(...) | semmle.label | throwingIdentity(...) | +| test.swift:200:31:200:45 | source(...) | semmle.label | source(...) | +| test.swift:201:9:201:11 | opt [some.0] | semmle.label | opt [some.0] | +| test.swift:201:15:201:52 | try? ... [some.0] | semmle.label | try? ... [some.0] | +| test.swift:201:20:201:52 | throwingIdentity(...) | semmle.label | throwingIdentity(...) | +| test.swift:201:37:201:51 | source(...) | semmle.label | source(...) | +| test.swift:202:8:202:14 | ... .some(...) [some.0] | semmle.label | ... .some(...) [some.0] | +| test.swift:202:8:202:14 | ExprPattern | semmle.label | ExprPattern | +| test.swift:202:12:202:14 | opt | semmle.label | opt | +| test.swift:202:12:202:14 | opt [some.0] | semmle.label | opt [some.0] | +| test.swift:203:14:203:16 | opt | semmle.label | opt | +| test.swift:205:10:205:47 | try! ... | semmle.label | try! ... | +| test.swift:205:15:205:47 | throwingIdentity(...) | semmle.label | throwingIdentity(...) | +| test.swift:205:32:205:46 | source(...) | semmle.label | source(...) | +| test.swift:209:10:209:57 | try ... | semmle.label | try ... | +| test.swift:209:20:209:57 | asyncThrowingIdentity(...) | semmle.label | asyncThrowingIdentity(...) | +| test.swift:209:42:209:56 | source(...) | semmle.label | source(...) | +| test.swift:210:9:210:11 | opt [some.0] | semmle.label | opt [some.0] | +| test.swift:210:15:210:63 | try? ... [some.0] | semmle.label | try? ... [some.0] | +| test.swift:210:20:210:63 | await ... | semmle.label | await ... | +| test.swift:210:26:210:63 | asyncThrowingIdentity(...) | semmle.label | asyncThrowingIdentity(...) | +| test.swift:210:48:210:62 | source(...) | semmle.label | source(...) | +| test.swift:211:8:211:14 | ... .some(...) [some.0] | semmle.label | ... .some(...) [some.0] | +| test.swift:211:8:211:14 | ExprPattern | semmle.label | ExprPattern | +| test.swift:211:12:211:14 | opt | semmle.label | opt | +| test.swift:211:12:211:14 | opt [some.0] | semmle.label | opt [some.0] | +| test.swift:212:14:212:16 | opt | semmle.label | opt | +| test.swift:214:10:214:58 | try! ... | semmle.label | try! ... | +| test.swift:214:21:214:58 | asyncThrowingIdentity(...) | semmle.label | asyncThrowingIdentity(...) | +| test.swift:214:43:214:57 | source(...) | semmle.label | source(...) | +| test.swift:218:9:218:9 | x | semmle.label | x | +| test.swift:218:13:218:27 | source(...) | semmle.label | source(...) | +| test.swift:219:10:219:10 | x | semmle.label | x | +| test.swift:219:10:219:20 | TypeCastExpr | semmle.label | TypeCastExpr | +| test.swift:220:10:220:10 | x | semmle.label | x | +| test.swift:220:10:220:21 | TypeCastExpr | semmle.label | TypeCastExpr | +| test.swift:221:8:221:12 | ... .some(...) [some.0] | semmle.label | ... .some(...) [some.0] | +| test.swift:221:8:221:12 | ExprPattern | semmle.label | ExprPattern | +| test.swift:221:12:221:12 | y | semmle.label | y | +| test.swift:221:16:221:16 | x | semmle.label | x | +| test.swift:221:16:221:27 | TypeCastExpr [some.0] | semmle.label | TypeCastExpr [some.0] | +| test.swift:222:14:222:14 | y | semmle.label | y | subpaths | calls.swift:31:17:31:30 | source(...) | calls.swift:28:19:28:19 | x | calls.swift:29:16:29:24 | ... + ... | calls.swift:31:10:31:31 | target(...) | | calls.swift:32:17:32:30 | source(...) | calls.swift:28:19:28:19 | x | calls.swift:29:16:29:24 | ... + ... | calls.swift:32:10:32:31 | target(...) | +| test.swift:187:30:187:44 | source(...) | test.swift:180:22:180:22 | s | test.swift:180:58:180:58 | s | test.swift:187:16:187:45 | asyncIdentity(...) | +| test.swift:191:31:191:45 | source(...) | test.swift:182:25:182:25 | s | test.swift:182:62:182:62 | s | test.swift:191:14:191:46 | throwingIdentity(...) | +| test.swift:192:37:192:51 | source(...) | test.swift:182:25:182:25 | s | test.swift:182:62:182:62 | s | test.swift:192:20:192:52 | throwingIdentity(...) | +| test.swift:196:32:196:46 | source(...) | test.swift:182:25:182:25 | s | test.swift:182:62:182:62 | s | test.swift:196:15:196:47 | throwingIdentity(...) | +| test.swift:200:42:200:56 | source(...) | test.swift:184:30:184:30 | s | test.swift:184:73:184:73 | s | test.swift:200:20:200:57 | asyncThrowingIdentity(...) | +| test.swift:201:48:201:62 | source(...) | test.swift:184:30:184:30 | s | test.swift:184:73:184:73 | s | test.swift:201:26:201:63 | asyncThrowingIdentity(...) | +| test.swift:205:43:205:57 | source(...) | test.swift:184:30:184:30 | s | test.swift:184:73:184:73 | s | test.swift:205:21:205:58 | asyncThrowingIdentity(...) | testFailures #select | calls.swift:8:14:8:14 | x | calls.swift:10:12:10:25 | source(...) | calls.swift:8:14:8:14 | x | $@ | calls.swift:10:12:10:25 | source(...) | source(...) | @@ -606,3 +713,13 @@ testFailures | test.swift:177:10:177:10 | b | test.swift:175:52:175:66 | source(...) | test.swift:177:10:177:10 | b | $@ | test.swift:175:52:175:66 | source(...) | source(...) | | test.swift:185:10:185:10 | x | test.swift:182:13:182:27 | source(...) | test.swift:185:10:185:10 | x | $@ | test.swift:182:13:182:27 | source(...) | source(...) | | test.swift:186:10:186:10 | y | test.swift:183:13:183:27 | source(...) | test.swift:186:10:186:10 | y | $@ | test.swift:183:13:183:27 | source(...) | source(...) | +| test.swift:196:10:196:45 | await ... | test.swift:196:30:196:44 | source(...) | test.swift:196:10:196:45 | await ... | $@ | test.swift:196:30:196:44 | source(...) | source(...) | +| test.swift:200:10:200:46 | try ... | test.swift:200:31:200:45 | source(...) | test.swift:200:10:200:46 | try ... | $@ | test.swift:200:31:200:45 | source(...) | source(...) | +| test.swift:203:14:203:16 | opt | test.swift:201:37:201:51 | source(...) | test.swift:203:14:203:16 | opt | $@ | test.swift:201:37:201:51 | source(...) | source(...) | +| test.swift:205:10:205:47 | try! ... | test.swift:205:32:205:46 | source(...) | test.swift:205:10:205:47 | try! ... | $@ | test.swift:205:32:205:46 | source(...) | source(...) | +| test.swift:209:10:209:57 | try ... | test.swift:209:42:209:56 | source(...) | test.swift:209:10:209:57 | try ... | $@ | test.swift:209:42:209:56 | source(...) | source(...) | +| test.swift:212:14:212:16 | opt | test.swift:210:48:210:62 | source(...) | test.swift:212:14:212:16 | opt | $@ | test.swift:210:48:210:62 | source(...) | source(...) | +| test.swift:214:10:214:58 | try! ... | test.swift:214:43:214:57 | source(...) | test.swift:214:10:214:58 | try! ... | $@ | test.swift:214:43:214:57 | source(...) | source(...) | +| test.swift:219:10:219:20 | TypeCastExpr | test.swift:218:13:218:27 | source(...) | test.swift:219:10:219:20 | TypeCastExpr | $@ | test.swift:218:13:218:27 | source(...) | source(...) | +| test.swift:220:10:220:21 | TypeCastExpr | test.swift:218:13:218:27 | source(...) | test.swift:220:10:220:21 | TypeCastExpr | $@ | test.swift:218:13:218:27 | source(...) | source(...) | +| test.swift:222:14:222:14 | y | test.swift:218:13:218:27 | source(...) | test.swift:222:14:222:14 | y | $@ | test.swift:218:13:218:27 | source(...) | source(...) | diff --git a/unified/ql/test/library-tests/dataflow/test.swift b/unified/ql/test/library-tests/dataflow/test.swift index b817497a1e57..74d0a2829ebf 100644 --- a/unified/ql/test/library-tests/dataflow/test.swift +++ b/unified/ql/test/library-tests/dataflow/test.swift @@ -185,3 +185,40 @@ func t20() { sink(x) // $ hasValueFlow=t20.1 sink(y) // $ hasValueFlow=t20.2 } + +func asyncIdentity(_ s: String) async -> String { return s } + +func throwingIdentity(_ s: String) throws -> String { return s } + +func asyncThrowingIdentity(_ s: String) async throws -> String { return s } + +func t21() async { + sink(await asyncIdentity(source("t21.1"))) // $ hasValueFlow=t21.1 +} + +func t22() throws { + sink(try throwingIdentity(source("t22.1"))) // $ hasValueFlow=t22.1 + let opt = try? throwingIdentity(source("t22.2")) + if let opt { + sink(opt) // $ hasValueFlow=t22.2 + } + sink(try! throwingIdentity(source("t22.3"))) // $ hasValueFlow=t22.3 +} + +func t23() async throws { + sink(try await asyncThrowingIdentity(source("t23.1"))) // $ hasValueFlow=t23.1 + let opt = try? await asyncThrowingIdentity(source("t23.2")) + if let opt { + sink(opt) // $ hasValueFlow=t23.2 + } + sink(try! await asyncThrowingIdentity(source("t23.3"))) // $ hasValueFlow=t23.3 +} + +func t24() { + let x = source("t24.1") + sink(x as String) // $ hasValueFlow=t24.1 + sink(x as! String) // $ hasValueFlow=t24.1 + if let y = x as? String { + sink(y) // $ hasValueFlow=t24.1 + } +} From 488f1319216cef0513c9f70969dd426a0d2d1a5b Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 5 Oct 2026 13:26:24 +0200 Subject: [PATCH 05/18] unified: Add basic flow through arrays Only array literals and for-in statements are handled --- .../codeql/unified/internal/dataflow/Content.qll | 11 ++++++++++- .../unified/internal/dataflow/DataFlowGraph.qll | 12 ++++++++++++ .../lib/codeql/unified/internal/dataflow/Step.qll | 8 ++++++++ .../ql/test/library-tests/dataflow/test.expected | 15 +++++++++++++++ unified/ql/test/library-tests/dataflow/test.swift | 8 ++++++++ 5 files changed, 53 insertions(+), 1 deletion(-) diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/Content.qll b/unified/ql/lib/codeql/unified/internal/dataflow/Content.qll index 77a138484138..1ee22eb2d57c 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/Content.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/Content.qll @@ -2,6 +2,7 @@ private import unified private import AllDataFlow private newtype TContent = + TArrayElement() or TNamedMember(string name) { name = any(Identifier id).getValue() or @@ -15,7 +16,13 @@ private newtype TContent = class Content extends TContent { string asNamedMember() { this = TNamedMember(result) } - string toString() { result = this.asNamedMember() } + predicate isArrayElement() { this = TArrayElement() } + + string toString() { + result = this.asNamedMember() + or + this.isArrayElement() and result = "ArrayElement" + } Location getLocation() { none() } } @@ -36,4 +43,6 @@ class ContentSet extends TContentSet { module ContentSet { ContentSet namedMember(string name) { result.asSingleton().asNamedMember() = name } + + ContentSet arrayElement() { result.asSingleton().isArrayElement() } } diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll index fd478feccd2f..f2d76644f8e5 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll @@ -134,6 +134,18 @@ predicate step(Node node1, Step step, Node node2) { node2.isIncomingValue(expr.getExpr()) ) or + exists(ArrayLiteral expr | + node1.isResultValue(expr.getAnElement()) and + step.store(ContentSet::arrayElement()) and + node2.isResultValue(expr) + ) + or + exists(ForEachStmt stmt | + node1.isResultValue(stmt.getIterable()) and + step.readArrayElement() and + node2.isIncomingValue(stmt.getPattern()) + ) + or none() // Temporarily disable compilation errors from unsatisfiable types } diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/Step.qll b/unified/ql/lib/codeql/unified/internal/dataflow/Step.qll index 65d2667a464e..ccf2354a6291 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/Step.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/Step.qll @@ -28,6 +28,10 @@ class Step extends TStep { pragma[nomagic] predicate readName(string name) { this.read(ContentSet::namedMember(name)) } + /** Holds if this represents a step reading an element from an array. */ + pragma[nomagic] + predicate readArrayElement() { this.read(ContentSet::arrayElement()) } + /** Holds if this represents a step storing into `contents`. */ predicate store(ContentSet contents) { this = TStoreStep(contents) } @@ -35,6 +39,10 @@ class Step extends TStep { pragma[nomagic] predicate storeName(string name) { this.store(ContentSet::namedMember(name)) } + /** Holds if this represents a step storing a value into an array. */ + pragma[nomagic] + predicate storeArrayElement() { this.store(ContentSet::arrayElement()) } + string toString() { this.value() and result = "value" or diff --git a/unified/ql/test/library-tests/dataflow/test.expected b/unified/ql/test/library-tests/dataflow/test.expected index 6f04234517e1..dfc56c04683c 100644 --- a/unified/ql/test/library-tests/dataflow/test.expected +++ b/unified/ql/test/library-tests/dataflow/test.expected @@ -282,6 +282,12 @@ edges | test.swift:221:12:221:12 | y | test.swift:222:14:222:14 | y | provenance | | | test.swift:221:16:221:16 | x | test.swift:221:16:221:27 | TypeCastExpr [some.0] | provenance | | | test.swift:221:16:221:27 | TypeCastExpr [some.0] | test.swift:221:8:221:12 | ... .some(...) [some.0] | provenance | | +| test.swift:227:9:227:9 | x [ArrayElement] | test.swift:228:20:228:20 | x [ArrayElement] | provenance | | +| test.swift:227:13:227:46 | ArrayLiteral [ArrayElement] | test.swift:227:9:227:9 | x [ArrayElement] | provenance | | +| test.swift:227:14:227:28 | source(...) | test.swift:227:13:227:46 | ArrayLiteral [ArrayElement] | provenance | | +| test.swift:227:31:227:45 | source(...) | test.swift:227:13:227:46 | ArrayLiteral [ArrayElement] | provenance | | +| test.swift:228:9:228:15 | element | test.swift:229:14:229:20 | element | provenance | | +| test.swift:228:20:228:20 | x [ArrayElement] | test.swift:228:9:228:15 | element | provenance | | nodes | calls.swift:7:19:7:19 | x | semmle.label | x | | calls.swift:8:14:8:14 | x | semmle.label | x | @@ -627,6 +633,13 @@ nodes | test.swift:221:16:221:16 | x | semmle.label | x | | test.swift:221:16:221:27 | TypeCastExpr [some.0] | semmle.label | TypeCastExpr [some.0] | | test.swift:222:14:222:14 | y | semmle.label | y | +| test.swift:227:9:227:9 | x [ArrayElement] | semmle.label | x [ArrayElement] | +| test.swift:227:13:227:46 | ArrayLiteral [ArrayElement] | semmle.label | ArrayLiteral [ArrayElement] | +| test.swift:227:14:227:28 | source(...) | semmle.label | source(...) | +| test.swift:227:31:227:45 | source(...) | semmle.label | source(...) | +| test.swift:228:9:228:15 | element | semmle.label | element | +| test.swift:228:20:228:20 | x [ArrayElement] | semmle.label | x [ArrayElement] | +| test.swift:229:14:229:20 | element | semmle.label | element | subpaths | calls.swift:31:17:31:30 | source(...) | calls.swift:28:19:28:19 | x | calls.swift:29:16:29:24 | ... + ... | calls.swift:31:10:31:31 | target(...) | | calls.swift:32:17:32:30 | source(...) | calls.swift:28:19:28:19 | x | calls.swift:29:16:29:24 | ... + ... | calls.swift:32:10:32:31 | target(...) | @@ -723,3 +736,5 @@ testFailures | test.swift:219:10:219:20 | TypeCastExpr | test.swift:218:13:218:27 | source(...) | test.swift:219:10:219:20 | TypeCastExpr | $@ | test.swift:218:13:218:27 | source(...) | source(...) | | test.swift:220:10:220:21 | TypeCastExpr | test.swift:218:13:218:27 | source(...) | test.swift:220:10:220:21 | TypeCastExpr | $@ | test.swift:218:13:218:27 | source(...) | source(...) | | test.swift:222:14:222:14 | y | test.swift:218:13:218:27 | source(...) | test.swift:222:14:222:14 | y | $@ | test.swift:218:13:218:27 | source(...) | source(...) | +| test.swift:229:14:229:20 | element | test.swift:227:14:227:28 | source(...) | test.swift:229:14:229:20 | element | $@ | test.swift:227:14:227:28 | source(...) | source(...) | +| test.swift:229:14:229:20 | element | test.swift:227:31:227:45 | source(...) | test.swift:229:14:229:20 | element | $@ | test.swift:227:31:227:45 | source(...) | source(...) | diff --git a/unified/ql/test/library-tests/dataflow/test.swift b/unified/ql/test/library-tests/dataflow/test.swift index 74d0a2829ebf..e9b2af820268 100644 --- a/unified/ql/test/library-tests/dataflow/test.swift +++ b/unified/ql/test/library-tests/dataflow/test.swift @@ -222,3 +222,11 @@ func t24() { sink(y) // $ hasValueFlow=t24.1 } } + +func t25() { + let x = [source("t25.1"), source("t25.2")] + for element in x { + sink(element) // $ hasValueFlow=t25.1 hasValueFlow=t25.2 + } + +} From a0c937c332b271daa042f91651d2a78fcb565322 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 5 Oct 2026 13:42:40 +0200 Subject: [PATCH 06/18] unified: Also treat "!" as a taint step and update test output Note that one of the annotations were moved because the location we report is different --- .../internal/dataflow/DataFlowPluginSwift.qll | 2 +- .../PathInjection/PathInjectionTest.expected | 13 +++++++++++++ .../CWE-022/PathInjection/testPathInjection.swift | 6 +++--- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll index 55cc3b001bc7..5ac92a80bbcf 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll @@ -29,7 +29,7 @@ private class SwiftDataFlowPlugin extends DataFlowPlugin { exists(UnaryExpr expr | expr.getOperator().(PostfixOperator).getValue() = "!" and node1.isResultValue(expr.getOperand()) and - step.readName("some.0") and + (step.readName("some.0") or step.taint()) and node2.isResultValue(expr) or expr.getOperator().(PrefixOperator).getValue() = ["try", "try!", "await"] and diff --git a/unified/ql/test/query-tests/security/CWE-022/PathInjection/PathInjectionTest.expected b/unified/ql/test/query-tests/security/CWE-022/PathInjection/PathInjectionTest.expected index f80c567968ce..bdee25f4a81d 100644 --- a/unified/ql/test/query-tests/security/CWE-022/PathInjection/PathInjectionTest.expected +++ b/unified/ql/test/query-tests/security/CWE-022/PathInjection/PathInjectionTest.expected @@ -61,6 +61,8 @@ | testPathInjection.swift:421:26:421:34 | remoteUrl | testPathInjection.swift:340:24:340:78 | String(...) | testPathInjection.swift:421:26:421:34 | remoteUrl | This path depends on a $@. | testPathInjection.swift:340:24:340:78 | String(...) | user-provided value | | testPathInjection.swift:422:30:422:41 | remoteString | testPathInjection.swift:340:24:340:78 | String(...) | testPathInjection.swift:422:30:422:41 | remoteString | This path depends on a $@. | testPathInjection.swift:340:24:340:78 | String(...) | user-provided value | | testPathInjection.swift:424:59:424:70 | remoteString | testPathInjection.swift:340:24:340:78 | String(...) | testPathInjection.swift:424:59:424:70 | remoteString | This path depends on a $@. | testPathInjection.swift:340:24:340:78 | String(...) | user-provided value | +| testPathInjection.swift:425:46:425:76 | TypeCastExpr | testPathInjection.swift:340:24:340:78 | String(...) | testPathInjection.swift:425:46:425:76 | TypeCastExpr | This path depends on a $@. | testPathInjection.swift:340:24:340:78 | String(...) | user-provided value | +| testPathInjection.swift:426:42:426:72 | TypeCastExpr | testPathInjection.swift:340:24:340:78 | String(...) | testPathInjection.swift:426:42:426:72 | TypeCastExpr | This path depends on a $@. | testPathInjection.swift:340:24:340:78 | String(...) | user-provided value | | testPathInjection.swift:436:25:436:33 | remoteUrl | testPathInjection.swift:340:24:340:78 | String(...) | testPathInjection.swift:436:25:436:33 | remoteUrl | This path depends on a $@. | testPathInjection.swift:340:24:340:78 | String(...) | user-provided value | | testPathInjection.swift:437:26:437:37 | remoteString | testPathInjection.swift:340:24:340:78 | String(...) | testPathInjection.swift:437:26:437:37 | remoteString | This path depends on a $@. | testPathInjection.swift:340:24:340:78 | String(...) | user-provided value | | testPathInjection.swift:441:28:441:39 | remoteString | testPathInjection.swift:340:24:340:78 | String(...) | testPathInjection.swift:441:28:441:39 | remoteString | This path depends on a $@. | testPathInjection.swift:340:24:340:78 | String(...) | user-provided value | @@ -125,6 +127,9 @@ edges | testPathInjection.swift:340:9:340:20 | remoteString | testPathInjection.swift:420:43:420:54 | remoteString | provenance | | | testPathInjection.swift:340:9:340:20 | remoteString | testPathInjection.swift:422:30:422:41 | remoteString | provenance | | | testPathInjection.swift:340:9:340:20 | remoteString | testPathInjection.swift:424:59:424:70 | remoteString | provenance | | +| testPathInjection.swift:340:9:340:20 | remoteString | testPathInjection.swift:425:46:425:57 | remoteString | provenance | | +| testPathInjection.swift:340:9:340:20 | remoteString | testPathInjection.swift:426:42:426:53 | remoteString | provenance | | +| testPathInjection.swift:340:9:340:20 | remoteString | testPathInjection.swift:436:25:436:33 | remoteUrl | provenance | | | testPathInjection.swift:340:9:340:20 | remoteString | testPathInjection.swift:437:26:437:37 | remoteString | provenance | | | testPathInjection.swift:340:9:340:20 | remoteString | testPathInjection.swift:441:28:441:39 | remoteString | provenance | | | testPathInjection.swift:340:9:340:20 | remoteString | testPathInjection.swift:443:32:443:43 | remoteString | provenance | | @@ -162,6 +167,10 @@ edges | testPathInjection.swift:342:9:342:19 | remoteNsUrl | testPathInjection.swift:410:26:410:36 | remoteNsUrl | provenance | | | testPathInjection.swift:342:9:342:19 | remoteNsUrl | testPathInjection.swift:412:52:412:62 | remoteNsUrl | provenance | | | testPathInjection.swift:342:37:342:48 | remoteString | testPathInjection.swift:342:9:342:19 | remoteNsUrl | provenance | | +| testPathInjection.swift:425:46:425:57 | remoteString | testPathInjection.swift:425:46:425:76 | TypeCastExpr | provenance | | +| testPathInjection.swift:426:42:426:53 | remoteString | testPathInjection.swift:426:42:426:72 | TypeCastExpr | provenance | | +| testPathInjection.swift:477:9:477:18 | remoteData | testPathInjection.swift:484:24:484:30 | buffer2 | provenance | | +| testPathInjection.swift:477:22:477:87 | Data(...) | testPathInjection.swift:477:9:477:18 | remoteData | provenance | | | testPathInjection.swift:504:9:504:20 | remoteString | testPathInjection.swift:510:37:510:48 | remoteString | provenance | | | testPathInjection.swift:504:9:504:20 | remoteString | testPathInjection.swift:512:33:512:44 | remoteString | provenance | | | testPathInjection.swift:504:24:504:78 | String(...) | testPathInjection.swift:504:9:504:20 | remoteString | provenance | | @@ -251,6 +260,10 @@ nodes | testPathInjection.swift:421:26:421:34 | remoteUrl | semmle.label | remoteUrl | | testPathInjection.swift:422:30:422:41 | remoteString | semmle.label | remoteString | | testPathInjection.swift:424:59:424:70 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:425:46:425:57 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:425:46:425:76 | TypeCastExpr | semmle.label | TypeCastExpr | +| testPathInjection.swift:426:42:426:53 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:426:42:426:72 | TypeCastExpr | semmle.label | TypeCastExpr | | testPathInjection.swift:436:25:436:33 | remoteUrl | semmle.label | remoteUrl | | testPathInjection.swift:437:26:437:37 | remoteString | semmle.label | remoteString | | testPathInjection.swift:441:28:441:39 | remoteString | semmle.label | remoteString | diff --git a/unified/ql/test/query-tests/security/CWE-022/PathInjection/testPathInjection.swift b/unified/ql/test/query-tests/security/CWE-022/PathInjection/testPathInjection.swift index 9ada50020f56..455f019bdc06 100644 --- a/unified/ql/test/query-tests/security/CWE-022/PathInjection/testPathInjection.swift +++ b/unified/ql/test/query-tests/security/CWE-022/PathInjection/testPathInjection.swift @@ -422,10 +422,10 @@ func test(buffer1: UnsafeMutablePointer, buffer2: UnsafeMutablePointer Date: Mon, 5 Oct 2026 13:52:29 +0200 Subject: [PATCH 07/18] unified: Manually add one-argument version of Data.init(contentsOf:) --- unified/ql/lib/ext/legacy-swift.model.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/unified/ql/lib/ext/legacy-swift.model.yml b/unified/ql/lib/ext/legacy-swift.model.yml index d44d441bcea7..1c69c303c757 100644 --- a/unified/ql/lib/ext/legacy-swift.model.yml +++ b/unified/ql/lib/ext/legacy-swift.model.yml @@ -40,6 +40,7 @@ extensions: - ["", "NSString", true, "init(contentsOfFile:usedEncoding:)", "", "", "ReturnValue", "local", "manual"] - ["", "FileManager", true, "contents(atPath:)", "", "", "ReturnValue", "local", "manual"] - ["", "Data", true, "init(contentsOf:options:)", "", "", "ReturnValue", "remote", "manual"] + - ["", "Data", true, "init(contentsOf:)", "", "", "ReturnValue", "remote", "manual"] - ["", "UISceneDelegate", true, "scene(_:continue:)", "", "", "Parameter[continue:]", "remote", "manual"] - ["", "UISceneDelegate", true, "scene(_:didUpdate:)", "", "", "Parameter[didUpdate:]", "remote", "manual"] - ["", "UISceneDelegate", true, "scene(_:openURLContexts:)", "", "", "Parameter[openURLContexts:]", "remote", "manual"] From 143aef9cf37d46f5c02729017847b2c64a2ae33c Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 5 Oct 2026 13:52:44 +0200 Subject: [PATCH 08/18] unified: Add path-injection specific step through 'path' --- unified/ql/src/queries/security/CWE-022/PathInjection.ql | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/unified/ql/src/queries/security/CWE-022/PathInjection.ql b/unified/ql/src/queries/security/CWE-022/PathInjection.ql index ecdd2ba3600f..20779510fc1b 100644 --- a/unified/ql/src/queries/security/CWE-022/PathInjection.ql +++ b/unified/ql/src/queries/security/CWE-022/PathInjection.ql @@ -46,7 +46,13 @@ module PathInjectionConfig implements DataFlow::ConfigSig { heuristicSink(node) } - predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { none() } + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + exists(MemberAccessExpr expr | + expr.getMemberName() = "path" and + node1.isResultValue(expr.getBase()) and + node2.isResultValue(expr) + ) + } predicate isBarrier(DataFlow::Node node) { // TODO: add barriers From 7c4a2e81d0667cfca97bb8cb1f31abf2ea4fc274 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 5 Oct 2026 14:00:05 +0200 Subject: [PATCH 09/18] unified: Dont show ExprPattern in path --- .../dataflow/DataFlowInstantiation.qll | 5 ++- .../test/library-tests/dataflow/test.expected | 44 +++++-------------- 2 files changed, 15 insertions(+), 34 deletions(-) diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll index c762ec0de7d0..af30891b77c6 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll @@ -153,7 +153,10 @@ module DataFlowInput implements InputSig { // Misc // additional predicate nodeIsVisible(Node node) { - node instanceof TValueNode + exists(Expr e | + node = TValueNode(e) and + not e instanceof ExprPattern + ) or node instanceof TStrictlyIncomingValue or diff --git a/unified/ql/test/library-tests/dataflow/test.expected b/unified/ql/test/library-tests/dataflow/test.expected index dfc56c04683c..5b84cf28d912 100644 --- a/unified/ql/test/library-tests/dataflow/test.expected +++ b/unified/ql/test/library-tests/dataflow/test.expected @@ -42,40 +42,34 @@ edges | enums.swift:7:13:7:35 | ... .case1(...) [case1.0] | enums.swift:7:9:7:9 | e [case1.0] | provenance | | | enums.swift:7:21:7:34 | source(...) | enums.swift:7:13:7:35 | ... .case1(...) [case1.0] | provenance | | | enums.swift:9:12:9:12 | e [case1.0] | enums.swift:10:10:10:23 | ... .case1(...) [case1.0] | provenance | | -| enums.swift:10:10:10:23 | ... .case1(...) [case1.0] | enums.swift:10:18:10:22 | ExprPattern | provenance | | -| enums.swift:10:18:10:22 | ExprPattern | enums.swift:10:22:10:22 | x | provenance | | +| enums.swift:10:10:10:23 | ... .case1(...) [case1.0] | enums.swift:10:22:10:22 | x | provenance | | | enums.swift:10:22:10:22 | x | enums.swift:11:14:11:14 | x | provenance | | | enums.swift:18:9:18:9 | e [case1.0] | enums.swift:20:12:20:12 | e [case1.0] | provenance | | | enums.swift:18:13:18:35 | ... .case1(...) [case1.0] | enums.swift:18:9:18:9 | e [case1.0] | provenance | | | enums.swift:18:21:18:34 | source(...) | enums.swift:18:13:18:35 | ... .case1(...) [case1.0] | provenance | | | enums.swift:20:12:20:12 | e [case1.0] | enums.swift:21:10:21:22 | ... .case1(...) [case1.0] | provenance | | -| enums.swift:21:10:21:22 | ... .case1(...) [case1.0] | enums.swift:21:17:21:21 | ExprPattern | provenance | | -| enums.swift:21:17:21:21 | ExprPattern | enums.swift:21:21:21:21 | x | provenance | | +| enums.swift:21:10:21:22 | ... .case1(...) [case1.0] | enums.swift:21:21:21:21 | x | provenance | | | enums.swift:21:21:21:21 | x | enums.swift:22:14:22:14 | x | provenance | | | enums.swift:35:9:35:9 | e [case2.0] | enums.swift:36:12:36:12 | e [case2.0] | provenance | | | enums.swift:35:9:35:9 | e [case2.0] | enums.swift:43:12:43:12 | e [case2.0] | provenance | | | enums.swift:35:13:35:35 | ... .case2(...) [case2.0] | enums.swift:35:9:35:9 | e [case2.0] | provenance | | | enums.swift:35:21:35:34 | source(...) | enums.swift:35:13:35:35 | ... .case2(...) [case2.0] | provenance | | | enums.swift:36:12:36:12 | e [case2.0] | enums.swift:39:10:39:23 | ... .case2(...) [case2.0] | provenance | | -| enums.swift:39:10:39:23 | ... .case2(...) [case2.0] | enums.swift:39:18:39:22 | ExprPattern | provenance | | -| enums.swift:39:18:39:22 | ExprPattern | enums.swift:39:22:39:22 | x | provenance | | +| enums.swift:39:10:39:23 | ... .case2(...) [case2.0] | enums.swift:39:22:39:22 | x | provenance | | | enums.swift:39:22:39:22 | x | enums.swift:40:14:40:14 | x | provenance | | | enums.swift:43:12:43:12 | e [case2.0] | enums.swift:44:10:44:23 | ... .case2(...) [case2.0] | provenance | | -| enums.swift:44:10:44:23 | ... .case2(...) [case2.0] | enums.swift:44:18:44:22 | ExprPattern | provenance | | -| enums.swift:44:18:44:22 | ExprPattern | enums.swift:44:22:44:22 | x | provenance | | +| enums.swift:44:10:44:23 | ... .case2(...) [case2.0] | enums.swift:44:22:44:22 | x | provenance | | | enums.swift:44:22:44:22 | x | enums.swift:45:14:45:14 | x | provenance | | | enums.swift:52:9:52:13 | opt_x [some.0] | enums.swift:53:19:53:23 | opt_x [some.0] | provenance | | | enums.swift:52:17:52:45 | ... .some(...) [some.0] | enums.swift:52:9:52:13 | opt_x [some.0] | provenance | | | enums.swift:52:31:52:44 | source(...) | enums.swift:52:17:52:45 | ... .some(...) [some.0] | provenance | | -| enums.swift:53:11:53:15 | ... .some(...) [some.0] | enums.swift:53:11:53:15 | ExprPattern | provenance | | -| enums.swift:53:11:53:15 | ExprPattern | enums.swift:53:15:53:15 | x | provenance | | +| enums.swift:53:11:53:15 | ... .some(...) [some.0] | enums.swift:53:15:53:15 | x | provenance | | | enums.swift:53:15:53:15 | x | enums.swift:54:10:54:10 | x | provenance | | | enums.swift:53:19:53:23 | opt_x [some.0] | enums.swift:53:11:53:15 | ... .some(...) [some.0] | provenance | | | enums.swift:58:9:58:13 | opt_x [some.0] | enums.swift:59:15:59:19 | opt_x [some.0] | provenance | | | enums.swift:58:17:58:45 | ... .some(...) [some.0] | enums.swift:58:9:58:13 | opt_x [some.0] | provenance | | | enums.swift:58:31:58:44 | source(...) | enums.swift:58:17:58:45 | ... .some(...) [some.0] | provenance | | -| enums.swift:59:11:59:19 | ... .some(...) [some.0] | enums.swift:59:11:59:19 | ExprPattern | provenance | | -| enums.swift:59:11:59:19 | ExprPattern | enums.swift:59:15:59:19 | opt_x | provenance | | +| enums.swift:59:11:59:19 | ... .some(...) [some.0] | enums.swift:59:15:59:19 | opt_x | provenance | | | enums.swift:59:15:59:19 | opt_x | enums.swift:60:10:60:14 | opt_x | provenance | | | enums.swift:59:15:59:19 | opt_x [some.0] | enums.swift:59:11:59:19 | ... .some(...) [some.0] | provenance | | | enums.swift:68:9:68:9 | e [foo.0] | enums.swift:69:12:69:12 | e [foo.0] | provenance | | @@ -83,12 +77,10 @@ edges | enums.swift:68:13:68:48 | ... .foo(...) [foo.0] | enums.swift:68:9:68:9 | e [foo.0] | provenance | | | enums.swift:68:34:68:47 | source(...) | enums.swift:68:13:68:48 | ... .foo(...) [foo.0] | provenance | | | enums.swift:69:12:69:12 | e [foo.0] | enums.swift:70:10:70:20 | ... .foo(...) [foo.0] | provenance | | -| enums.swift:70:10:70:20 | ... .foo(...) [foo.0] | enums.swift:70:15:70:19 | ExprPattern | provenance | | -| enums.swift:70:15:70:19 | ExprPattern | enums.swift:70:19:70:19 | x | provenance | | +| enums.swift:70:10:70:20 | ... .foo(...) [foo.0] | enums.swift:70:19:70:19 | x | provenance | | | enums.swift:70:19:70:19 | x | enums.swift:71:14:71:14 | x | provenance | | | enums.swift:77:12:77:12 | e [foo.0] | enums.swift:78:10:78:23 | ... .foo(...) [foo.0] | provenance | | -| enums.swift:78:10:78:23 | ... .foo(...) [foo.0] | enums.swift:78:18:78:22 | ExprPattern | provenance | | -| enums.swift:78:18:78:22 | ExprPattern | enums.swift:78:22:78:22 | x | provenance | | +| enums.swift:78:10:78:23 | ... .foo(...) [foo.0] | enums.swift:78:22:78:22 | x | provenance | | | enums.swift:78:22:78:22 | x | enums.swift:79:14:79:14 | x | provenance | | | implicit-self.swift:16:9:16:12 | [post] self [x] | implicit-self.swift:17:14:17:17 | self [x] | provenance | | | implicit-self.swift:16:9:16:14 | ... .x | implicit-self.swift:16:9:16:12 | [post] self [x] | provenance | | @@ -248,8 +240,7 @@ edges | test.swift:201:20:201:52 | throwingIdentity(...) | test.swift:201:15:201:52 | try? ... [some.0] | provenance | | | test.swift:201:37:201:51 | source(...) | test.swift:191:25:191:25 | s | provenance | | | test.swift:201:37:201:51 | source(...) | test.swift:201:20:201:52 | throwingIdentity(...) | provenance | | -| test.swift:202:8:202:14 | ... .some(...) [some.0] | test.swift:202:8:202:14 | ExprPattern | provenance | | -| test.swift:202:8:202:14 | ExprPattern | test.swift:202:12:202:14 | opt | provenance | | +| test.swift:202:8:202:14 | ... .some(...) [some.0] | test.swift:202:12:202:14 | opt | provenance | | | test.swift:202:12:202:14 | opt | test.swift:203:14:203:16 | opt | provenance | | | test.swift:202:12:202:14 | opt [some.0] | test.swift:202:8:202:14 | ... .some(...) [some.0] | provenance | | | test.swift:205:15:205:47 | throwingIdentity(...) | test.swift:205:10:205:47 | try! ... | provenance | | @@ -264,8 +255,7 @@ edges | test.swift:210:26:210:63 | asyncThrowingIdentity(...) | test.swift:210:20:210:63 | await ... | provenance | | | test.swift:210:48:210:62 | source(...) | test.swift:193:30:193:30 | s | provenance | | | test.swift:210:48:210:62 | source(...) | test.swift:210:26:210:63 | asyncThrowingIdentity(...) | provenance | | -| test.swift:211:8:211:14 | ... .some(...) [some.0] | test.swift:211:8:211:14 | ExprPattern | provenance | | -| test.swift:211:8:211:14 | ExprPattern | test.swift:211:12:211:14 | opt | provenance | | +| test.swift:211:8:211:14 | ... .some(...) [some.0] | test.swift:211:12:211:14 | opt | provenance | | | test.swift:211:12:211:14 | opt | test.swift:212:14:212:16 | opt | provenance | | | test.swift:211:12:211:14 | opt [some.0] | test.swift:211:8:211:14 | ... .some(...) [some.0] | provenance | | | test.swift:214:21:214:58 | asyncThrowingIdentity(...) | test.swift:214:10:214:58 | try! ... | provenance | | @@ -277,8 +267,7 @@ edges | test.swift:218:13:218:27 | source(...) | test.swift:218:9:218:9 | x | provenance | | | test.swift:219:10:219:10 | x | test.swift:219:10:219:20 | TypeCastExpr | provenance | | | test.swift:220:10:220:10 | x | test.swift:220:10:220:21 | TypeCastExpr | provenance | | -| test.swift:221:8:221:12 | ... .some(...) [some.0] | test.swift:221:8:221:12 | ExprPattern | provenance | | -| test.swift:221:8:221:12 | ExprPattern | test.swift:221:12:221:12 | y | provenance | | +| test.swift:221:8:221:12 | ... .some(...) [some.0] | test.swift:221:12:221:12 | y | provenance | | | test.swift:221:12:221:12 | y | test.swift:222:14:222:14 | y | provenance | | | test.swift:221:16:221:16 | x | test.swift:221:16:221:27 | TypeCastExpr [some.0] | provenance | | | test.swift:221:16:221:27 | TypeCastExpr [some.0] | test.swift:221:8:221:12 | ... .some(...) [some.0] | provenance | | @@ -342,7 +331,6 @@ nodes | enums.swift:7:21:7:34 | source(...) | semmle.label | source(...) | | enums.swift:9:12:9:12 | e [case1.0] | semmle.label | e [case1.0] | | enums.swift:10:10:10:23 | ... .case1(...) [case1.0] | semmle.label | ... .case1(...) [case1.0] | -| enums.swift:10:18:10:22 | ExprPattern | semmle.label | ExprPattern | | enums.swift:10:22:10:22 | x | semmle.label | x | | enums.swift:11:14:11:14 | x | semmle.label | x | | enums.swift:18:9:18:9 | e [case1.0] | semmle.label | e [case1.0] | @@ -350,7 +338,6 @@ nodes | enums.swift:18:21:18:34 | source(...) | semmle.label | source(...) | | enums.swift:20:12:20:12 | e [case1.0] | semmle.label | e [case1.0] | | enums.swift:21:10:21:22 | ... .case1(...) [case1.0] | semmle.label | ... .case1(...) [case1.0] | -| enums.swift:21:17:21:21 | ExprPattern | semmle.label | ExprPattern | | enums.swift:21:21:21:21 | x | semmle.label | x | | enums.swift:22:14:22:14 | x | semmle.label | x | | enums.swift:35:9:35:9 | e [case2.0] | semmle.label | e [case2.0] | @@ -358,19 +345,16 @@ nodes | enums.swift:35:21:35:34 | source(...) | semmle.label | source(...) | | enums.swift:36:12:36:12 | e [case2.0] | semmle.label | e [case2.0] | | enums.swift:39:10:39:23 | ... .case2(...) [case2.0] | semmle.label | ... .case2(...) [case2.0] | -| enums.swift:39:18:39:22 | ExprPattern | semmle.label | ExprPattern | | enums.swift:39:22:39:22 | x | semmle.label | x | | enums.swift:40:14:40:14 | x | semmle.label | x | | enums.swift:43:12:43:12 | e [case2.0] | semmle.label | e [case2.0] | | enums.swift:44:10:44:23 | ... .case2(...) [case2.0] | semmle.label | ... .case2(...) [case2.0] | -| enums.swift:44:18:44:22 | ExprPattern | semmle.label | ExprPattern | | enums.swift:44:22:44:22 | x | semmle.label | x | | enums.swift:45:14:45:14 | x | semmle.label | x | | enums.swift:52:9:52:13 | opt_x [some.0] | semmle.label | opt_x [some.0] | | enums.swift:52:17:52:45 | ... .some(...) [some.0] | semmle.label | ... .some(...) [some.0] | | enums.swift:52:31:52:44 | source(...) | semmle.label | source(...) | | enums.swift:53:11:53:15 | ... .some(...) [some.0] | semmle.label | ... .some(...) [some.0] | -| enums.swift:53:11:53:15 | ExprPattern | semmle.label | ExprPattern | | enums.swift:53:15:53:15 | x | semmle.label | x | | enums.swift:53:19:53:23 | opt_x [some.0] | semmle.label | opt_x [some.0] | | enums.swift:54:10:54:10 | x | semmle.label | x | @@ -378,7 +362,6 @@ nodes | enums.swift:58:17:58:45 | ... .some(...) [some.0] | semmle.label | ... .some(...) [some.0] | | enums.swift:58:31:58:44 | source(...) | semmle.label | source(...) | | enums.swift:59:11:59:19 | ... .some(...) [some.0] | semmle.label | ... .some(...) [some.0] | -| enums.swift:59:11:59:19 | ExprPattern | semmle.label | ExprPattern | | enums.swift:59:15:59:19 | opt_x | semmle.label | opt_x | | enums.swift:59:15:59:19 | opt_x [some.0] | semmle.label | opt_x [some.0] | | enums.swift:60:10:60:14 | opt_x | semmle.label | opt_x | @@ -387,12 +370,10 @@ nodes | enums.swift:68:34:68:47 | source(...) | semmle.label | source(...) | | enums.swift:69:12:69:12 | e [foo.0] | semmle.label | e [foo.0] | | enums.swift:70:10:70:20 | ... .foo(...) [foo.0] | semmle.label | ... .foo(...) [foo.0] | -| enums.swift:70:15:70:19 | ExprPattern | semmle.label | ExprPattern | | enums.swift:70:19:70:19 | x | semmle.label | x | | enums.swift:71:14:71:14 | x | semmle.label | x | | enums.swift:77:12:77:12 | e [foo.0] | semmle.label | e [foo.0] | | enums.swift:78:10:78:23 | ... .foo(...) [foo.0] | semmle.label | ... .foo(...) [foo.0] | -| enums.swift:78:18:78:22 | ExprPattern | semmle.label | ExprPattern | | enums.swift:78:22:78:22 | x | semmle.label | x | | enums.swift:79:14:79:14 | x | semmle.label | x | | implicit-self.swift:16:9:16:12 | [post] self [x] | semmle.label | [post] self [x] | @@ -598,7 +579,6 @@ nodes | test.swift:201:20:201:52 | throwingIdentity(...) | semmle.label | throwingIdentity(...) | | test.swift:201:37:201:51 | source(...) | semmle.label | source(...) | | test.swift:202:8:202:14 | ... .some(...) [some.0] | semmle.label | ... .some(...) [some.0] | -| test.swift:202:8:202:14 | ExprPattern | semmle.label | ExprPattern | | test.swift:202:12:202:14 | opt | semmle.label | opt | | test.swift:202:12:202:14 | opt [some.0] | semmle.label | opt [some.0] | | test.swift:203:14:203:16 | opt | semmle.label | opt | @@ -614,7 +594,6 @@ nodes | test.swift:210:26:210:63 | asyncThrowingIdentity(...) | semmle.label | asyncThrowingIdentity(...) | | test.swift:210:48:210:62 | source(...) | semmle.label | source(...) | | test.swift:211:8:211:14 | ... .some(...) [some.0] | semmle.label | ... .some(...) [some.0] | -| test.swift:211:8:211:14 | ExprPattern | semmle.label | ExprPattern | | test.swift:211:12:211:14 | opt | semmle.label | opt | | test.swift:211:12:211:14 | opt [some.0] | semmle.label | opt [some.0] | | test.swift:212:14:212:16 | opt | semmle.label | opt | @@ -628,7 +607,6 @@ nodes | test.swift:220:10:220:10 | x | semmle.label | x | | test.swift:220:10:220:21 | TypeCastExpr | semmle.label | TypeCastExpr | | test.swift:221:8:221:12 | ... .some(...) [some.0] | semmle.label | ... .some(...) [some.0] | -| test.swift:221:8:221:12 | ExprPattern | semmle.label | ExprPattern | | test.swift:221:12:221:12 | y | semmle.label | y | | test.swift:221:16:221:16 | x | semmle.label | x | | test.swift:221:16:221:27 | TypeCastExpr [some.0] | semmle.label | TypeCastExpr [some.0] | From 61fc5fd8a67c47069bce585ae389d25876e49012 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 7 Oct 2026 13:19:04 +0200 Subject: [PATCH 10/18] unified: Improve join order --- .../internal/dataflow/ConstructorPatterns.qll | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/ConstructorPatterns.qll b/unified/ql/lib/codeql/unified/internal/dataflow/ConstructorPatterns.qll index 58bcfed8e1cb..84c471141e50 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/ConstructorPatterns.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/ConstructorPatterns.qll @@ -24,15 +24,21 @@ private string getShortConstructorName(CallExpr call) { // so do not do this for Identifiers } +/** + * Holds if `call` targets a member called `name` and has the given `arity`. + */ +pragma[nomagic] +private predicate callSiteHasSignature(CallExpr call, string name, int arity) { + name = call.getCallee().(MemberAccessExpr).getMemberName() and + arity = call.getNumberOfArguments() +} + /** * Holds if a constructor pattern has the given short `name` and `arity`. */ pragma[nomagic] private predicate isSignatureUsedInConstructorPattern(string name, int arity) { - exists(ConstructorPattern ctor | - name = getShortConstructorName(ctor) and - arity = ctor.getNumberOfArguments() - ) + callSiteHasSignature(any(ConstructorPattern p), name, arity) } /** Holds if `callable` is an enum-case constructor */ @@ -50,12 +56,13 @@ private predicate assumeResolvesToEnumCaseConstructor(CallExpr call) { isEnumCaseConstructor(T::resolveCallTarget(call)) or // If the `E` in `E.foo(...)` could not be resolved, check if the name `foo` matches a constructor pattern. - exists(MemberAccessExpr callee, Expr base | + exists(MemberAccessExpr callee, Expr base, string name, int arity | callee = call.getCallee() and base = callee.getBase() and not exists(NameBinding::getStaticBindingTargetFromRef(base)) and not exists(T::inferType(base)) and - isSignatureUsedInConstructorPattern(callee.getMemberName(), call.getNumberOfArguments()) + callSiteHasSignature(call, name, arity) and + isSignatureUsedInConstructorPattern(name, arity) ) } From 6ab543551d140cdd080f7821a660e050e990e363 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 8 Oct 2026 11:27:59 +0200 Subject: [PATCH 11/18] unified: Record consistency errors The data-flow consistency errors are easier to fix once the stage-splitting of TDataFlowNode has merged, which is part of another PR (variable-capture). --- .../controlflow/CONSISTENCY/DataFlowConsistency.expected | 3 +++ .../dataflow/CONSISTENCY/CfgConsistency.expected | 5 +++++ 2 files changed, 8 insertions(+) create mode 100644 unified/ql/test/library-tests/controlflow/CONSISTENCY/DataFlowConsistency.expected create mode 100644 unified/ql/test/library-tests/dataflow/CONSISTENCY/CfgConsistency.expected diff --git a/unified/ql/test/library-tests/controlflow/CONSISTENCY/DataFlowConsistency.expected b/unified/ql/test/library-tests/controlflow/CONSISTENCY/DataFlowConsistency.expected new file mode 100644 index 000000000000..fb50ea76d3d0 --- /dev/null +++ b/unified/ql/test/library-tests/controlflow/CONSISTENCY/DataFlowConsistency.expected @@ -0,0 +1,3 @@ +reverseRead +| cfg.swift:130:13:130:15 | opt | Origin of readStep is missing a PostUpdateNode. | +| cfg.swift:131:13:131:15 | opt | Origin of readStep is missing a PostUpdateNode. | diff --git a/unified/ql/test/library-tests/dataflow/CONSISTENCY/CfgConsistency.expected b/unified/ql/test/library-tests/dataflow/CONSISTENCY/CfgConsistency.expected new file mode 100644 index 000000000000..e0fa2bbe60ce --- /dev/null +++ b/unified/ql/test/library-tests/dataflow/CONSISTENCY/CfgConsistency.expected @@ -0,0 +1,5 @@ +consistencyOverview +| deadEnd | 2 | +deadEnd +| enums.swift:2:10:2:22 | Entry | +| enums.swift:3:10:3:22 | Entry | From 677f52e27f463ae9717c743450781c1decc9f5dc Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 8 Oct 2026 11:54:42 +0200 Subject: [PATCH 12/18] unified: Add consistency exclusion, but record the miss in a test case --- unified/ql/consistency-queries/DataFlowConsistency.ql | 6 ++++++ .../controlflow/CONSISTENCY/DataFlowConsistency.expected | 3 --- unified/ql/test/library-tests/dataflow/test.swift | 7 +++++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/unified/ql/consistency-queries/DataFlowConsistency.ql b/unified/ql/consistency-queries/DataFlowConsistency.ql index 00a782f847c1..0f8bbde49324 100644 --- a/unified/ql/consistency-queries/DataFlowConsistency.ql +++ b/unified/ql/consistency-queries/DataFlowConsistency.ql @@ -6,6 +6,12 @@ module ConsistencyInput implements InputSig { predicate argHasPostUpdateExclude(DataFlowInput::ArgumentNode n) { not exists(n.getBasicBlock()) // ignore unreachable data flow nodes } + + predicate reverseReadExclude(DataFlow::Node n) { + // When read steps are contributed by a language plugin we currently don't expect them to + // have post-update nodes for reverse-reads. + any(DataFlowPlugin p).step(n, any(Step s | s.read(_)), _) + } } module ConsistencyOutput = diff --git a/unified/ql/test/library-tests/controlflow/CONSISTENCY/DataFlowConsistency.expected b/unified/ql/test/library-tests/controlflow/CONSISTENCY/DataFlowConsistency.expected index fb50ea76d3d0..e69de29bb2d1 100644 --- a/unified/ql/test/library-tests/controlflow/CONSISTENCY/DataFlowConsistency.expected +++ b/unified/ql/test/library-tests/controlflow/CONSISTENCY/DataFlowConsistency.expected @@ -1,3 +0,0 @@ -reverseRead -| cfg.swift:130:13:130:15 | opt | Origin of readStep is missing a PostUpdateNode. | -| cfg.swift:131:13:131:15 | opt | Origin of readStep is missing a PostUpdateNode. | diff --git a/unified/ql/test/library-tests/dataflow/test.swift b/unified/ql/test/library-tests/dataflow/test.swift index e9b2af820268..772c17a849d8 100644 --- a/unified/ql/test/library-tests/dataflow/test.swift +++ b/unified/ql/test/library-tests/dataflow/test.swift @@ -230,3 +230,10 @@ func t25() { } } + +func t26() { + // TODO: This flow requires reverse-read through the "!" operator + var opt = Optional.some(("x", "y")) + opt!.0 = source("t26.1") + sink(opt!.0) // $ MISSING: hasValueFlow=t26.1 +} From c811698a437bd504ce2d94d15fb1214f723502a0 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 8 Oct 2026 13:40:21 +0200 Subject: [PATCH 13/18] unified: Fix bug in test case Without the 'case' keyword the pattern is implicitly wrapped in .some, which was not the intention here --- unified/ql/test/library-tests/dataflow/enums.swift | 4 ++-- .../ql/test/library-tests/dataflow/test.expected | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/unified/ql/test/library-tests/dataflow/enums.swift b/unified/ql/test/library-tests/dataflow/enums.swift index b2082c36709b..85f2ff878dfb 100644 --- a/unified/ql/test/library-tests/dataflow/enums.swift +++ b/unified/ql/test/library-tests/dataflow/enums.swift @@ -27,8 +27,8 @@ func t2() { func t3() { let e = E.case1(source("t3.1")) - guard let E.case1(x) = e else { return } - sink(x) // $ MISSING: hasValueFlow=t3.1 + guard case E.case1(let x) = e else { return } + sink(x) // $ hasValueFlow=t3.1 } func t4() { diff --git a/unified/ql/test/library-tests/dataflow/test.expected b/unified/ql/test/library-tests/dataflow/test.expected index 5b84cf28d912..eb85e30525be 100644 --- a/unified/ql/test/library-tests/dataflow/test.expected +++ b/unified/ql/test/library-tests/dataflow/test.expected @@ -50,6 +50,12 @@ edges | enums.swift:20:12:20:12 | e [case1.0] | enums.swift:21:10:21:22 | ... .case1(...) [case1.0] | provenance | | | enums.swift:21:10:21:22 | ... .case1(...) [case1.0] | enums.swift:21:21:21:21 | x | provenance | | | enums.swift:21:21:21:21 | x | enums.swift:22:14:22:14 | x | provenance | | +| enums.swift:29:9:29:9 | e [case1.0] | enums.swift:30:33:30:33 | e [case1.0] | provenance | | +| enums.swift:29:13:29:35 | ... .case1(...) [case1.0] | enums.swift:29:9:29:9 | e [case1.0] | provenance | | +| enums.swift:29:21:29:34 | source(...) | enums.swift:29:13:29:35 | ... .case1(...) [case1.0] | provenance | | +| enums.swift:30:16:30:29 | ... .case1(...) [case1.0] | enums.swift:30:28:30:28 | x | provenance | | +| enums.swift:30:28:30:28 | x | enums.swift:31:10:31:10 | x | provenance | | +| enums.swift:30:33:30:33 | e [case1.0] | enums.swift:30:16:30:29 | ... .case1(...) [case1.0] | provenance | | | enums.swift:35:9:35:9 | e [case2.0] | enums.swift:36:12:36:12 | e [case2.0] | provenance | | | enums.swift:35:9:35:9 | e [case2.0] | enums.swift:43:12:43:12 | e [case2.0] | provenance | | | enums.swift:35:13:35:35 | ... .case2(...) [case2.0] | enums.swift:35:9:35:9 | e [case2.0] | provenance | | @@ -340,6 +346,13 @@ nodes | enums.swift:21:10:21:22 | ... .case1(...) [case1.0] | semmle.label | ... .case1(...) [case1.0] | | enums.swift:21:21:21:21 | x | semmle.label | x | | enums.swift:22:14:22:14 | x | semmle.label | x | +| enums.swift:29:9:29:9 | e [case1.0] | semmle.label | e [case1.0] | +| enums.swift:29:13:29:35 | ... .case1(...) [case1.0] | semmle.label | ... .case1(...) [case1.0] | +| enums.swift:29:21:29:34 | source(...) | semmle.label | source(...) | +| enums.swift:30:16:30:29 | ... .case1(...) [case1.0] | semmle.label | ... .case1(...) [case1.0] | +| enums.swift:30:28:30:28 | x | semmle.label | x | +| enums.swift:30:33:30:33 | e [case1.0] | semmle.label | e [case1.0] | +| enums.swift:31:10:31:10 | x | semmle.label | x | | enums.swift:35:9:35:9 | e [case2.0] | semmle.label | e [case2.0] | | enums.swift:35:13:35:35 | ... .case2(...) [case2.0] | semmle.label | ... .case2(...) [case2.0] | | enums.swift:35:21:35:34 | source(...) | semmle.label | source(...) | @@ -650,6 +663,7 @@ testFailures | calls.swift:123:18:123:27 | ... .field | calls.swift:75:21:75:34 | source(...) | calls.swift:123:18:123:27 | ... .field | $@ | calls.swift:75:21:75:34 | source(...) | source(...) | | enums.swift:11:14:11:14 | x | enums.swift:7:21:7:34 | source(...) | enums.swift:11:14:11:14 | x | $@ | enums.swift:7:21:7:34 | source(...) | source(...) | | enums.swift:22:14:22:14 | x | enums.swift:18:21:18:34 | source(...) | enums.swift:22:14:22:14 | x | $@ | enums.swift:18:21:18:34 | source(...) | source(...) | +| enums.swift:31:10:31:10 | x | enums.swift:29:21:29:34 | source(...) | enums.swift:31:10:31:10 | x | $@ | enums.swift:29:21:29:34 | source(...) | source(...) | | enums.swift:40:14:40:14 | x | enums.swift:35:21:35:34 | source(...) | enums.swift:40:14:40:14 | x | $@ | enums.swift:35:21:35:34 | source(...) | source(...) | | enums.swift:45:14:45:14 | x | enums.swift:35:21:35:34 | source(...) | enums.swift:45:14:45:14 | x | $@ | enums.swift:35:21:35:34 | source(...) | source(...) | | enums.swift:54:10:54:10 | x | enums.swift:52:31:52:44 | source(...) | enums.swift:54:10:54:10 | x | $@ | enums.swift:52:31:52:44 | source(...) | source(...) | From c41377384b41408db94b24d169e22d17b5e67cc8 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 8 Oct 2026 13:41:37 +0200 Subject: [PATCH 14/18] unified: Make the test case type-checkable The file now passes 'swiftc -typecheck' without errors --- .../CONSISTENCY/CfgConsistency.expected | 4 +- .../test/library-tests/dataflow/enums.swift | 5 + .../test/library-tests/dataflow/test.expected | 222 +++++++++--------- 3 files changed, 118 insertions(+), 113 deletions(-) diff --git a/unified/ql/test/library-tests/dataflow/CONSISTENCY/CfgConsistency.expected b/unified/ql/test/library-tests/dataflow/CONSISTENCY/CfgConsistency.expected index e0fa2bbe60ce..ae1fa85c4383 100644 --- a/unified/ql/test/library-tests/dataflow/CONSISTENCY/CfgConsistency.expected +++ b/unified/ql/test/library-tests/dataflow/CONSISTENCY/CfgConsistency.expected @@ -1,5 +1,5 @@ consistencyOverview | deadEnd | 2 | deadEnd -| enums.swift:2:10:2:22 | Entry | -| enums.swift:3:10:3:22 | Entry | +| enums.swift:7:10:7:22 | Entry | +| enums.swift:8:10:8:22 | Entry | diff --git a/unified/ql/test/library-tests/dataflow/enums.swift b/unified/ql/test/library-tests/dataflow/enums.swift index 85f2ff878dfb..25fd568748c2 100644 --- a/unified/ql/test/library-tests/dataflow/enums.swift +++ b/unified/ql/test/library-tests/dataflow/enums.swift @@ -1,3 +1,8 @@ +func source(_ s: String) -> String { return s } + +@discardableResult +func sink(_ s: Any) -> String { return "" } + enum E { case case1(String) case case2(String) diff --git a/unified/ql/test/library-tests/dataflow/test.expected b/unified/ql/test/library-tests/dataflow/test.expected index eb85e30525be..442288b5a2e2 100644 --- a/unified/ql/test/library-tests/dataflow/test.expected +++ b/unified/ql/test/library-tests/dataflow/test.expected @@ -38,56 +38,56 @@ edges | calls.swift:99:18:99:21 | self [field] | calls.swift:99:18:99:27 | ... .field | provenance | | | calls.swift:111:18:111:21 | self [field] | calls.swift:111:18:111:27 | ... .field | provenance | | | calls.swift:123:18:123:21 | self [field] | calls.swift:123:18:123:27 | ... .field | provenance | | -| enums.swift:7:9:7:9 | e [case1.0] | enums.swift:9:12:9:12 | e [case1.0] | provenance | | -| enums.swift:7:13:7:35 | ... .case1(...) [case1.0] | enums.swift:7:9:7:9 | e [case1.0] | provenance | | -| enums.swift:7:21:7:34 | source(...) | enums.swift:7:13:7:35 | ... .case1(...) [case1.0] | provenance | | -| enums.swift:9:12:9:12 | e [case1.0] | enums.swift:10:10:10:23 | ... .case1(...) [case1.0] | provenance | | -| enums.swift:10:10:10:23 | ... .case1(...) [case1.0] | enums.swift:10:22:10:22 | x | provenance | | -| enums.swift:10:22:10:22 | x | enums.swift:11:14:11:14 | x | provenance | | -| enums.swift:18:9:18:9 | e [case1.0] | enums.swift:20:12:20:12 | e [case1.0] | provenance | | -| enums.swift:18:13:18:35 | ... .case1(...) [case1.0] | enums.swift:18:9:18:9 | e [case1.0] | provenance | | -| enums.swift:18:21:18:34 | source(...) | enums.swift:18:13:18:35 | ... .case1(...) [case1.0] | provenance | | -| enums.swift:20:12:20:12 | e [case1.0] | enums.swift:21:10:21:22 | ... .case1(...) [case1.0] | provenance | | -| enums.swift:21:10:21:22 | ... .case1(...) [case1.0] | enums.swift:21:21:21:21 | x | provenance | | -| enums.swift:21:21:21:21 | x | enums.swift:22:14:22:14 | x | provenance | | -| enums.swift:29:9:29:9 | e [case1.0] | enums.swift:30:33:30:33 | e [case1.0] | provenance | | -| enums.swift:29:13:29:35 | ... .case1(...) [case1.0] | enums.swift:29:9:29:9 | e [case1.0] | provenance | | -| enums.swift:29:21:29:34 | source(...) | enums.swift:29:13:29:35 | ... .case1(...) [case1.0] | provenance | | -| enums.swift:30:16:30:29 | ... .case1(...) [case1.0] | enums.swift:30:28:30:28 | x | provenance | | -| enums.swift:30:28:30:28 | x | enums.swift:31:10:31:10 | x | provenance | | -| enums.swift:30:33:30:33 | e [case1.0] | enums.swift:30:16:30:29 | ... .case1(...) [case1.0] | provenance | | -| enums.swift:35:9:35:9 | e [case2.0] | enums.swift:36:12:36:12 | e [case2.0] | provenance | | -| enums.swift:35:9:35:9 | e [case2.0] | enums.swift:43:12:43:12 | e [case2.0] | provenance | | -| enums.swift:35:13:35:35 | ... .case2(...) [case2.0] | enums.swift:35:9:35:9 | e [case2.0] | provenance | | -| enums.swift:35:21:35:34 | source(...) | enums.swift:35:13:35:35 | ... .case2(...) [case2.0] | provenance | | -| enums.swift:36:12:36:12 | e [case2.0] | enums.swift:39:10:39:23 | ... .case2(...) [case2.0] | provenance | | -| enums.swift:39:10:39:23 | ... .case2(...) [case2.0] | enums.swift:39:22:39:22 | x | provenance | | -| enums.swift:39:22:39:22 | x | enums.swift:40:14:40:14 | x | provenance | | -| enums.swift:43:12:43:12 | e [case2.0] | enums.swift:44:10:44:23 | ... .case2(...) [case2.0] | provenance | | +| enums.swift:12:9:12:9 | e [case1.0] | enums.swift:14:12:14:12 | e [case1.0] | provenance | | +| enums.swift:12:13:12:35 | ... .case1(...) [case1.0] | enums.swift:12:9:12:9 | e [case1.0] | provenance | | +| enums.swift:12:21:12:34 | source(...) | enums.swift:12:13:12:35 | ... .case1(...) [case1.0] | provenance | | +| enums.swift:14:12:14:12 | e [case1.0] | enums.swift:15:10:15:23 | ... .case1(...) [case1.0] | provenance | | +| enums.swift:15:10:15:23 | ... .case1(...) [case1.0] | enums.swift:15:22:15:22 | x | provenance | | +| enums.swift:15:22:15:22 | x | enums.swift:16:14:16:14 | x | provenance | | +| enums.swift:23:9:23:9 | e [case1.0] | enums.swift:25:12:25:12 | e [case1.0] | provenance | | +| enums.swift:23:13:23:35 | ... .case1(...) [case1.0] | enums.swift:23:9:23:9 | e [case1.0] | provenance | | +| enums.swift:23:21:23:34 | source(...) | enums.swift:23:13:23:35 | ... .case1(...) [case1.0] | provenance | | +| enums.swift:25:12:25:12 | e [case1.0] | enums.swift:26:10:26:22 | ... .case1(...) [case1.0] | provenance | | +| enums.swift:26:10:26:22 | ... .case1(...) [case1.0] | enums.swift:26:21:26:21 | x | provenance | | +| enums.swift:26:21:26:21 | x | enums.swift:27:14:27:14 | x | provenance | | +| enums.swift:34:9:34:9 | e [case1.0] | enums.swift:35:33:35:33 | e [case1.0] | provenance | | +| enums.swift:34:13:34:35 | ... .case1(...) [case1.0] | enums.swift:34:9:34:9 | e [case1.0] | provenance | | +| enums.swift:34:21:34:34 | source(...) | enums.swift:34:13:34:35 | ... .case1(...) [case1.0] | provenance | | +| enums.swift:35:16:35:29 | ... .case1(...) [case1.0] | enums.swift:35:28:35:28 | x | provenance | | +| enums.swift:35:28:35:28 | x | enums.swift:36:10:36:10 | x | provenance | | +| enums.swift:35:33:35:33 | e [case1.0] | enums.swift:35:16:35:29 | ... .case1(...) [case1.0] | provenance | | +| enums.swift:40:9:40:9 | e [case2.0] | enums.swift:41:12:41:12 | e [case2.0] | provenance | | +| enums.swift:40:9:40:9 | e [case2.0] | enums.swift:48:12:48:12 | e [case2.0] | provenance | | +| enums.swift:40:13:40:35 | ... .case2(...) [case2.0] | enums.swift:40:9:40:9 | e [case2.0] | provenance | | +| enums.swift:40:21:40:34 | source(...) | enums.swift:40:13:40:35 | ... .case2(...) [case2.0] | provenance | | +| enums.swift:41:12:41:12 | e [case2.0] | enums.swift:44:10:44:23 | ... .case2(...) [case2.0] | provenance | | | enums.swift:44:10:44:23 | ... .case2(...) [case2.0] | enums.swift:44:22:44:22 | x | provenance | | | enums.swift:44:22:44:22 | x | enums.swift:45:14:45:14 | x | provenance | | -| enums.swift:52:9:52:13 | opt_x [some.0] | enums.swift:53:19:53:23 | opt_x [some.0] | provenance | | -| enums.swift:52:17:52:45 | ... .some(...) [some.0] | enums.swift:52:9:52:13 | opt_x [some.0] | provenance | | -| enums.swift:52:31:52:44 | source(...) | enums.swift:52:17:52:45 | ... .some(...) [some.0] | provenance | | -| enums.swift:53:11:53:15 | ... .some(...) [some.0] | enums.swift:53:15:53:15 | x | provenance | | -| enums.swift:53:15:53:15 | x | enums.swift:54:10:54:10 | x | provenance | | -| enums.swift:53:19:53:23 | opt_x [some.0] | enums.swift:53:11:53:15 | ... .some(...) [some.0] | provenance | | -| enums.swift:58:9:58:13 | opt_x [some.0] | enums.swift:59:15:59:19 | opt_x [some.0] | provenance | | -| enums.swift:58:17:58:45 | ... .some(...) [some.0] | enums.swift:58:9:58:13 | opt_x [some.0] | provenance | | -| enums.swift:58:31:58:44 | source(...) | enums.swift:58:17:58:45 | ... .some(...) [some.0] | provenance | | -| enums.swift:59:11:59:19 | ... .some(...) [some.0] | enums.swift:59:15:59:19 | opt_x | provenance | | -| enums.swift:59:15:59:19 | opt_x | enums.swift:60:10:60:14 | opt_x | provenance | | -| enums.swift:59:15:59:19 | opt_x [some.0] | enums.swift:59:11:59:19 | ... .some(...) [some.0] | provenance | | -| enums.swift:68:9:68:9 | e [foo.0] | enums.swift:69:12:69:12 | e [foo.0] | provenance | | -| enums.swift:68:9:68:9 | e [foo.0] | enums.swift:77:12:77:12 | e [foo.0] | provenance | | -| enums.swift:68:13:68:48 | ... .foo(...) [foo.0] | enums.swift:68:9:68:9 | e [foo.0] | provenance | | -| enums.swift:68:34:68:47 | source(...) | enums.swift:68:13:68:48 | ... .foo(...) [foo.0] | provenance | | -| enums.swift:69:12:69:12 | e [foo.0] | enums.swift:70:10:70:20 | ... .foo(...) [foo.0] | provenance | | -| enums.swift:70:10:70:20 | ... .foo(...) [foo.0] | enums.swift:70:19:70:19 | x | provenance | | -| enums.swift:70:19:70:19 | x | enums.swift:71:14:71:14 | x | provenance | | -| enums.swift:77:12:77:12 | e [foo.0] | enums.swift:78:10:78:23 | ... .foo(...) [foo.0] | provenance | | -| enums.swift:78:10:78:23 | ... .foo(...) [foo.0] | enums.swift:78:22:78:22 | x | provenance | | -| enums.swift:78:22:78:22 | x | enums.swift:79:14:79:14 | x | provenance | | +| enums.swift:48:12:48:12 | e [case2.0] | enums.swift:49:10:49:23 | ... .case2(...) [case2.0] | provenance | | +| enums.swift:49:10:49:23 | ... .case2(...) [case2.0] | enums.swift:49:22:49:22 | x | provenance | | +| enums.swift:49:22:49:22 | x | enums.swift:50:14:50:14 | x | provenance | | +| enums.swift:57:9:57:13 | opt_x [some.0] | enums.swift:58:19:58:23 | opt_x [some.0] | provenance | | +| enums.swift:57:17:57:45 | ... .some(...) [some.0] | enums.swift:57:9:57:13 | opt_x [some.0] | provenance | | +| enums.swift:57:31:57:44 | source(...) | enums.swift:57:17:57:45 | ... .some(...) [some.0] | provenance | | +| enums.swift:58:11:58:15 | ... .some(...) [some.0] | enums.swift:58:15:58:15 | x | provenance | | +| enums.swift:58:15:58:15 | x | enums.swift:59:10:59:10 | x | provenance | | +| enums.swift:58:19:58:23 | opt_x [some.0] | enums.swift:58:11:58:15 | ... .some(...) [some.0] | provenance | | +| enums.swift:63:9:63:13 | opt_x [some.0] | enums.swift:64:15:64:19 | opt_x [some.0] | provenance | | +| enums.swift:63:17:63:45 | ... .some(...) [some.0] | enums.swift:63:9:63:13 | opt_x [some.0] | provenance | | +| enums.swift:63:31:63:44 | source(...) | enums.swift:63:17:63:45 | ... .some(...) [some.0] | provenance | | +| enums.swift:64:11:64:19 | ... .some(...) [some.0] | enums.swift:64:15:64:19 | opt_x | provenance | | +| enums.swift:64:15:64:19 | opt_x | enums.swift:65:10:65:14 | opt_x | provenance | | +| enums.swift:64:15:64:19 | opt_x [some.0] | enums.swift:64:11:64:19 | ... .some(...) [some.0] | provenance | | +| enums.swift:73:9:73:9 | e [foo.0] | enums.swift:74:12:74:12 | e [foo.0] | provenance | | +| enums.swift:73:9:73:9 | e [foo.0] | enums.swift:82:12:82:12 | e [foo.0] | provenance | | +| enums.swift:73:13:73:48 | ... .foo(...) [foo.0] | enums.swift:73:9:73:9 | e [foo.0] | provenance | | +| enums.swift:73:34:73:47 | source(...) | enums.swift:73:13:73:48 | ... .foo(...) [foo.0] | provenance | | +| enums.swift:74:12:74:12 | e [foo.0] | enums.swift:75:10:75:20 | ... .foo(...) [foo.0] | provenance | | +| enums.swift:75:10:75:20 | ... .foo(...) [foo.0] | enums.swift:75:19:75:19 | x | provenance | | +| enums.swift:75:19:75:19 | x | enums.swift:76:14:76:14 | x | provenance | | +| enums.swift:82:12:82:12 | e [foo.0] | enums.swift:83:10:83:23 | ... .foo(...) [foo.0] | provenance | | +| enums.swift:83:10:83:23 | ... .foo(...) [foo.0] | enums.swift:83:22:83:22 | x | provenance | | +| enums.swift:83:22:83:22 | x | enums.swift:84:14:84:14 | x | provenance | | | implicit-self.swift:16:9:16:12 | [post] self [x] | implicit-self.swift:17:14:17:17 | self [x] | provenance | | | implicit-self.swift:16:9:16:14 | ... .x | implicit-self.swift:16:9:16:12 | [post] self [x] | provenance | | | implicit-self.swift:16:18:16:31 | source(...) | implicit-self.swift:16:9:16:14 | ... .x | provenance | | @@ -332,63 +332,63 @@ nodes | calls.swift:117:18:117:22 | field | semmle.label | field | | calls.swift:123:18:123:21 | self [field] | semmle.label | self [field] | | calls.swift:123:18:123:27 | ... .field | semmle.label | ... .field | -| enums.swift:7:9:7:9 | e [case1.0] | semmle.label | e [case1.0] | -| enums.swift:7:13:7:35 | ... .case1(...) [case1.0] | semmle.label | ... .case1(...) [case1.0] | -| enums.swift:7:21:7:34 | source(...) | semmle.label | source(...) | -| enums.swift:9:12:9:12 | e [case1.0] | semmle.label | e [case1.0] | -| enums.swift:10:10:10:23 | ... .case1(...) [case1.0] | semmle.label | ... .case1(...) [case1.0] | -| enums.swift:10:22:10:22 | x | semmle.label | x | -| enums.swift:11:14:11:14 | x | semmle.label | x | -| enums.swift:18:9:18:9 | e [case1.0] | semmle.label | e [case1.0] | -| enums.swift:18:13:18:35 | ... .case1(...) [case1.0] | semmle.label | ... .case1(...) [case1.0] | -| enums.swift:18:21:18:34 | source(...) | semmle.label | source(...) | -| enums.swift:20:12:20:12 | e [case1.0] | semmle.label | e [case1.0] | -| enums.swift:21:10:21:22 | ... .case1(...) [case1.0] | semmle.label | ... .case1(...) [case1.0] | -| enums.swift:21:21:21:21 | x | semmle.label | x | -| enums.swift:22:14:22:14 | x | semmle.label | x | -| enums.swift:29:9:29:9 | e [case1.0] | semmle.label | e [case1.0] | -| enums.swift:29:13:29:35 | ... .case1(...) [case1.0] | semmle.label | ... .case1(...) [case1.0] | -| enums.swift:29:21:29:34 | source(...) | semmle.label | source(...) | -| enums.swift:30:16:30:29 | ... .case1(...) [case1.0] | semmle.label | ... .case1(...) [case1.0] | -| enums.swift:30:28:30:28 | x | semmle.label | x | -| enums.swift:30:33:30:33 | e [case1.0] | semmle.label | e [case1.0] | -| enums.swift:31:10:31:10 | x | semmle.label | x | -| enums.swift:35:9:35:9 | e [case2.0] | semmle.label | e [case2.0] | -| enums.swift:35:13:35:35 | ... .case2(...) [case2.0] | semmle.label | ... .case2(...) [case2.0] | -| enums.swift:35:21:35:34 | source(...) | semmle.label | source(...) | -| enums.swift:36:12:36:12 | e [case2.0] | semmle.label | e [case2.0] | -| enums.swift:39:10:39:23 | ... .case2(...) [case2.0] | semmle.label | ... .case2(...) [case2.0] | -| enums.swift:39:22:39:22 | x | semmle.label | x | -| enums.swift:40:14:40:14 | x | semmle.label | x | -| enums.swift:43:12:43:12 | e [case2.0] | semmle.label | e [case2.0] | +| enums.swift:12:9:12:9 | e [case1.0] | semmle.label | e [case1.0] | +| enums.swift:12:13:12:35 | ... .case1(...) [case1.0] | semmle.label | ... .case1(...) [case1.0] | +| enums.swift:12:21:12:34 | source(...) | semmle.label | source(...) | +| enums.swift:14:12:14:12 | e [case1.0] | semmle.label | e [case1.0] | +| enums.swift:15:10:15:23 | ... .case1(...) [case1.0] | semmle.label | ... .case1(...) [case1.0] | +| enums.swift:15:22:15:22 | x | semmle.label | x | +| enums.swift:16:14:16:14 | x | semmle.label | x | +| enums.swift:23:9:23:9 | e [case1.0] | semmle.label | e [case1.0] | +| enums.swift:23:13:23:35 | ... .case1(...) [case1.0] | semmle.label | ... .case1(...) [case1.0] | +| enums.swift:23:21:23:34 | source(...) | semmle.label | source(...) | +| enums.swift:25:12:25:12 | e [case1.0] | semmle.label | e [case1.0] | +| enums.swift:26:10:26:22 | ... .case1(...) [case1.0] | semmle.label | ... .case1(...) [case1.0] | +| enums.swift:26:21:26:21 | x | semmle.label | x | +| enums.swift:27:14:27:14 | x | semmle.label | x | +| enums.swift:34:9:34:9 | e [case1.0] | semmle.label | e [case1.0] | +| enums.swift:34:13:34:35 | ... .case1(...) [case1.0] | semmle.label | ... .case1(...) [case1.0] | +| enums.swift:34:21:34:34 | source(...) | semmle.label | source(...) | +| enums.swift:35:16:35:29 | ... .case1(...) [case1.0] | semmle.label | ... .case1(...) [case1.0] | +| enums.swift:35:28:35:28 | x | semmle.label | x | +| enums.swift:35:33:35:33 | e [case1.0] | semmle.label | e [case1.0] | +| enums.swift:36:10:36:10 | x | semmle.label | x | +| enums.swift:40:9:40:9 | e [case2.0] | semmle.label | e [case2.0] | +| enums.swift:40:13:40:35 | ... .case2(...) [case2.0] | semmle.label | ... .case2(...) [case2.0] | +| enums.swift:40:21:40:34 | source(...) | semmle.label | source(...) | +| enums.swift:41:12:41:12 | e [case2.0] | semmle.label | e [case2.0] | | enums.swift:44:10:44:23 | ... .case2(...) [case2.0] | semmle.label | ... .case2(...) [case2.0] | | enums.swift:44:22:44:22 | x | semmle.label | x | | enums.swift:45:14:45:14 | x | semmle.label | x | -| enums.swift:52:9:52:13 | opt_x [some.0] | semmle.label | opt_x [some.0] | -| enums.swift:52:17:52:45 | ... .some(...) [some.0] | semmle.label | ... .some(...) [some.0] | -| enums.swift:52:31:52:44 | source(...) | semmle.label | source(...) | -| enums.swift:53:11:53:15 | ... .some(...) [some.0] | semmle.label | ... .some(...) [some.0] | -| enums.swift:53:15:53:15 | x | semmle.label | x | -| enums.swift:53:19:53:23 | opt_x [some.0] | semmle.label | opt_x [some.0] | -| enums.swift:54:10:54:10 | x | semmle.label | x | -| enums.swift:58:9:58:13 | opt_x [some.0] | semmle.label | opt_x [some.0] | -| enums.swift:58:17:58:45 | ... .some(...) [some.0] | semmle.label | ... .some(...) [some.0] | -| enums.swift:58:31:58:44 | source(...) | semmle.label | source(...) | -| enums.swift:59:11:59:19 | ... .some(...) [some.0] | semmle.label | ... .some(...) [some.0] | -| enums.swift:59:15:59:19 | opt_x | semmle.label | opt_x | -| enums.swift:59:15:59:19 | opt_x [some.0] | semmle.label | opt_x [some.0] | -| enums.swift:60:10:60:14 | opt_x | semmle.label | opt_x | -| enums.swift:68:9:68:9 | e [foo.0] | semmle.label | e [foo.0] | -| enums.swift:68:13:68:48 | ... .foo(...) [foo.0] | semmle.label | ... .foo(...) [foo.0] | -| enums.swift:68:34:68:47 | source(...) | semmle.label | source(...) | -| enums.swift:69:12:69:12 | e [foo.0] | semmle.label | e [foo.0] | -| enums.swift:70:10:70:20 | ... .foo(...) [foo.0] | semmle.label | ... .foo(...) [foo.0] | -| enums.swift:70:19:70:19 | x | semmle.label | x | -| enums.swift:71:14:71:14 | x | semmle.label | x | -| enums.swift:77:12:77:12 | e [foo.0] | semmle.label | e [foo.0] | -| enums.swift:78:10:78:23 | ... .foo(...) [foo.0] | semmle.label | ... .foo(...) [foo.0] | -| enums.swift:78:22:78:22 | x | semmle.label | x | -| enums.swift:79:14:79:14 | x | semmle.label | x | +| enums.swift:48:12:48:12 | e [case2.0] | semmle.label | e [case2.0] | +| enums.swift:49:10:49:23 | ... .case2(...) [case2.0] | semmle.label | ... .case2(...) [case2.0] | +| enums.swift:49:22:49:22 | x | semmle.label | x | +| enums.swift:50:14:50:14 | x | semmle.label | x | +| enums.swift:57:9:57:13 | opt_x [some.0] | semmle.label | opt_x [some.0] | +| enums.swift:57:17:57:45 | ... .some(...) [some.0] | semmle.label | ... .some(...) [some.0] | +| enums.swift:57:31:57:44 | source(...) | semmle.label | source(...) | +| enums.swift:58:11:58:15 | ... .some(...) [some.0] | semmle.label | ... .some(...) [some.0] | +| enums.swift:58:15:58:15 | x | semmle.label | x | +| enums.swift:58:19:58:23 | opt_x [some.0] | semmle.label | opt_x [some.0] | +| enums.swift:59:10:59:10 | x | semmle.label | x | +| enums.swift:63:9:63:13 | opt_x [some.0] | semmle.label | opt_x [some.0] | +| enums.swift:63:17:63:45 | ... .some(...) [some.0] | semmle.label | ... .some(...) [some.0] | +| enums.swift:63:31:63:44 | source(...) | semmle.label | source(...) | +| enums.swift:64:11:64:19 | ... .some(...) [some.0] | semmle.label | ... .some(...) [some.0] | +| enums.swift:64:15:64:19 | opt_x | semmle.label | opt_x | +| enums.swift:64:15:64:19 | opt_x [some.0] | semmle.label | opt_x [some.0] | +| enums.swift:65:10:65:14 | opt_x | semmle.label | opt_x | +| enums.swift:73:9:73:9 | e [foo.0] | semmle.label | e [foo.0] | +| enums.swift:73:13:73:48 | ... .foo(...) [foo.0] | semmle.label | ... .foo(...) [foo.0] | +| enums.swift:73:34:73:47 | source(...) | semmle.label | source(...) | +| enums.swift:74:12:74:12 | e [foo.0] | semmle.label | e [foo.0] | +| enums.swift:75:10:75:20 | ... .foo(...) [foo.0] | semmle.label | ... .foo(...) [foo.0] | +| enums.swift:75:19:75:19 | x | semmle.label | x | +| enums.swift:76:14:76:14 | x | semmle.label | x | +| enums.swift:82:12:82:12 | e [foo.0] | semmle.label | e [foo.0] | +| enums.swift:83:10:83:23 | ... .foo(...) [foo.0] | semmle.label | ... .foo(...) [foo.0] | +| enums.swift:83:22:83:22 | x | semmle.label | x | +| enums.swift:84:14:84:14 | x | semmle.label | x | | implicit-self.swift:16:9:16:12 | [post] self [x] | semmle.label | [post] self [x] | | implicit-self.swift:16:9:16:14 | ... .x | semmle.label | ... .x | | implicit-self.swift:16:18:16:31 | source(...) | semmle.label | source(...) | @@ -661,15 +661,15 @@ testFailures | calls.swift:111:18:111:27 | ... .field | calls.swift:75:21:75:34 | source(...) | calls.swift:111:18:111:27 | ... .field | $@ | calls.swift:75:21:75:34 | source(...) | source(...) | | calls.swift:117:18:117:22 | field | calls.swift:75:21:75:34 | source(...) | calls.swift:117:18:117:22 | field | $@ | calls.swift:75:21:75:34 | source(...) | source(...) | | calls.swift:123:18:123:27 | ... .field | calls.swift:75:21:75:34 | source(...) | calls.swift:123:18:123:27 | ... .field | $@ | calls.swift:75:21:75:34 | source(...) | source(...) | -| enums.swift:11:14:11:14 | x | enums.swift:7:21:7:34 | source(...) | enums.swift:11:14:11:14 | x | $@ | enums.swift:7:21:7:34 | source(...) | source(...) | -| enums.swift:22:14:22:14 | x | enums.swift:18:21:18:34 | source(...) | enums.swift:22:14:22:14 | x | $@ | enums.swift:18:21:18:34 | source(...) | source(...) | -| enums.swift:31:10:31:10 | x | enums.swift:29:21:29:34 | source(...) | enums.swift:31:10:31:10 | x | $@ | enums.swift:29:21:29:34 | source(...) | source(...) | -| enums.swift:40:14:40:14 | x | enums.swift:35:21:35:34 | source(...) | enums.swift:40:14:40:14 | x | $@ | enums.swift:35:21:35:34 | source(...) | source(...) | -| enums.swift:45:14:45:14 | x | enums.swift:35:21:35:34 | source(...) | enums.swift:45:14:45:14 | x | $@ | enums.swift:35:21:35:34 | source(...) | source(...) | -| enums.swift:54:10:54:10 | x | enums.swift:52:31:52:44 | source(...) | enums.swift:54:10:54:10 | x | $@ | enums.swift:52:31:52:44 | source(...) | source(...) | -| enums.swift:60:10:60:14 | opt_x | enums.swift:58:31:58:44 | source(...) | enums.swift:60:10:60:14 | opt_x | $@ | enums.swift:58:31:58:44 | source(...) | source(...) | -| enums.swift:71:14:71:14 | x | enums.swift:68:34:68:47 | source(...) | enums.swift:71:14:71:14 | x | $@ | enums.swift:68:34:68:47 | source(...) | source(...) | -| enums.swift:79:14:79:14 | x | enums.swift:68:34:68:47 | source(...) | enums.swift:79:14:79:14 | x | $@ | enums.swift:68:34:68:47 | source(...) | source(...) | +| enums.swift:16:14:16:14 | x | enums.swift:12:21:12:34 | source(...) | enums.swift:16:14:16:14 | x | $@ | enums.swift:12:21:12:34 | source(...) | source(...) | +| enums.swift:27:14:27:14 | x | enums.swift:23:21:23:34 | source(...) | enums.swift:27:14:27:14 | x | $@ | enums.swift:23:21:23:34 | source(...) | source(...) | +| enums.swift:36:10:36:10 | x | enums.swift:34:21:34:34 | source(...) | enums.swift:36:10:36:10 | x | $@ | enums.swift:34:21:34:34 | source(...) | source(...) | +| enums.swift:45:14:45:14 | x | enums.swift:40:21:40:34 | source(...) | enums.swift:45:14:45:14 | x | $@ | enums.swift:40:21:40:34 | source(...) | source(...) | +| enums.swift:50:14:50:14 | x | enums.swift:40:21:40:34 | source(...) | enums.swift:50:14:50:14 | x | $@ | enums.swift:40:21:40:34 | source(...) | source(...) | +| enums.swift:59:10:59:10 | x | enums.swift:57:31:57:44 | source(...) | enums.swift:59:10:59:10 | x | $@ | enums.swift:57:31:57:44 | source(...) | source(...) | +| enums.swift:65:10:65:14 | opt_x | enums.swift:63:31:63:44 | source(...) | enums.swift:65:10:65:14 | opt_x | $@ | enums.swift:63:31:63:44 | source(...) | source(...) | +| enums.swift:76:14:76:14 | x | enums.swift:73:34:73:47 | source(...) | enums.swift:76:14:76:14 | x | $@ | enums.swift:73:34:73:47 | source(...) | source(...) | +| enums.swift:84:14:84:14 | x | enums.swift:73:34:73:47 | source(...) | enums.swift:84:14:84:14 | x | $@ | enums.swift:73:34:73:47 | source(...) | source(...) | | implicit-self.swift:17:14:17:19 | ... .x | implicit-self.swift:16:18:16:31 | source(...) | implicit-self.swift:17:14:17:19 | ... .x | $@ | implicit-self.swift:16:18:16:31 | source(...) | source(...) | | implicit-self.swift:23:14:23:14 | x | implicit-self.swift:22:13:22:26 | source(...) | implicit-self.swift:23:14:23:14 | x | $@ | implicit-self.swift:22:13:22:26 | source(...) | source(...) | | implicit-self.swift:29:14:29:19 | ... .x | implicit-self.swift:28:13:28:26 | source(...) | implicit-self.swift:29:14:29:19 | ... .x | $@ | implicit-self.swift:28:13:28:26 | source(...) | source(...) | From 18f97f7d72694f0dd684da4c9f3fce98694e7a80 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 8 Oct 2026 13:48:49 +0200 Subject: [PATCH 15/18] unified: Mention flattening behavior of 'try?' --- .../unified/internal/dataflow/DataFlowPluginSwift.qll | 1 + unified/ql/test/library-tests/dataflow/test.swift | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll index 5ac92a80bbcf..480113c9e45f 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll @@ -38,6 +38,7 @@ private class SwiftDataFlowPlugin extends DataFlowPlugin { node2.isResultValue(expr) or expr.getOperator().(PrefixOperator).getValue() = "try?" and + // TODO: preserve the value of some.0 if it is already stored in that node1.isResultValue(expr.getOperand()) and step.storeName("some.0") and node2.isResultValue(expr) diff --git a/unified/ql/test/library-tests/dataflow/test.swift b/unified/ql/test/library-tests/dataflow/test.swift index 772c17a849d8..0ae09fd30d91 100644 --- a/unified/ql/test/library-tests/dataflow/test.swift +++ b/unified/ql/test/library-tests/dataflow/test.swift @@ -237,3 +237,14 @@ func t26() { opt!.0 = source("t26.1") sink(opt!.0) // $ MISSING: hasValueFlow=t26.1 } + +func t27() { + func getOptional() throws -> String? { + return Optional.some(source("t27.1")) + } + // TODO: try? should not double-wrap in Optional + let x = try? getOptional() + if let x { + sink(x) // $ MISSING: hasValueFlow=t27.1 + } +} From f1aeecb0e00bdaa2e8257dc6761013ce2ed717e0 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 8 Oct 2026 14:02:38 +0200 Subject: [PATCH 16/18] unified: Add test case for one-arg Data call --- .../PathInjection/PathInjectionTest.expected | 132 ++++++++++-------- .../PathInjection/testPathInjection.swift | 2 + 2 files changed, 78 insertions(+), 56 deletions(-) diff --git a/unified/ql/test/query-tests/security/CWE-022/PathInjection/PathInjectionTest.expected b/unified/ql/test/query-tests/security/CWE-022/PathInjection/PathInjectionTest.expected index bdee25f4a81d..fe1438f3e19b 100644 --- a/unified/ql/test/query-tests/security/CWE-022/PathInjection/PathInjectionTest.expected +++ b/unified/ql/test/query-tests/security/CWE-022/PathInjection/PathInjectionTest.expected @@ -73,19 +73,22 @@ | testPathInjection.swift:482:22:482:33 | remoteString | testPathInjection.swift:340:24:340:78 | String(...) | testPathInjection.swift:482:22:482:33 | remoteString | This path depends on a $@. | testPathInjection.swift:340:24:340:78 | String(...) | user-provided value | | testPathInjection.swift:486:25:486:36 | remoteString | testPathInjection.swift:340:24:340:78 | String(...) | testPathInjection.swift:486:25:486:36 | remoteString | This path depends on a $@. | testPathInjection.swift:340:24:340:78 | String(...) | user-provided value | | testPathInjection.swift:498:49:498:60 | remoteString | testPathInjection.swift:340:24:340:78 | String(...) | testPathInjection.swift:498:49:498:60 | remoteString | This path depends on a $@. | testPathInjection.swift:340:24:340:78 | String(...) | user-provided value | -| testPathInjection.swift:510:37:510:48 | remoteString | testPathInjection.swift:504:24:504:78 | String(...) | testPathInjection.swift:510:37:510:48 | remoteString | This path depends on a $@. | testPathInjection.swift:504:24:504:78 | String(...) | user-provided value | -| testPathInjection.swift:512:33:512:44 | remoteString | testPathInjection.swift:504:24:504:78 | String(...) | testPathInjection.swift:512:33:512:44 | remoteString | This path depends on a $@. | testPathInjection.swift:504:24:504:78 | String(...) | user-provided value | -| testPathInjection.swift:529:28:529:39 | remoteString | testPathInjection.swift:519:24:519:78 | String(...) | testPathInjection.swift:529:28:529:39 | remoteString | This path depends on a $@. | testPathInjection.swift:519:24:519:78 | String(...) | user-provided value | -| testPathInjection.swift:541:32:541:43 | remoteString | testPathInjection.swift:519:24:519:78 | String(...) | testPathInjection.swift:541:32:541:43 | remoteString | This path depends on a $@. | testPathInjection.swift:519:24:519:78 | String(...) | user-provided value | -| testPathInjection.swift:542:38:542:49 | remoteString | testPathInjection.swift:519:24:519:78 | String(...) | testPathInjection.swift:542:38:542:49 | remoteString | This path depends on a $@. | testPathInjection.swift:519:24:519:78 | String(...) | user-provided value | -| testPathInjection.swift:543:45:543:56 | remoteString | testPathInjection.swift:519:24:519:78 | String(...) | testPathInjection.swift:543:45:543:56 | remoteString | This path depends on a $@. | testPathInjection.swift:519:24:519:78 | String(...) | user-provided value | -| testPathInjection.swift:547:32:547:41 | ... .pointee | testPathInjection.swift:519:24:519:78 | String(...) | testPathInjection.swift:547:32:547:41 | ... .pointee | This path depends on a $@. | testPathInjection.swift:519:24:519:78 | String(...) | user-provided value | -| testPathInjection.swift:559:35:559:46 | remoteString | testPathInjection.swift:519:24:519:78 | String(...) | testPathInjection.swift:559:35:559:46 | remoteString | This path depends on a $@. | testPathInjection.swift:519:24:519:78 | String(...) | user-provided value | -| testPathInjection.swift:560:41:560:52 | remoteString | testPathInjection.swift:519:24:519:78 | String(...) | testPathInjection.swift:560:41:560:52 | remoteString | This path depends on a $@. | testPathInjection.swift:519:24:519:78 | String(...) | user-provided value | -| testPathInjection.swift:582:25:582:36 | remoteString | testPathInjection.swift:580:24:580:78 | String(...) | testPathInjection.swift:582:25:582:36 | remoteString | This path depends on a $@. | testPathInjection.swift:580:24:580:78 | String(...) | user-provided value | -| testPathInjection.swift:584:41:584:52 | remoteString | testPathInjection.swift:580:24:580:78 | String(...) | testPathInjection.swift:584:41:584:52 | remoteString | This path depends on a $@. | testPathInjection.swift:580:24:580:78 | String(...) | user-provided value | -| testPathInjection.swift:586:38:586:49 | remoteString | testPathInjection.swift:580:24:580:78 | String(...) | testPathInjection.swift:586:38:586:49 | remoteString | This path depends on a $@. | testPathInjection.swift:580:24:580:78 | String(...) | user-provided value | -| testPathInjection.swift:588:22:588:33 | remoteString | testPathInjection.swift:580:24:580:78 | String(...) | testPathInjection.swift:588:22:588:33 | remoteString | This path depends on a $@. | testPathInjection.swift:580:24:580:78 | String(...) | user-provided value | +| testPathInjection.swift:503:26:503:36 | remoteData2 | testPathInjection.swift:502:23:502:75 | Data(...) | testPathInjection.swift:503:26:503:36 | remoteData2 | This path depends on a $@. | testPathInjection.swift:502:23:502:75 | Data(...) | user-provided value | +| testPathInjection.swift:513:37:513:48 | remoteString | testPathInjection.swift:507:24:507:78 | String(...) | testPathInjection.swift:513:37:513:48 | remoteString | This path depends on a $@. | testPathInjection.swift:507:24:507:78 | String(...) | user-provided value | +| testPathInjection.swift:515:33:515:44 | remoteString | testPathInjection.swift:507:24:507:78 | String(...) | testPathInjection.swift:515:33:515:44 | remoteString | This path depends on a $@. | testPathInjection.swift:507:24:507:78 | String(...) | user-provided value | +| testPathInjection.swift:528:28:528:93 | ... .appendingPathComponent(...) | testPathInjection.swift:522:24:522:78 | String(...) | testPathInjection.swift:528:28:528:93 | ... .appendingPathComponent(...) | This path depends on a $@. | testPathInjection.swift:522:24:522:78 | String(...) | user-provided value | +| testPathInjection.swift:530:28:530:29 | u1 | testPathInjection.swift:522:24:522:78 | String(...) | testPathInjection.swift:530:28:530:29 | u1 | This path depends on a $@. | testPathInjection.swift:522:24:522:78 | String(...) | user-provided value | +| testPathInjection.swift:532:28:532:39 | remoteString | testPathInjection.swift:522:24:522:78 | String(...) | testPathInjection.swift:532:28:532:39 | remoteString | This path depends on a $@. | testPathInjection.swift:522:24:522:78 | String(...) | user-provided value | +| testPathInjection.swift:544:32:544:43 | remoteString | testPathInjection.swift:522:24:522:78 | String(...) | testPathInjection.swift:544:32:544:43 | remoteString | This path depends on a $@. | testPathInjection.swift:522:24:522:78 | String(...) | user-provided value | +| testPathInjection.swift:545:38:545:49 | remoteString | testPathInjection.swift:522:24:522:78 | String(...) | testPathInjection.swift:545:38:545:49 | remoteString | This path depends on a $@. | testPathInjection.swift:522:24:522:78 | String(...) | user-provided value | +| testPathInjection.swift:546:45:546:56 | remoteString | testPathInjection.swift:522:24:522:78 | String(...) | testPathInjection.swift:546:45:546:56 | remoteString | This path depends on a $@. | testPathInjection.swift:522:24:522:78 | String(...) | user-provided value | +| testPathInjection.swift:550:32:550:41 | ... .pointee | testPathInjection.swift:522:24:522:78 | String(...) | testPathInjection.swift:550:32:550:41 | ... .pointee | This path depends on a $@. | testPathInjection.swift:522:24:522:78 | String(...) | user-provided value | +| testPathInjection.swift:562:35:562:46 | remoteString | testPathInjection.swift:522:24:522:78 | String(...) | testPathInjection.swift:562:35:562:46 | remoteString | This path depends on a $@. | testPathInjection.swift:522:24:522:78 | String(...) | user-provided value | +| testPathInjection.swift:563:41:563:52 | remoteString | testPathInjection.swift:522:24:522:78 | String(...) | testPathInjection.swift:563:41:563:52 | remoteString | This path depends on a $@. | testPathInjection.swift:522:24:522:78 | String(...) | user-provided value | +| testPathInjection.swift:585:25:585:36 | remoteString | testPathInjection.swift:583:24:583:78 | String(...) | testPathInjection.swift:585:25:585:36 | remoteString | This path depends on a $@. | testPathInjection.swift:583:24:583:78 | String(...) | user-provided value | +| testPathInjection.swift:587:41:587:52 | remoteString | testPathInjection.swift:583:24:583:78 | String(...) | testPathInjection.swift:587:41:587:52 | remoteString | This path depends on a $@. | testPathInjection.swift:583:24:583:78 | String(...) | user-provided value | +| testPathInjection.swift:589:38:589:49 | remoteString | testPathInjection.swift:583:24:583:78 | String(...) | testPathInjection.swift:589:38:589:49 | remoteString | This path depends on a $@. | testPathInjection.swift:583:24:583:78 | String(...) | user-provided value | +| testPathInjection.swift:591:22:591:33 | remoteString | testPathInjection.swift:583:24:583:78 | String(...) | testPathInjection.swift:591:22:591:33 | remoteString | This path depends on a $@. | testPathInjection.swift:583:24:583:78 | String(...) | user-provided value | edges | testPathInjection.swift:340:9:340:20 | remoteString | testPathInjection.swift:341:33:341:44 | remoteString | provenance | | | testPathInjection.swift:340:9:340:20 | remoteString | testPathInjection.swift:342:37:342:48 | remoteString | provenance | | @@ -171,26 +174,34 @@ edges | testPathInjection.swift:426:42:426:53 | remoteString | testPathInjection.swift:426:42:426:72 | TypeCastExpr | provenance | | | testPathInjection.swift:477:9:477:18 | remoteData | testPathInjection.swift:484:24:484:30 | buffer2 | provenance | | | testPathInjection.swift:477:22:477:87 | Data(...) | testPathInjection.swift:477:9:477:18 | remoteData | provenance | | -| testPathInjection.swift:504:9:504:20 | remoteString | testPathInjection.swift:510:37:510:48 | remoteString | provenance | | -| testPathInjection.swift:504:9:504:20 | remoteString | testPathInjection.swift:512:33:512:44 | remoteString | provenance | | -| testPathInjection.swift:504:24:504:78 | String(...) | testPathInjection.swift:504:9:504:20 | remoteString | provenance | | -| testPathInjection.swift:519:9:519:20 | remoteString | testPathInjection.swift:529:28:529:39 | remoteString | provenance | | -| testPathInjection.swift:519:9:519:20 | remoteString | testPathInjection.swift:541:32:541:43 | remoteString | provenance | | -| testPathInjection.swift:519:9:519:20 | remoteString | testPathInjection.swift:542:38:542:49 | remoteString | provenance | | -| testPathInjection.swift:519:9:519:20 | remoteString | testPathInjection.swift:543:45:543:56 | remoteString | provenance | | -| testPathInjection.swift:519:9:519:20 | remoteString | testPathInjection.swift:546:18:546:29 | remoteString | provenance | | -| testPathInjection.swift:519:9:519:20 | remoteString | testPathInjection.swift:559:35:559:46 | remoteString | provenance | | -| testPathInjection.swift:519:9:519:20 | remoteString | testPathInjection.swift:560:41:560:52 | remoteString | provenance | | -| testPathInjection.swift:519:24:519:78 | String(...) | testPathInjection.swift:519:9:519:20 | remoteString | provenance | | -| testPathInjection.swift:546:5:546:6 | [post] s1 [pointee] | testPathInjection.swift:547:32:547:33 | s1 [pointee] | provenance | | -| testPathInjection.swift:546:5:546:14 | ... .pointee | testPathInjection.swift:546:5:546:6 | [post] s1 [pointee] | provenance | | -| testPathInjection.swift:546:18:546:29 | remoteString | testPathInjection.swift:546:5:546:14 | ... .pointee | provenance | | -| testPathInjection.swift:547:32:547:33 | s1 [pointee] | testPathInjection.swift:547:32:547:41 | ... .pointee | provenance | | -| testPathInjection.swift:580:9:580:20 | remoteString | testPathInjection.swift:582:25:582:36 | remoteString | provenance | | -| testPathInjection.swift:580:9:580:20 | remoteString | testPathInjection.swift:584:41:584:52 | remoteString | provenance | | -| testPathInjection.swift:580:9:580:20 | remoteString | testPathInjection.swift:586:38:586:49 | remoteString | provenance | | -| testPathInjection.swift:580:9:580:20 | remoteString | testPathInjection.swift:588:22:588:33 | remoteString | provenance | | -| testPathInjection.swift:580:24:580:78 | String(...) | testPathInjection.swift:580:9:580:20 | remoteString | provenance | | +| testPathInjection.swift:502:9:502:19 | remoteData2 | testPathInjection.swift:503:26:503:36 | remoteData2 | provenance | | +| testPathInjection.swift:502:23:502:75 | Data(...) | testPathInjection.swift:502:9:502:19 | remoteData2 | provenance | | +| testPathInjection.swift:507:9:507:20 | remoteString | testPathInjection.swift:513:37:513:48 | remoteString | provenance | | +| testPathInjection.swift:507:9:507:20 | remoteString | testPathInjection.swift:515:33:515:44 | remoteString | provenance | | +| testPathInjection.swift:507:24:507:78 | String(...) | testPathInjection.swift:507:9:507:20 | remoteString | provenance | | +| testPathInjection.swift:522:9:522:20 | remoteString | testPathInjection.swift:528:28:528:29 | u1 | provenance | | +| testPathInjection.swift:522:9:522:20 | remoteString | testPathInjection.swift:530:28:530:29 | u1 | provenance | | +| testPathInjection.swift:522:9:522:20 | remoteString | testPathInjection.swift:532:28:532:39 | remoteString | provenance | | +| testPathInjection.swift:522:9:522:20 | remoteString | testPathInjection.swift:544:32:544:43 | remoteString | provenance | | +| testPathInjection.swift:522:9:522:20 | remoteString | testPathInjection.swift:545:38:545:49 | remoteString | provenance | | +| testPathInjection.swift:522:9:522:20 | remoteString | testPathInjection.swift:546:45:546:56 | remoteString | provenance | | +| testPathInjection.swift:522:9:522:20 | remoteString | testPathInjection.swift:549:18:549:29 | remoteString | provenance | | +| testPathInjection.swift:522:9:522:20 | remoteString | testPathInjection.swift:562:35:562:46 | remoteString | provenance | | +| testPathInjection.swift:522:9:522:20 | remoteString | testPathInjection.swift:563:41:563:52 | remoteString | provenance | | +| testPathInjection.swift:522:24:522:78 | String(...) | testPathInjection.swift:522:9:522:20 | remoteString | provenance | | +| testPathInjection.swift:528:28:528:29 | u1 | testPathInjection.swift:16:74:16:77 | self | provenance | | +| testPathInjection.swift:528:28:528:29 | u1 | testPathInjection.swift:528:28:528:66 | ... .appendingPathComponent(...) | provenance | | +| testPathInjection.swift:528:28:528:66 | ... .appendingPathComponent(...) | testPathInjection.swift:16:74:16:77 | self | provenance | | +| testPathInjection.swift:528:28:528:66 | ... .appendingPathComponent(...) | testPathInjection.swift:528:28:528:93 | ... .appendingPathComponent(...) | provenance | | +| testPathInjection.swift:549:5:549:6 | [post] s1 [pointee] | testPathInjection.swift:550:32:550:33 | s1 [pointee] | provenance | | +| testPathInjection.swift:549:5:549:14 | ... .pointee | testPathInjection.swift:549:5:549:6 | [post] s1 [pointee] | provenance | | +| testPathInjection.swift:549:18:549:29 | remoteString | testPathInjection.swift:549:5:549:14 | ... .pointee | provenance | | +| testPathInjection.swift:550:32:550:33 | s1 [pointee] | testPathInjection.swift:550:32:550:41 | ... .pointee | provenance | | +| testPathInjection.swift:583:9:583:20 | remoteString | testPathInjection.swift:585:25:585:36 | remoteString | provenance | | +| testPathInjection.swift:583:9:583:20 | remoteString | testPathInjection.swift:587:41:587:52 | remoteString | provenance | | +| testPathInjection.swift:583:9:583:20 | remoteString | testPathInjection.swift:589:38:589:49 | remoteString | provenance | | +| testPathInjection.swift:583:9:583:20 | remoteString | testPathInjection.swift:591:22:591:33 | remoteString | provenance | | +| testPathInjection.swift:583:24:583:78 | String(...) | testPathInjection.swift:583:9:583:20 | remoteString | provenance | | nodes | testPathInjection.swift:340:9:340:20 | remoteString | semmle.label | remoteString | | testPathInjection.swift:340:24:340:78 | String(...) | semmle.label | String(...) | @@ -274,27 +285,36 @@ nodes | testPathInjection.swift:482:22:482:33 | remoteString | semmle.label | remoteString | | testPathInjection.swift:486:25:486:36 | remoteString | semmle.label | remoteString | | testPathInjection.swift:498:49:498:60 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:504:9:504:20 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:504:24:504:78 | String(...) | semmle.label | String(...) | -| testPathInjection.swift:510:37:510:48 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:512:33:512:44 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:519:9:519:20 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:519:24:519:78 | String(...) | semmle.label | String(...) | -| testPathInjection.swift:529:28:529:39 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:541:32:541:43 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:542:38:542:49 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:543:45:543:56 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:546:5:546:6 | [post] s1 [pointee] | semmle.label | [post] s1 [pointee] | -| testPathInjection.swift:546:5:546:14 | ... .pointee | semmle.label | ... .pointee | -| testPathInjection.swift:546:18:546:29 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:547:32:547:33 | s1 [pointee] | semmle.label | s1 [pointee] | -| testPathInjection.swift:547:32:547:41 | ... .pointee | semmle.label | ... .pointee | -| testPathInjection.swift:559:35:559:46 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:560:41:560:52 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:580:9:580:20 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:580:24:580:78 | String(...) | semmle.label | String(...) | -| testPathInjection.swift:582:25:582:36 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:584:41:584:52 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:586:38:586:49 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:588:22:588:33 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:502:9:502:19 | remoteData2 | semmle.label | remoteData2 | +| testPathInjection.swift:502:23:502:75 | Data(...) | semmle.label | Data(...) | +| testPathInjection.swift:503:26:503:36 | remoteData2 | semmle.label | remoteData2 | +| testPathInjection.swift:507:9:507:20 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:507:24:507:78 | String(...) | semmle.label | String(...) | +| testPathInjection.swift:513:37:513:48 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:515:33:515:44 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:522:9:522:20 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:522:24:522:78 | String(...) | semmle.label | String(...) | +| testPathInjection.swift:528:28:528:29 | u1 | semmle.label | u1 | +| testPathInjection.swift:528:28:528:66 | ... .appendingPathComponent(...) | semmle.label | ... .appendingPathComponent(...) | +| testPathInjection.swift:528:28:528:93 | ... .appendingPathComponent(...) | semmle.label | ... .appendingPathComponent(...) | +| testPathInjection.swift:530:28:530:29 | u1 | semmle.label | u1 | +| testPathInjection.swift:532:28:532:39 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:544:32:544:43 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:545:38:545:49 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:546:45:546:56 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:549:5:549:6 | [post] s1 [pointee] | semmle.label | [post] s1 [pointee] | +| testPathInjection.swift:549:5:549:14 | ... .pointee | semmle.label | ... .pointee | +| testPathInjection.swift:549:18:549:29 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:550:32:550:33 | s1 [pointee] | semmle.label | s1 [pointee] | +| testPathInjection.swift:550:32:550:41 | ... .pointee | semmle.label | ... .pointee | +| testPathInjection.swift:562:35:562:46 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:563:41:563:52 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:583:9:583:20 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:583:24:583:78 | String(...) | semmle.label | String(...) | +| testPathInjection.swift:585:25:585:36 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:587:41:587:52 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:589:38:589:49 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:591:22:591:33 | remoteString | semmle.label | remoteString | subpaths +| testPathInjection.swift:528:28:528:29 | u1 | testPathInjection.swift:16:74:16:77 | self | testPathInjection.swift:16:74:16:77 | self | testPathInjection.swift:528:28:528:66 | ... .appendingPathComponent(...) | +| testPathInjection.swift:528:28:528:66 | ... .appendingPathComponent(...) | testPathInjection.swift:16:74:16:77 | self | testPathInjection.swift:16:74:16:77 | self | testPathInjection.swift:528:28:528:93 | ... .appendingPathComponent(...) | diff --git a/unified/ql/test/query-tests/security/CWE-022/PathInjection/testPathInjection.swift b/unified/ql/test/query-tests/security/CWE-022/PathInjection/testPathInjection.swift index 455f019bdc06..5474b426ec9f 100644 --- a/unified/ql/test/query-tests/security/CWE-022/PathInjection/testPathInjection.swift +++ b/unified/ql/test/query-tests/security/CWE-022/PathInjection/testPathInjection.swift @@ -498,6 +498,8 @@ func test(buffer1: UnsafeMutablePointer, buffer2: UnsafeMutablePointer Date: Thu, 8 Oct 2026 16:33:39 +0200 Subject: [PATCH 17/18] unified: Update test output after rebasing This change happened after rebasing onto the SSA changes, which had significant impact for unified data flow --- .../test/library-tests/dataflow/test.expected | 14 +- .../PathInjection/PathInjectionTest.expected | 141 ++++++++---------- 2 files changed, 69 insertions(+), 86 deletions(-) diff --git a/unified/ql/test/library-tests/dataflow/test.expected b/unified/ql/test/library-tests/dataflow/test.expected index 442288b5a2e2..d45f2ca83f62 100644 --- a/unified/ql/test/library-tests/dataflow/test.expected +++ b/unified/ql/test/library-tests/dataflow/test.expected @@ -634,13 +634,13 @@ nodes subpaths | calls.swift:31:17:31:30 | source(...) | calls.swift:28:19:28:19 | x | calls.swift:29:16:29:24 | ... + ... | calls.swift:31:10:31:31 | target(...) | | calls.swift:32:17:32:30 | source(...) | calls.swift:28:19:28:19 | x | calls.swift:29:16:29:24 | ... + ... | calls.swift:32:10:32:31 | target(...) | -| test.swift:187:30:187:44 | source(...) | test.swift:180:22:180:22 | s | test.swift:180:58:180:58 | s | test.swift:187:16:187:45 | asyncIdentity(...) | -| test.swift:191:31:191:45 | source(...) | test.swift:182:25:182:25 | s | test.swift:182:62:182:62 | s | test.swift:191:14:191:46 | throwingIdentity(...) | -| test.swift:192:37:192:51 | source(...) | test.swift:182:25:182:25 | s | test.swift:182:62:182:62 | s | test.swift:192:20:192:52 | throwingIdentity(...) | -| test.swift:196:32:196:46 | source(...) | test.swift:182:25:182:25 | s | test.swift:182:62:182:62 | s | test.swift:196:15:196:47 | throwingIdentity(...) | -| test.swift:200:42:200:56 | source(...) | test.swift:184:30:184:30 | s | test.swift:184:73:184:73 | s | test.swift:200:20:200:57 | asyncThrowingIdentity(...) | -| test.swift:201:48:201:62 | source(...) | test.swift:184:30:184:30 | s | test.swift:184:73:184:73 | s | test.swift:201:26:201:63 | asyncThrowingIdentity(...) | -| test.swift:205:43:205:57 | source(...) | test.swift:184:30:184:30 | s | test.swift:184:73:184:73 | s | test.swift:205:21:205:58 | asyncThrowingIdentity(...) | +| test.swift:196:30:196:44 | source(...) | test.swift:189:22:189:22 | s | test.swift:189:58:189:58 | s | test.swift:196:16:196:45 | asyncIdentity(...) | +| test.swift:200:31:200:45 | source(...) | test.swift:191:25:191:25 | s | test.swift:191:62:191:62 | s | test.swift:200:14:200:46 | throwingIdentity(...) | +| test.swift:201:37:201:51 | source(...) | test.swift:191:25:191:25 | s | test.swift:191:62:191:62 | s | test.swift:201:20:201:52 | throwingIdentity(...) | +| test.swift:205:32:205:46 | source(...) | test.swift:191:25:191:25 | s | test.swift:191:62:191:62 | s | test.swift:205:15:205:47 | throwingIdentity(...) | +| test.swift:209:42:209:56 | source(...) | test.swift:193:30:193:30 | s | test.swift:193:73:193:73 | s | test.swift:209:20:209:57 | asyncThrowingIdentity(...) | +| test.swift:210:48:210:62 | source(...) | test.swift:193:30:193:30 | s | test.swift:193:73:193:73 | s | test.swift:210:26:210:63 | asyncThrowingIdentity(...) | +| test.swift:214:43:214:57 | source(...) | test.swift:193:30:193:30 | s | test.swift:193:73:193:73 | s | test.swift:214:21:214:58 | asyncThrowingIdentity(...) | testFailures #select | calls.swift:8:14:8:14 | x | calls.swift:10:12:10:25 | source(...) | calls.swift:8:14:8:14 | x | $@ | calls.swift:10:12:10:25 | source(...) | source(...) | diff --git a/unified/ql/test/query-tests/security/CWE-022/PathInjection/PathInjectionTest.expected b/unified/ql/test/query-tests/security/CWE-022/PathInjection/PathInjectionTest.expected index fe1438f3e19b..45d502f8e647 100644 --- a/unified/ql/test/query-tests/security/CWE-022/PathInjection/PathInjectionTest.expected +++ b/unified/ql/test/query-tests/security/CWE-022/PathInjection/PathInjectionTest.expected @@ -73,22 +73,20 @@ | testPathInjection.swift:482:22:482:33 | remoteString | testPathInjection.swift:340:24:340:78 | String(...) | testPathInjection.swift:482:22:482:33 | remoteString | This path depends on a $@. | testPathInjection.swift:340:24:340:78 | String(...) | user-provided value | | testPathInjection.swift:486:25:486:36 | remoteString | testPathInjection.swift:340:24:340:78 | String(...) | testPathInjection.swift:486:25:486:36 | remoteString | This path depends on a $@. | testPathInjection.swift:340:24:340:78 | String(...) | user-provided value | | testPathInjection.swift:498:49:498:60 | remoteString | testPathInjection.swift:340:24:340:78 | String(...) | testPathInjection.swift:498:49:498:60 | remoteString | This path depends on a $@. | testPathInjection.swift:340:24:340:78 | String(...) | user-provided value | -| testPathInjection.swift:503:26:503:36 | remoteData2 | testPathInjection.swift:502:23:502:75 | Data(...) | testPathInjection.swift:503:26:503:36 | remoteData2 | This path depends on a $@. | testPathInjection.swift:502:23:502:75 | Data(...) | user-provided value | -| testPathInjection.swift:513:37:513:48 | remoteString | testPathInjection.swift:507:24:507:78 | String(...) | testPathInjection.swift:513:37:513:48 | remoteString | This path depends on a $@. | testPathInjection.swift:507:24:507:78 | String(...) | user-provided value | -| testPathInjection.swift:515:33:515:44 | remoteString | testPathInjection.swift:507:24:507:78 | String(...) | testPathInjection.swift:515:33:515:44 | remoteString | This path depends on a $@. | testPathInjection.swift:507:24:507:78 | String(...) | user-provided value | -| testPathInjection.swift:528:28:528:93 | ... .appendingPathComponent(...) | testPathInjection.swift:522:24:522:78 | String(...) | testPathInjection.swift:528:28:528:93 | ... .appendingPathComponent(...) | This path depends on a $@. | testPathInjection.swift:522:24:522:78 | String(...) | user-provided value | -| testPathInjection.swift:530:28:530:29 | u1 | testPathInjection.swift:522:24:522:78 | String(...) | testPathInjection.swift:530:28:530:29 | u1 | This path depends on a $@. | testPathInjection.swift:522:24:522:78 | String(...) | user-provided value | -| testPathInjection.swift:532:28:532:39 | remoteString | testPathInjection.swift:522:24:522:78 | String(...) | testPathInjection.swift:532:28:532:39 | remoteString | This path depends on a $@. | testPathInjection.swift:522:24:522:78 | String(...) | user-provided value | -| testPathInjection.swift:544:32:544:43 | remoteString | testPathInjection.swift:522:24:522:78 | String(...) | testPathInjection.swift:544:32:544:43 | remoteString | This path depends on a $@. | testPathInjection.swift:522:24:522:78 | String(...) | user-provided value | -| testPathInjection.swift:545:38:545:49 | remoteString | testPathInjection.swift:522:24:522:78 | String(...) | testPathInjection.swift:545:38:545:49 | remoteString | This path depends on a $@. | testPathInjection.swift:522:24:522:78 | String(...) | user-provided value | -| testPathInjection.swift:546:45:546:56 | remoteString | testPathInjection.swift:522:24:522:78 | String(...) | testPathInjection.swift:546:45:546:56 | remoteString | This path depends on a $@. | testPathInjection.swift:522:24:522:78 | String(...) | user-provided value | -| testPathInjection.swift:550:32:550:41 | ... .pointee | testPathInjection.swift:522:24:522:78 | String(...) | testPathInjection.swift:550:32:550:41 | ... .pointee | This path depends on a $@. | testPathInjection.swift:522:24:522:78 | String(...) | user-provided value | -| testPathInjection.swift:562:35:562:46 | remoteString | testPathInjection.swift:522:24:522:78 | String(...) | testPathInjection.swift:562:35:562:46 | remoteString | This path depends on a $@. | testPathInjection.swift:522:24:522:78 | String(...) | user-provided value | -| testPathInjection.swift:563:41:563:52 | remoteString | testPathInjection.swift:522:24:522:78 | String(...) | testPathInjection.swift:563:41:563:52 | remoteString | This path depends on a $@. | testPathInjection.swift:522:24:522:78 | String(...) | user-provided value | -| testPathInjection.swift:585:25:585:36 | remoteString | testPathInjection.swift:583:24:583:78 | String(...) | testPathInjection.swift:585:25:585:36 | remoteString | This path depends on a $@. | testPathInjection.swift:583:24:583:78 | String(...) | user-provided value | -| testPathInjection.swift:587:41:587:52 | remoteString | testPathInjection.swift:583:24:583:78 | String(...) | testPathInjection.swift:587:41:587:52 | remoteString | This path depends on a $@. | testPathInjection.swift:583:24:583:78 | String(...) | user-provided value | -| testPathInjection.swift:589:38:589:49 | remoteString | testPathInjection.swift:583:24:583:78 | String(...) | testPathInjection.swift:589:38:589:49 | remoteString | This path depends on a $@. | testPathInjection.swift:583:24:583:78 | String(...) | user-provided value | -| testPathInjection.swift:591:22:591:33 | remoteString | testPathInjection.swift:583:24:583:78 | String(...) | testPathInjection.swift:591:22:591:33 | remoteString | This path depends on a $@. | testPathInjection.swift:583:24:583:78 | String(...) | user-provided value | +| testPathInjection.swift:502:26:502:36 | remoteData2 | testPathInjection.swift:501:23:501:75 | Data(...) | testPathInjection.swift:502:26:502:36 | remoteData2 | This path depends on a $@. | testPathInjection.swift:501:23:501:75 | Data(...) | user-provided value | +| testPathInjection.swift:512:37:512:48 | remoteString | testPathInjection.swift:506:24:506:78 | String(...) | testPathInjection.swift:512:37:512:48 | remoteString | This path depends on a $@. | testPathInjection.swift:506:24:506:78 | String(...) | user-provided value | +| testPathInjection.swift:514:33:514:44 | remoteString | testPathInjection.swift:506:24:506:78 | String(...) | testPathInjection.swift:514:33:514:44 | remoteString | This path depends on a $@. | testPathInjection.swift:506:24:506:78 | String(...) | user-provided value | +| testPathInjection.swift:531:28:531:39 | remoteString | testPathInjection.swift:521:24:521:78 | String(...) | testPathInjection.swift:531:28:531:39 | remoteString | This path depends on a $@. | testPathInjection.swift:521:24:521:78 | String(...) | user-provided value | +| testPathInjection.swift:543:32:543:43 | remoteString | testPathInjection.swift:521:24:521:78 | String(...) | testPathInjection.swift:543:32:543:43 | remoteString | This path depends on a $@. | testPathInjection.swift:521:24:521:78 | String(...) | user-provided value | +| testPathInjection.swift:544:38:544:49 | remoteString | testPathInjection.swift:521:24:521:78 | String(...) | testPathInjection.swift:544:38:544:49 | remoteString | This path depends on a $@. | testPathInjection.swift:521:24:521:78 | String(...) | user-provided value | +| testPathInjection.swift:545:45:545:56 | remoteString | testPathInjection.swift:521:24:521:78 | String(...) | testPathInjection.swift:545:45:545:56 | remoteString | This path depends on a $@. | testPathInjection.swift:521:24:521:78 | String(...) | user-provided value | +| testPathInjection.swift:549:32:549:41 | ... .pointee | testPathInjection.swift:521:24:521:78 | String(...) | testPathInjection.swift:549:32:549:41 | ... .pointee | This path depends on a $@. | testPathInjection.swift:521:24:521:78 | String(...) | user-provided value | +| testPathInjection.swift:561:35:561:46 | remoteString | testPathInjection.swift:521:24:521:78 | String(...) | testPathInjection.swift:561:35:561:46 | remoteString | This path depends on a $@. | testPathInjection.swift:521:24:521:78 | String(...) | user-provided value | +| testPathInjection.swift:562:41:562:52 | remoteString | testPathInjection.swift:521:24:521:78 | String(...) | testPathInjection.swift:562:41:562:52 | remoteString | This path depends on a $@. | testPathInjection.swift:521:24:521:78 | String(...) | user-provided value | +| testPathInjection.swift:584:25:584:36 | remoteString | testPathInjection.swift:582:24:582:78 | String(...) | testPathInjection.swift:584:25:584:36 | remoteString | This path depends on a $@. | testPathInjection.swift:582:24:582:78 | String(...) | user-provided value | +| testPathInjection.swift:586:41:586:52 | remoteString | testPathInjection.swift:582:24:582:78 | String(...) | testPathInjection.swift:586:41:586:52 | remoteString | This path depends on a $@. | testPathInjection.swift:582:24:582:78 | String(...) | user-provided value | +| testPathInjection.swift:588:38:588:49 | remoteString | testPathInjection.swift:582:24:582:78 | String(...) | testPathInjection.swift:588:38:588:49 | remoteString | This path depends on a $@. | testPathInjection.swift:582:24:582:78 | String(...) | user-provided value | +| testPathInjection.swift:590:22:590:33 | remoteString | testPathInjection.swift:582:24:582:78 | String(...) | testPathInjection.swift:590:22:590:33 | remoteString | This path depends on a $@. | testPathInjection.swift:582:24:582:78 | String(...) | user-provided value | edges | testPathInjection.swift:340:9:340:20 | remoteString | testPathInjection.swift:341:33:341:44 | remoteString | provenance | | | testPathInjection.swift:340:9:340:20 | remoteString | testPathInjection.swift:342:37:342:48 | remoteString | provenance | | @@ -132,7 +130,6 @@ edges | testPathInjection.swift:340:9:340:20 | remoteString | testPathInjection.swift:424:59:424:70 | remoteString | provenance | | | testPathInjection.swift:340:9:340:20 | remoteString | testPathInjection.swift:425:46:425:57 | remoteString | provenance | | | testPathInjection.swift:340:9:340:20 | remoteString | testPathInjection.swift:426:42:426:53 | remoteString | provenance | | -| testPathInjection.swift:340:9:340:20 | remoteString | testPathInjection.swift:436:25:436:33 | remoteUrl | provenance | | | testPathInjection.swift:340:9:340:20 | remoteString | testPathInjection.swift:437:26:437:37 | remoteString | provenance | | | testPathInjection.swift:340:9:340:20 | remoteString | testPathInjection.swift:441:28:441:39 | remoteString | provenance | | | testPathInjection.swift:340:9:340:20 | remoteString | testPathInjection.swift:443:32:443:43 | remoteString | provenance | | @@ -172,36 +169,28 @@ edges | testPathInjection.swift:342:37:342:48 | remoteString | testPathInjection.swift:342:9:342:19 | remoteNsUrl | provenance | | | testPathInjection.swift:425:46:425:57 | remoteString | testPathInjection.swift:425:46:425:76 | TypeCastExpr | provenance | | | testPathInjection.swift:426:42:426:53 | remoteString | testPathInjection.swift:426:42:426:72 | TypeCastExpr | provenance | | -| testPathInjection.swift:477:9:477:18 | remoteData | testPathInjection.swift:484:24:484:30 | buffer2 | provenance | | -| testPathInjection.swift:477:22:477:87 | Data(...) | testPathInjection.swift:477:9:477:18 | remoteData | provenance | | -| testPathInjection.swift:502:9:502:19 | remoteData2 | testPathInjection.swift:503:26:503:36 | remoteData2 | provenance | | -| testPathInjection.swift:502:23:502:75 | Data(...) | testPathInjection.swift:502:9:502:19 | remoteData2 | provenance | | -| testPathInjection.swift:507:9:507:20 | remoteString | testPathInjection.swift:513:37:513:48 | remoteString | provenance | | -| testPathInjection.swift:507:9:507:20 | remoteString | testPathInjection.swift:515:33:515:44 | remoteString | provenance | | -| testPathInjection.swift:507:24:507:78 | String(...) | testPathInjection.swift:507:9:507:20 | remoteString | provenance | | -| testPathInjection.swift:522:9:522:20 | remoteString | testPathInjection.swift:528:28:528:29 | u1 | provenance | | -| testPathInjection.swift:522:9:522:20 | remoteString | testPathInjection.swift:530:28:530:29 | u1 | provenance | | -| testPathInjection.swift:522:9:522:20 | remoteString | testPathInjection.swift:532:28:532:39 | remoteString | provenance | | -| testPathInjection.swift:522:9:522:20 | remoteString | testPathInjection.swift:544:32:544:43 | remoteString | provenance | | -| testPathInjection.swift:522:9:522:20 | remoteString | testPathInjection.swift:545:38:545:49 | remoteString | provenance | | -| testPathInjection.swift:522:9:522:20 | remoteString | testPathInjection.swift:546:45:546:56 | remoteString | provenance | | -| testPathInjection.swift:522:9:522:20 | remoteString | testPathInjection.swift:549:18:549:29 | remoteString | provenance | | -| testPathInjection.swift:522:9:522:20 | remoteString | testPathInjection.swift:562:35:562:46 | remoteString | provenance | | -| testPathInjection.swift:522:9:522:20 | remoteString | testPathInjection.swift:563:41:563:52 | remoteString | provenance | | -| testPathInjection.swift:522:24:522:78 | String(...) | testPathInjection.swift:522:9:522:20 | remoteString | provenance | | -| testPathInjection.swift:528:28:528:29 | u1 | testPathInjection.swift:16:74:16:77 | self | provenance | | -| testPathInjection.swift:528:28:528:29 | u1 | testPathInjection.swift:528:28:528:66 | ... .appendingPathComponent(...) | provenance | | -| testPathInjection.swift:528:28:528:66 | ... .appendingPathComponent(...) | testPathInjection.swift:16:74:16:77 | self | provenance | | -| testPathInjection.swift:528:28:528:66 | ... .appendingPathComponent(...) | testPathInjection.swift:528:28:528:93 | ... .appendingPathComponent(...) | provenance | | -| testPathInjection.swift:549:5:549:6 | [post] s1 [pointee] | testPathInjection.swift:550:32:550:33 | s1 [pointee] | provenance | | -| testPathInjection.swift:549:5:549:14 | ... .pointee | testPathInjection.swift:549:5:549:6 | [post] s1 [pointee] | provenance | | -| testPathInjection.swift:549:18:549:29 | remoteString | testPathInjection.swift:549:5:549:14 | ... .pointee | provenance | | -| testPathInjection.swift:550:32:550:33 | s1 [pointee] | testPathInjection.swift:550:32:550:41 | ... .pointee | provenance | | -| testPathInjection.swift:583:9:583:20 | remoteString | testPathInjection.swift:585:25:585:36 | remoteString | provenance | | -| testPathInjection.swift:583:9:583:20 | remoteString | testPathInjection.swift:587:41:587:52 | remoteString | provenance | | -| testPathInjection.swift:583:9:583:20 | remoteString | testPathInjection.swift:589:38:589:49 | remoteString | provenance | | -| testPathInjection.swift:583:9:583:20 | remoteString | testPathInjection.swift:591:22:591:33 | remoteString | provenance | | -| testPathInjection.swift:583:24:583:78 | String(...) | testPathInjection.swift:583:9:583:20 | remoteString | provenance | | +| testPathInjection.swift:501:9:501:19 | remoteData2 | testPathInjection.swift:502:26:502:36 | remoteData2 | provenance | | +| testPathInjection.swift:501:23:501:75 | Data(...) | testPathInjection.swift:501:9:501:19 | remoteData2 | provenance | | +| testPathInjection.swift:506:9:506:20 | remoteString | testPathInjection.swift:512:37:512:48 | remoteString | provenance | | +| testPathInjection.swift:506:9:506:20 | remoteString | testPathInjection.swift:514:33:514:44 | remoteString | provenance | | +| testPathInjection.swift:506:24:506:78 | String(...) | testPathInjection.swift:506:9:506:20 | remoteString | provenance | | +| testPathInjection.swift:521:9:521:20 | remoteString | testPathInjection.swift:531:28:531:39 | remoteString | provenance | | +| testPathInjection.swift:521:9:521:20 | remoteString | testPathInjection.swift:543:32:543:43 | remoteString | provenance | | +| testPathInjection.swift:521:9:521:20 | remoteString | testPathInjection.swift:544:38:544:49 | remoteString | provenance | | +| testPathInjection.swift:521:9:521:20 | remoteString | testPathInjection.swift:545:45:545:56 | remoteString | provenance | | +| testPathInjection.swift:521:9:521:20 | remoteString | testPathInjection.swift:548:18:548:29 | remoteString | provenance | | +| testPathInjection.swift:521:9:521:20 | remoteString | testPathInjection.swift:561:35:561:46 | remoteString | provenance | | +| testPathInjection.swift:521:9:521:20 | remoteString | testPathInjection.swift:562:41:562:52 | remoteString | provenance | | +| testPathInjection.swift:521:24:521:78 | String(...) | testPathInjection.swift:521:9:521:20 | remoteString | provenance | | +| testPathInjection.swift:548:5:548:6 | [post] s1 [pointee] | testPathInjection.swift:549:32:549:33 | s1 [pointee] | provenance | | +| testPathInjection.swift:548:5:548:14 | ... .pointee | testPathInjection.swift:548:5:548:6 | [post] s1 [pointee] | provenance | | +| testPathInjection.swift:548:18:548:29 | remoteString | testPathInjection.swift:548:5:548:14 | ... .pointee | provenance | | +| testPathInjection.swift:549:32:549:33 | s1 [pointee] | testPathInjection.swift:549:32:549:41 | ... .pointee | provenance | | +| testPathInjection.swift:582:9:582:20 | remoteString | testPathInjection.swift:584:25:584:36 | remoteString | provenance | | +| testPathInjection.swift:582:9:582:20 | remoteString | testPathInjection.swift:586:41:586:52 | remoteString | provenance | | +| testPathInjection.swift:582:9:582:20 | remoteString | testPathInjection.swift:588:38:588:49 | remoteString | provenance | | +| testPathInjection.swift:582:9:582:20 | remoteString | testPathInjection.swift:590:22:590:33 | remoteString | provenance | | +| testPathInjection.swift:582:24:582:78 | String(...) | testPathInjection.swift:582:9:582:20 | remoteString | provenance | | nodes | testPathInjection.swift:340:9:340:20 | remoteString | semmle.label | remoteString | | testPathInjection.swift:340:24:340:78 | String(...) | semmle.label | String(...) | @@ -285,36 +274,30 @@ nodes | testPathInjection.swift:482:22:482:33 | remoteString | semmle.label | remoteString | | testPathInjection.swift:486:25:486:36 | remoteString | semmle.label | remoteString | | testPathInjection.swift:498:49:498:60 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:502:9:502:19 | remoteData2 | semmle.label | remoteData2 | -| testPathInjection.swift:502:23:502:75 | Data(...) | semmle.label | Data(...) | -| testPathInjection.swift:503:26:503:36 | remoteData2 | semmle.label | remoteData2 | -| testPathInjection.swift:507:9:507:20 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:507:24:507:78 | String(...) | semmle.label | String(...) | -| testPathInjection.swift:513:37:513:48 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:515:33:515:44 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:522:9:522:20 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:522:24:522:78 | String(...) | semmle.label | String(...) | -| testPathInjection.swift:528:28:528:29 | u1 | semmle.label | u1 | -| testPathInjection.swift:528:28:528:66 | ... .appendingPathComponent(...) | semmle.label | ... .appendingPathComponent(...) | -| testPathInjection.swift:528:28:528:93 | ... .appendingPathComponent(...) | semmle.label | ... .appendingPathComponent(...) | -| testPathInjection.swift:530:28:530:29 | u1 | semmle.label | u1 | -| testPathInjection.swift:532:28:532:39 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:544:32:544:43 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:545:38:545:49 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:546:45:546:56 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:549:5:549:6 | [post] s1 [pointee] | semmle.label | [post] s1 [pointee] | -| testPathInjection.swift:549:5:549:14 | ... .pointee | semmle.label | ... .pointee | -| testPathInjection.swift:549:18:549:29 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:550:32:550:33 | s1 [pointee] | semmle.label | s1 [pointee] | -| testPathInjection.swift:550:32:550:41 | ... .pointee | semmle.label | ... .pointee | -| testPathInjection.swift:562:35:562:46 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:563:41:563:52 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:583:9:583:20 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:583:24:583:78 | String(...) | semmle.label | String(...) | -| testPathInjection.swift:585:25:585:36 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:587:41:587:52 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:589:38:589:49 | remoteString | semmle.label | remoteString | -| testPathInjection.swift:591:22:591:33 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:501:9:501:19 | remoteData2 | semmle.label | remoteData2 | +| testPathInjection.swift:501:23:501:75 | Data(...) | semmle.label | Data(...) | +| testPathInjection.swift:502:26:502:36 | remoteData2 | semmle.label | remoteData2 | +| testPathInjection.swift:506:9:506:20 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:506:24:506:78 | String(...) | semmle.label | String(...) | +| testPathInjection.swift:512:37:512:48 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:514:33:514:44 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:521:9:521:20 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:521:24:521:78 | String(...) | semmle.label | String(...) | +| testPathInjection.swift:531:28:531:39 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:543:32:543:43 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:544:38:544:49 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:545:45:545:56 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:548:5:548:6 | [post] s1 [pointee] | semmle.label | [post] s1 [pointee] | +| testPathInjection.swift:548:5:548:14 | ... .pointee | semmle.label | ... .pointee | +| testPathInjection.swift:548:18:548:29 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:549:32:549:33 | s1 [pointee] | semmle.label | s1 [pointee] | +| testPathInjection.swift:549:32:549:41 | ... .pointee | semmle.label | ... .pointee | +| testPathInjection.swift:561:35:561:46 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:562:41:562:52 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:582:9:582:20 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:582:24:582:78 | String(...) | semmle.label | String(...) | +| testPathInjection.swift:584:25:584:36 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:586:41:586:52 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:588:38:588:49 | remoteString | semmle.label | remoteString | +| testPathInjection.swift:590:22:590:33 | remoteString | semmle.label | remoteString | subpaths -| testPathInjection.swift:528:28:528:29 | u1 | testPathInjection.swift:16:74:16:77 | self | testPathInjection.swift:16:74:16:77 | self | testPathInjection.swift:528:28:528:66 | ... .appendingPathComponent(...) | -| testPathInjection.swift:528:28:528:66 | ... .appendingPathComponent(...) | testPathInjection.swift:16:74:16:77 | self | testPathInjection.swift:16:74:16:77 | self | testPathInjection.swift:528:28:528:93 | ... .appendingPathComponent(...) | From 303385ebc9e9c382fa64f68c65977e26975eea91 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 8 Oct 2026 17:11:08 +0200 Subject: [PATCH 18/18] unified: Fix typo --- .../codeql/unified/internal/dataflow/ConstructorPatterns.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/ConstructorPatterns.qll b/unified/ql/lib/codeql/unified/internal/dataflow/ConstructorPatterns.qll index 84c471141e50..c132f4b57a26 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/ConstructorPatterns.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/ConstructorPatterns.qll @@ -16,7 +16,7 @@ class ConstructorPattern extends CallExpr { } /** - * Gets the unqualified name of the enum-case contructor that might be referenced by `call`. + * Gets the unqualified name of the enum-case constructor that might be referenced by `call`. */ private string getShortConstructorName(CallExpr call) { result = call.getCallee().(MemberAccessExpr).getMemberName()