From 1117fd62bae94f59e6e463f94a8b653c60855973 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 5 Oct 2026 11:50:29 +0200 Subject: [PATCH 1/9] 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 922fc073a070499fa1dddaab6ea56cbe36492813 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 5 Oct 2026 12:53:22 +0200 Subject: [PATCH 2/9] 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 7ae6ed5e6691..5ed6c3fe41cd 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 | | @@ -227,6 +279,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(...) | @@ -430,6 +540,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 0632f1e9ef16baf35d814169f95c6702145c4d48 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 5 Oct 2026 13:12:54 +0200 Subject: [PATCH 3/9] unified: Model postfix "!" as a read step --- .../internal/dataflow/DataFlowPluginSwift.qll | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll index ec03bf1536d3..1025a2d08ed0 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll @@ -25,5 +25,20 @@ private class SwiftDataFlowPlugin extends DataFlowPlugin { step.value() and node2.isResultValue(call) ) + or + exists(UnaryExpr expr | + expr.getOperator().(PostfixOperator).getValue() = "!" and + node1.isResultValue(expr.getOperand()) and + step.readName("some.0") and + node2.isResultValue(expr) + ) + or + // Taint flow through URL(string: x). TODO: Model with MaD and flow summaries + exists(CallExpr call | + call.getCallee().(Identifier).getValue() = ["URL", "NSURL"] and + node1.isResultValue(call.getNamedArgument("string")) and + step.taint() and + node2.isResultValue(call) + ) } } From 8e62b1c0c290c3f95eaeac253b6864b6a953dce0 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 5 Oct 2026 13:13:27 +0200 Subject: [PATCH 4/9] 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 5ed6c3fe41cd..fc412b9cae64 100644 --- a/unified/ql/test/library-tests/dataflow/test.expected +++ b/unified/ql/test/library-tests/dataflow/test.expected @@ -230,6 +230,54 @@ edges | test.swift:175:25:175:39 | source(...) | test.swift:175:15:175:39 | ... + ... | provenance | | | test.swift:175:42:175:66 | ... + ... | test.swift:175:14:175:67 | TupleExpr [1] | provenance | | | test.swift:175:52:175:66 | source(...) | test.swift:175:42:175:66 | ... + ... | provenance | | +| test.swift:180:22:180:22 | s | test.swift:180:58:180:58 | s | provenance | | +| test.swift:182:25:182:25 | s | test.swift:182:62:182:62 | s | provenance | | +| test.swift:184:30:184:30 | s | test.swift:184:73:184:73 | s | provenance | | +| test.swift:187:16:187:45 | asyncIdentity(...) | test.swift:187:10:187:45 | await ... | provenance | | +| test.swift:187:30:187:44 | source(...) | test.swift:180:22:180:22 | s | provenance | | +| test.swift:187:30:187:44 | source(...) | test.swift:187:16:187:45 | asyncIdentity(...) | provenance | | +| test.swift:191:14:191:46 | throwingIdentity(...) | test.swift:191:10:191:46 | try ... | provenance | | +| test.swift:191:31:191:45 | source(...) | test.swift:182:25:182:25 | s | provenance | | +| test.swift:191:31:191:45 | source(...) | test.swift:191:14:191:46 | throwingIdentity(...) | provenance | | +| test.swift:192:9:192:11 | opt [some.0] | test.swift:193:12:193:14 | opt [some.0] | provenance | | +| test.swift:192:15:192:52 | try? ... [some.0] | test.swift:192:9:192:11 | opt [some.0] | provenance | | +| test.swift:192:20:192:52 | throwingIdentity(...) | test.swift:192:15:192:52 | try? ... [some.0] | provenance | | +| test.swift:192:37:192:51 | source(...) | test.swift:182:25:182:25 | s | provenance | | +| test.swift:192:37:192:51 | source(...) | test.swift:192:20:192:52 | throwingIdentity(...) | provenance | | +| test.swift:193:8:193:14 | ... .some(...) [some.0] | test.swift:193:8:193:14 | ExprPattern | provenance | | +| test.swift:193:8:193:14 | ExprPattern | test.swift:193:12:193:14 | opt | provenance | | +| test.swift:193:12:193:14 | opt | test.swift:194:14:194:16 | opt | provenance | | +| test.swift:193:12:193:14 | opt [some.0] | test.swift:193:8:193:14 | ... .some(...) [some.0] | provenance | | +| test.swift:196:15:196:47 | throwingIdentity(...) | test.swift:196:10:196:47 | try! ... | provenance | | +| test.swift:196:32:196:46 | source(...) | test.swift:182:25:182:25 | s | provenance | | +| test.swift:196:32:196:46 | source(...) | test.swift:196:15:196:47 | throwingIdentity(...) | provenance | | +| test.swift:200:20:200:57 | asyncThrowingIdentity(...) | test.swift:200:10:200:57 | try ... | provenance | | +| test.swift:200:42:200:56 | source(...) | test.swift:184:30:184:30 | s | provenance | | +| test.swift:200:42:200:56 | source(...) | test.swift:200:20:200:57 | asyncThrowingIdentity(...) | 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:63 | try? ... [some.0] | test.swift:201:9:201:11 | opt [some.0] | provenance | | +| test.swift:201:20:201:63 | await ... | test.swift:201:15:201:63 | try? ... [some.0] | provenance | | +| test.swift:201:26:201:63 | asyncThrowingIdentity(...) | test.swift:201:20:201:63 | await ... | provenance | | +| test.swift:201:48:201:62 | source(...) | test.swift:184:30:184:30 | s | provenance | | +| test.swift:201:48:201:62 | source(...) | test.swift:201:26:201:63 | asyncThrowingIdentity(...) | 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:21:205:58 | asyncThrowingIdentity(...) | test.swift:205:10:205:58 | try! ... | provenance | | +| test.swift:205:43:205:57 | source(...) | test.swift:184:30:184:30 | s | provenance | | +| test.swift:205:43:205:57 | source(...) | test.swift:205:21:205:58 | asyncThrowingIdentity(...) | provenance | | +| test.swift:209:9:209:9 | x | test.swift:210:10:210:10 | x | provenance | | +| test.swift:209:9:209:9 | x | test.swift:211:10:211:10 | x | provenance | | +| test.swift:209:9:209:9 | x | test.swift:212:16:212:16 | x | provenance | | +| test.swift:209:13:209:27 | source(...) | test.swift:209:9:209:9 | x | provenance | | +| test.swift:210:10:210:10 | x | test.swift:210:10:210:20 | TypeCastExpr | provenance | | +| test.swift:211:10:211:10 | x | test.swift:211:10:211:21 | TypeCastExpr | provenance | | +| test.swift:212:8:212:12 | ... .some(...) [some.0] | test.swift:212:8:212:12 | ExprPattern | provenance | | +| test.swift:212:8:212:12 | ExprPattern | test.swift:212:12:212:12 | y | provenance | | +| test.swift:212:12:212:12 | y | test.swift:213:14:213:14 | y | provenance | | +| test.swift:212:16:212:16 | x | test.swift:212:16:212:27 | TypeCastExpr [some.0] | provenance | | +| test.swift:212:16:212:27 | TypeCastExpr [some.0] | test.swift:212:8:212: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 | @@ -517,9 +565,68 @@ nodes | test.swift:175:52:175:66 | source(...) | semmle.label | source(...) | | test.swift:176:10:176:10 | a | semmle.label | a | | test.swift:177:10:177:10 | b | semmle.label | b | +| test.swift:180:22:180:22 | s | semmle.label | s | +| test.swift:180:58:180:58 | s | semmle.label | s | +| test.swift:182:25:182:25 | s | semmle.label | s | +| test.swift:182:62:182:62 | s | semmle.label | s | +| test.swift:184:30:184:30 | s | semmle.label | s | +| test.swift:184:73:184:73 | s | semmle.label | s | +| test.swift:187:10:187:45 | await ... | semmle.label | await ... | +| test.swift:187:16:187:45 | asyncIdentity(...) | semmle.label | asyncIdentity(...) | +| test.swift:187:30:187:44 | source(...) | semmle.label | source(...) | +| test.swift:191:10:191:46 | try ... | semmle.label | try ... | +| test.swift:191:14:191:46 | throwingIdentity(...) | semmle.label | throwingIdentity(...) | +| test.swift:191:31:191:45 | source(...) | semmle.label | source(...) | +| test.swift:192:9:192:11 | opt [some.0] | semmle.label | opt [some.0] | +| test.swift:192:15:192:52 | try? ... [some.0] | semmle.label | try? ... [some.0] | +| test.swift:192:20:192:52 | throwingIdentity(...) | semmle.label | throwingIdentity(...) | +| test.swift:192:37:192:51 | source(...) | semmle.label | source(...) | +| test.swift:193:8:193:14 | ... .some(...) [some.0] | semmle.label | ... .some(...) [some.0] | +| test.swift:193:8:193:14 | ExprPattern | semmle.label | ExprPattern | +| test.swift:193:12:193:14 | opt | semmle.label | opt | +| test.swift:193:12:193:14 | opt [some.0] | semmle.label | opt [some.0] | +| test.swift:194:14:194:16 | opt | semmle.label | opt | +| test.swift:196:10:196:47 | try! ... | semmle.label | try! ... | +| test.swift:196:15:196:47 | throwingIdentity(...) | semmle.label | throwingIdentity(...) | +| test.swift:196:32:196:46 | source(...) | semmle.label | source(...) | +| test.swift:200:10:200:57 | try ... | semmle.label | try ... | +| test.swift:200:20:200:57 | asyncThrowingIdentity(...) | semmle.label | asyncThrowingIdentity(...) | +| test.swift:200:42:200:56 | source(...) | semmle.label | source(...) | +| test.swift:201:9:201:11 | opt [some.0] | semmle.label | opt [some.0] | +| test.swift:201:15:201:63 | try? ... [some.0] | semmle.label | try? ... [some.0] | +| test.swift:201:20:201:63 | await ... | semmle.label | await ... | +| test.swift:201:26:201:63 | asyncThrowingIdentity(...) | semmle.label | asyncThrowingIdentity(...) | +| test.swift:201:48:201:62 | 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:58 | try! ... | semmle.label | try! ... | +| test.swift:205:21:205:58 | asyncThrowingIdentity(...) | semmle.label | asyncThrowingIdentity(...) | +| test.swift:205:43:205:57 | source(...) | semmle.label | source(...) | +| test.swift:209:9:209:9 | x | semmle.label | x | +| test.swift:209:13:209:27 | source(...) | semmle.label | source(...) | +| test.swift:210:10:210:10 | x | semmle.label | x | +| test.swift:210:10:210:20 | TypeCastExpr | semmle.label | TypeCastExpr | +| test.swift:211:10:211:10 | x | semmle.label | x | +| test.swift:211:10:211:21 | TypeCastExpr | semmle.label | TypeCastExpr | +| test.swift:212:8:212:12 | ... .some(...) [some.0] | semmle.label | ... .some(...) [some.0] | +| test.swift:212:8:212:12 | ExprPattern | semmle.label | ExprPattern | +| test.swift:212:12:212:12 | y | semmle.label | y | +| test.swift:212:16:212:16 | x | semmle.label | x | +| test.swift:212:16:212:27 | TypeCastExpr [some.0] | semmle.label | TypeCastExpr [some.0] | +| test.swift:213:14:213: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(...) | @@ -594,3 +701,13 @@ testFailures | test.swift:168:10:168:12 | ... .0 | test.swift:167:29:167:43 | source(...) | test.swift:168:10:168:12 | ... .0 | $@ | test.swift:167:29:167:43 | source(...) | source(...) | | test.swift:176:10:176:10 | a | test.swift:175:25:175:39 | source(...) | test.swift:176:10:176:10 | a | $@ | test.swift:175:25:175:39 | source(...) | source(...) | | 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:187:10:187:45 | await ... | test.swift:187:30:187:44 | source(...) | test.swift:187:10:187:45 | await ... | $@ | test.swift:187:30:187:44 | source(...) | source(...) | +| test.swift:191:10:191:46 | try ... | test.swift:191:31:191:45 | source(...) | test.swift:191:10:191:46 | try ... | $@ | test.swift:191:31:191:45 | source(...) | source(...) | +| test.swift:194:14:194:16 | opt | test.swift:192:37:192:51 | source(...) | test.swift:194:14:194:16 | opt | $@ | test.swift:192:37:192:51 | source(...) | source(...) | +| test.swift:196:10:196:47 | try! ... | test.swift:196:32:196:46 | source(...) | test.swift:196:10:196:47 | try! ... | $@ | test.swift:196:32:196:46 | source(...) | source(...) | +| test.swift:200:10:200:57 | try ... | test.swift:200:42:200:56 | source(...) | test.swift:200:10:200:57 | try ... | $@ | test.swift:200:42:200:56 | source(...) | source(...) | +| test.swift:203:14:203:16 | opt | test.swift:201:48:201:62 | source(...) | test.swift:203:14:203:16 | opt | $@ | test.swift:201:48:201:62 | source(...) | source(...) | +| test.swift:205:10:205:58 | try! ... | test.swift:205:43:205:57 | source(...) | test.swift:205:10:205:58 | try! ... | $@ | test.swift:205:43:205:57 | source(...) | source(...) | +| test.swift:210:10:210:20 | TypeCastExpr | test.swift:209:13:209:27 | source(...) | test.swift:210:10:210:20 | TypeCastExpr | $@ | test.swift:209:13:209:27 | source(...) | source(...) | +| test.swift:211:10:211:21 | TypeCastExpr | test.swift:209:13:209:27 | source(...) | test.swift:211:10:211:21 | TypeCastExpr | $@ | test.swift:209:13:209:27 | source(...) | source(...) | +| test.swift:213:14:213:14 | y | test.swift:209:13:209:27 | source(...) | test.swift:213:14:213:14 | y | $@ | test.swift:209:13:209:27 | source(...) | source(...) | diff --git a/unified/ql/test/library-tests/dataflow/test.swift b/unified/ql/test/library-tests/dataflow/test.swift index a7fa42ee6100..047a48dae29f 100644 --- a/unified/ql/test/library-tests/dataflow/test.swift +++ b/unified/ql/test/library-tests/dataflow/test.swift @@ -176,3 +176,40 @@ func t19() { sink(a) // $ hasTaintFlow=t19.1 sink(b) // $ hasTaintFlow=t19.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 943311277ba30fa0c5254b839bc894e29cccd8d7 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 5 Oct 2026 13:26:24 +0200 Subject: [PATCH 5/9] 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 fc412b9cae64..7c4450b54288 100644 --- a/unified/ql/test/library-tests/dataflow/test.expected +++ b/unified/ql/test/library-tests/dataflow/test.expected @@ -278,6 +278,12 @@ edges | test.swift:212:12:212:12 | y | test.swift:213:14:213:14 | y | provenance | | | test.swift:212:16:212:16 | x | test.swift:212:16:212:27 | TypeCastExpr [some.0] | provenance | | | test.swift:212:16:212:27 | TypeCastExpr [some.0] | test.swift:212:8:212:12 | ... .some(...) [some.0] | provenance | | +| test.swift:218:9:218:9 | x [ArrayElement] | test.swift:219:20:219:20 | x [ArrayElement] | provenance | | +| test.swift:218:13:218:46 | ArrayLiteral [ArrayElement] | test.swift:218:9:218:9 | x [ArrayElement] | provenance | | +| test.swift:218:14:218:28 | source(...) | test.swift:218:13:218:46 | ArrayLiteral [ArrayElement] | provenance | | +| test.swift:218:31:218:45 | source(...) | test.swift:218:13:218:46 | ArrayLiteral [ArrayElement] | provenance | | +| test.swift:219:9:219:15 | element | test.swift:220:14:220:20 | element | provenance | | +| test.swift:219:20:219:20 | x [ArrayElement] | test.swift:219:9:219:15 | element | provenance | | nodes | calls.swift:7:19:7:19 | x | semmle.label | x | | calls.swift:8:14:8:14 | x | semmle.label | x | @@ -617,6 +623,13 @@ nodes | test.swift:212:16:212:16 | x | semmle.label | x | | test.swift:212:16:212:27 | TypeCastExpr [some.0] | semmle.label | TypeCastExpr [some.0] | | test.swift:213:14:213:14 | y | semmle.label | y | +| test.swift:218:9:218:9 | x [ArrayElement] | semmle.label | x [ArrayElement] | +| test.swift:218:13:218:46 | ArrayLiteral [ArrayElement] | semmle.label | ArrayLiteral [ArrayElement] | +| test.swift:218:14:218:28 | source(...) | semmle.label | source(...) | +| test.swift:218:31:218:45 | source(...) | semmle.label | source(...) | +| test.swift:219:9:219:15 | element | semmle.label | element | +| test.swift:219:20:219:20 | x [ArrayElement] | semmle.label | x [ArrayElement] | +| test.swift:220:14:220: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(...) | @@ -711,3 +724,5 @@ testFailures | test.swift:210:10:210:20 | TypeCastExpr | test.swift:209:13:209:27 | source(...) | test.swift:210:10:210:20 | TypeCastExpr | $@ | test.swift:209:13:209:27 | source(...) | source(...) | | test.swift:211:10:211:21 | TypeCastExpr | test.swift:209:13:209:27 | source(...) | test.swift:211:10:211:21 | TypeCastExpr | $@ | test.swift:209:13:209:27 | source(...) | source(...) | | test.swift:213:14:213:14 | y | test.swift:209:13:209:27 | source(...) | test.swift:213:14:213:14 | y | $@ | test.swift:209:13:209:27 | source(...) | source(...) | +| test.swift:220:14:220:20 | element | test.swift:218:14:218:28 | source(...) | test.swift:220:14:220:20 | element | $@ | test.swift:218:14:218:28 | source(...) | source(...) | +| test.swift:220:14:220:20 | element | test.swift:218:31:218:45 | source(...) | test.swift:220:14:220:20 | element | $@ | test.swift:218:31:218:45 | source(...) | source(...) | diff --git a/unified/ql/test/library-tests/dataflow/test.swift b/unified/ql/test/library-tests/dataflow/test.swift index 047a48dae29f..f3c1c312546b 100644 --- a/unified/ql/test/library-tests/dataflow/test.swift +++ b/unified/ql/test/library-tests/dataflow/test.swift @@ -213,3 +213,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 098db80ecd2ab9b9882ba2ff7185ed76a2bc8b94 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 5 Oct 2026 13:42:40 +0200 Subject: [PATCH 6/9] 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 --- .../unified/internal/dataflow/DataFlowPluginSwift.qll | 2 +- .../CWE-022/PathInjection/PathInjectionTest.expected | 10 ++++++++++ .../CWE-022/PathInjection/testPathInjection.swift | 6 +++--- 3 files changed, 14 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 3e90894e5b86..f80be1db2964 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 @@ -67,6 +67,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 | @@ -162,6 +164,8 @@ edges | testPathInjection.swift:340:9:340:20 | remoteString | testPathInjection.swift:421:26:421:34 | remoteUrl | 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 | | @@ -173,6 +177,8 @@ edges | testPathInjection.swift:340:9:340:20 | remoteString | testPathInjection.swift:486:25:486:36 | remoteString | provenance | | | testPathInjection.swift:340:9:340:20 | remoteString | testPathInjection.swift:498:49:498:60 | remoteString | provenance | | | testPathInjection.swift:340:24:340:78 | String(...) | testPathInjection.swift:340:9:340:20 | remoteString | 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 | | @@ -273,6 +279,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 9cc20d430bdc..e991161b916d 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 7/9] 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 6c7140019a9aad9f467d131fa2790fbe0b33135f Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 5 Oct 2026 13:52:44 +0200 Subject: [PATCH 8/9] 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 3ae09d533f1e..434b0645563b 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 3178aa107a07096ecc3ebf20483af1a06ff0c512 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 5 Oct 2026 14:00:05 +0200 Subject: [PATCH 9/9] 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 7c4450b54288..6dd50c9ae46d 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 | | @@ -244,8 +236,7 @@ edges | test.swift:192:20:192:52 | throwingIdentity(...) | test.swift:192:15:192:52 | try? ... [some.0] | provenance | | | test.swift:192:37:192:51 | source(...) | test.swift:182:25:182:25 | s | provenance | | | test.swift:192:37:192:51 | source(...) | test.swift:192:20:192:52 | throwingIdentity(...) | provenance | | -| test.swift:193:8:193:14 | ... .some(...) [some.0] | test.swift:193:8:193:14 | ExprPattern | provenance | | -| test.swift:193:8:193:14 | ExprPattern | test.swift:193:12:193:14 | opt | provenance | | +| test.swift:193:8:193:14 | ... .some(...) [some.0] | test.swift:193:12:193:14 | opt | provenance | | | test.swift:193:12:193:14 | opt | test.swift:194:14:194:16 | opt | provenance | | | test.swift:193:12:193:14 | opt [some.0] | test.swift:193:8:193:14 | ... .some(...) [some.0] | provenance | | | test.swift:196:15:196:47 | throwingIdentity(...) | test.swift:196:10:196:47 | try! ... | provenance | | @@ -260,8 +251,7 @@ edges | test.swift:201:26:201:63 | asyncThrowingIdentity(...) | test.swift:201:20:201:63 | await ... | provenance | | | test.swift:201:48:201:62 | source(...) | test.swift:184:30:184:30 | s | provenance | | | test.swift:201:48:201:62 | source(...) | test.swift:201:26:201:63 | asyncThrowingIdentity(...) | 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:21:205:58 | asyncThrowingIdentity(...) | test.swift:205:10:205:58 | try! ... | provenance | | @@ -273,8 +263,7 @@ edges | test.swift:209:13:209:27 | source(...) | test.swift:209:9:209:9 | x | provenance | | | test.swift:210:10:210:10 | x | test.swift:210:10:210:20 | TypeCastExpr | provenance | | | test.swift:211:10:211:10 | x | test.swift:211:10:211:21 | TypeCastExpr | provenance | | -| test.swift:212:8:212:12 | ... .some(...) [some.0] | test.swift:212:8:212:12 | ExprPattern | provenance | | -| test.swift:212:8:212:12 | ExprPattern | test.swift:212:12:212:12 | y | provenance | | +| test.swift:212:8:212:12 | ... .some(...) [some.0] | test.swift:212:12:212:12 | y | provenance | | | test.swift:212:12:212:12 | y | test.swift:213:14:213:14 | y | provenance | | | test.swift:212:16:212:16 | x | test.swift:212:16:212:27 | TypeCastExpr [some.0] | provenance | | | test.swift:212:16:212:27 | TypeCastExpr [some.0] | test.swift:212:8:212:12 | ... .some(...) [some.0] | provenance | | @@ -338,7 +327,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] | @@ -346,7 +334,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] | @@ -354,19 +341,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 | @@ -374,7 +358,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 | @@ -383,12 +366,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] | @@ -588,7 +569,6 @@ nodes | test.swift:192:20:192:52 | throwingIdentity(...) | semmle.label | throwingIdentity(...) | | test.swift:192:37:192:51 | source(...) | semmle.label | source(...) | | test.swift:193:8:193:14 | ... .some(...) [some.0] | semmle.label | ... .some(...) [some.0] | -| test.swift:193:8:193:14 | ExprPattern | semmle.label | ExprPattern | | test.swift:193:12:193:14 | opt | semmle.label | opt | | test.swift:193:12:193:14 | opt [some.0] | semmle.label | opt [some.0] | | test.swift:194:14:194:16 | opt | semmle.label | opt | @@ -604,7 +584,6 @@ nodes | test.swift:201:26:201:63 | asyncThrowingIdentity(...) | semmle.label | asyncThrowingIdentity(...) | | test.swift:201:48:201:62 | 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 | @@ -618,7 +597,6 @@ nodes | test.swift:211:10:211:10 | x | semmle.label | x | | test.swift:211:10:211:21 | TypeCastExpr | semmle.label | TypeCastExpr | | test.swift:212:8:212:12 | ... .some(...) [some.0] | semmle.label | ... .some(...) [some.0] | -| test.swift:212:8:212:12 | ExprPattern | semmle.label | ExprPattern | | test.swift:212:12:212:12 | y | semmle.label | y | | test.swift:212:16:212:16 | x | semmle.label | x | | test.swift:212:16:212:27 | TypeCastExpr [some.0] | semmle.label | TypeCastExpr [some.0] |