From f6bd6e8b526355654a1e8235dec732758e9d4617 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 11 Sep 2026 14:57:46 +0200 Subject: [PATCH 01/30] unified: Add ExprPositions --- .../lib/codeql/unified/internal/AstExtra.qll | 21 +++--- .../codeql/unified/internal/ExprPositions.qll | 66 +++++++++++++++++++ 2 files changed, 79 insertions(+), 8 deletions(-) create mode 100644 unified/ql/lib/codeql/unified/internal/ExprPositions.qll diff --git a/unified/ql/lib/codeql/unified/internal/AstExtra.qll b/unified/ql/lib/codeql/unified/internal/AstExtra.qll index 5e8e54faa61a..64ab0262ea56 100644 --- a/unified/ql/lib/codeql/unified/internal/AstExtra.qll +++ b/unified/ql/lib/codeql/unified/internal/AstExtra.qll @@ -60,15 +60,20 @@ module Public { TopLevelStmt() { this = any(TopLevel t).getBody().getAStmt() } } + /** An identifier appearing in the context of a break/continue label, argument/parameter name, or name of a member lookup. */ + final class IdentifierLabel extends Identifier { + IdentifierLabel() { + this = any(MemberAccessExpr e).getMemberNameNode() or + this = any(Argument a).getNameNode() or + this = any(Parameter p).getExternalNameNode() or + this = any(LabeledStmt stmt).getLabelNameNode() or + this = any(BreakExpr expr).getLabelNameNode() or + this = any(ContinueExpr expr).getLabelNameNode() + } + } + /** An identifier appearing in the context of an expression, pattern, or type annotation. */ final class IdentifierExpr extends Identifier { - IdentifierExpr() { - not this = any(MemberAccessExpr e).getMemberNameNode() and - not this = any(Argument a).getNameNode() and - not this = any(Parameter p).getExternalNameNode() and - not this = any(LabeledStmt stmt).getLabelNameNode() and - not this = any(BreakExpr expr).getLabelNameNode() and - not this = any(ContinueExpr expr).getLabelNameNode() - } + IdentifierExpr() { not this instanceof IdentifierLabel } } } diff --git a/unified/ql/lib/codeql/unified/internal/ExprPositions.qll b/unified/ql/lib/codeql/unified/internal/ExprPositions.qll new file mode 100644 index 000000000000..4c38c5d241c9 --- /dev/null +++ b/unified/ql/lib/codeql/unified/internal/ExprPositions.qll @@ -0,0 +1,66 @@ +private import unified +private import NameBinding as NameBinding + +/** + * Holds if `expr` appears in the context of a type annotation. + */ +predicate isInTypeContext(Expr expr) { + expr = any(TypeCastExpr n).getType() + or + expr = any(TypeTestExpr n).getType() + or + expr = any(VariableDeclaration n).getType() + or + expr = any(FunctionDeclaration n).getReturnType() + or + expr = any(AccessorDeclaration n).getType() + or + expr = any(Parameter n).getType() + or + expr = any(TypeAliasDeclaration n).getType() + or + expr = any(BaseType n).getType() + or + expr = any(TypeParameter n).getBound() + or + expr.getParent() instanceof TypeConstraint + or + isInTypeContext(expr.getEnclosingExpr()) +} + +/** Holds if `e` appears in a name-binding position inside `declaration` */ +predicate isInBindingContext(Expr e, AstNode declaration) { + NameBinding::bindingContext(e, _, declaration) +} + +/** Holds if `e` is part of the target of `assignment`. */ +predicate isInAssignmentContext(Expr e, AstNode assignment) { + e = assignment.(AssignExpr).getTarget() + or + e = assignment.(CompoundAssignExpr).getTarget() + or + exists(TupleExpr tuple | + isInAssignmentContext(tuple, assignment) and + e = tuple.getAnElement().getValue() + ) +} + +/** + * Holds if `e` receives an incoming value because it is part of a binding pattern + * or assignment target. + */ +predicate hasIncomingValue(Expr e, AstNode declarationOrAssignment) { + isInBindingContext(e, declarationOrAssignment) + or + isInAssignmentContext(e, declarationOrAssignment) +} + +/** + * Holds if `e` evaluates to a result. + */ +predicate hasResultValue(Expr e) { + not isInTypeContext(e) and + not isInBindingContext(e, _) and + not isInAssignmentContext(e, any(AssignExpr n)) and // non-compound assignment target + not e instanceof IdentifierLabel +} From ed871524a626d418e457fde0f8bcd64caf8e94ce Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 8 Sep 2026 10:48:50 +0200 Subject: [PATCH 02/30] unified: Scaffold empty data flow graph with test The data flow graph has no actual edges yet --- .../unified/internal/dataflow/AllDataFlow.qll | 8 + .../unified/internal/dataflow/Content.qll | 30 +++ .../internal/dataflow/DataFlowGraph.qll | 6 + .../dataflow/DataFlowInstantiation.qll | 175 ++++++++++++++++++ .../internal/dataflow/DataFlowNode.qll | 29 +++ .../internal/dataflow/DataFlowPublic.qll | 13 ++ .../codeql/unified/internal/dataflow/Step.qll | 51 +++++ .../dataflow/TaintTrackingInstantiation.qll | 16 ++ unified/ql/lib/qlpack.yml | 1 + unified/ql/lib/unified.qll | 1 + unified/ql/lib/utils/test/InlineFlowTest.qll | 38 ++++ .../test/library-tests/dataflow/test.expected | 0 .../ql/test/library-tests/dataflow/test.ql | 3 + .../ql/test/library-tests/dataflow/test.swift | 8 + 14 files changed, 379 insertions(+) create mode 100644 unified/ql/lib/codeql/unified/internal/dataflow/AllDataFlow.qll create mode 100644 unified/ql/lib/codeql/unified/internal/dataflow/Content.qll create mode 100644 unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll create mode 100644 unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll create mode 100644 unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll create mode 100644 unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPublic.qll create mode 100644 unified/ql/lib/codeql/unified/internal/dataflow/Step.qll create mode 100644 unified/ql/lib/codeql/unified/internal/dataflow/TaintTrackingInstantiation.qll create mode 100644 unified/ql/lib/utils/test/InlineFlowTest.qll create mode 100644 unified/ql/test/library-tests/dataflow/test.expected create mode 100644 unified/ql/test/library-tests/dataflow/test.ql create mode 100644 unified/ql/test/library-tests/dataflow/test.swift diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/AllDataFlow.qll b/unified/ql/lib/codeql/unified/internal/dataflow/AllDataFlow.qll new file mode 100644 index 000000000000..dda7b2e472e0 --- /dev/null +++ b/unified/ql/lib/codeql/unified/internal/dataflow/AllDataFlow.qll @@ -0,0 +1,8 @@ +/** Re-exports all the files in the internal dataflow folder (except DataFlowPublic). */ + +import Content +import DataFlowGraph +import DataFlowInstantiation +import DataFlowNode +import Step +import TaintTrackingInstantiation diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/Content.qll b/unified/ql/lib/codeql/unified/internal/dataflow/Content.qll new file mode 100644 index 000000000000..ccaf1100c36d --- /dev/null +++ b/unified/ql/lib/codeql/unified/internal/dataflow/Content.qll @@ -0,0 +1,30 @@ +private import unified +private import AllDataFlow + +private newtype TContent = TNamedMember(string name) { name = any(Identifier id).getValue() } + +class Content extends TContent { + string asNamedMember() { this = TNamedMember(result) } + + string toString() { result = this.asNamedMember() } + + Location getLocation() { none() } +} + +private newtype TContentSet = TSingleton(Content content) + +class ContentSet extends TContentSet { + Content asSingleton() { this = TSingleton(result) } + + string toString() { result = this.asSingleton().toString() } + + Location getLocation() { result = this.asSingleton().getLocation() } + + Content getAStoreContent() { result = this.asSingleton() } + + Content getAReadContent() { result = this.asSingleton() } +} + +module ContentSet { + ContentSet namedMember(string name) { result.asSingleton().asNamedMember() = name } +} diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll new file mode 100644 index 000000000000..4d5c15a4169e --- /dev/null +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll @@ -0,0 +1,6 @@ +private import unified +private import AllDataFlow + +predicate step(Node node1, Step step, Node node2) { + none() // TODO +} diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll new file mode 100644 index 000000000000..452ee18fed0b --- /dev/null +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll @@ -0,0 +1,175 @@ +private import unified +private import AllDataFlow +private import AllDataFlow as D +private import codeql.dataflow.DataFlow +private import codeql.util.Void +private import codeql.util.Unit + +module DataFlowInput implements InputSig { + class Node = D::Node; + + // + // Contents + // + class Content = D::Content; + + class ContentSet = D::ContentSet; + + class ContentApprox = Content; // TODO + + ContentApprox getContentApprox(Content c) { result = c } // TODO + + // + // Parameter, argument, return, and out nodes and their positions/kinds + // + class ParameterNode extends Node { + ParameterNode() { none() } // TODO + } + + class ArgumentNode extends Node { + ArgumentNode() { none() } // TODO + } + + class ReturnNode extends Node { + ReturnNode() { none() } // TODO + + ReturnKind getKind() { none() } // TODO + } + + class OutNode extends Node { + OutNode() { none() } // TODO + } + + class ReturnKind = Unit; + + class ParameterPosition extends Void { + ParameterPosition() { none() } // TODO + + bindingset[this] + string toString() { none() } // TODO + } + + class ArgumentPosition extends Void { + ArgumentPosition() { none() } // TODO + + bindingset[this] + string toString() { none() } // TODO + } + + predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { none() } // TODO + + // + // Calls and callables + // + class DataFlowCall extends Void { + Location getLocation() { none() } // TODO + + DataFlowCallable getEnclosingCallable() { none() } // TODO + } + + class DataFlowCallable = Callable; // TODO: Use newtype + + DataFlowCallable viableCallable(DataFlowCall c) { none() } // TODO + + DataFlowCallable nodeGetEnclosingCallable(Node node) { result = node.getEnclosingCallable() } + + predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition pos) { + none() // TODO + } + + predicate isArgumentNode(ArgumentNode n, DataFlowCall call, ArgumentPosition pos) { + none() // TODO + } + + OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { none() } // TODO + + // + // Post-update nodes + // + class PostUpdateNode extends Node { + PostUpdateNode() { none() } // TODO + + Node getPreUpdateNode() { none() } // TODO + } + + // + // Types + // + class DataFlowType = Unit; // TODO: track types + + class CastNode extends Node { + CastNode() { none() } // TODO + } + + DataFlowType getNodeType(Node node) { any() } // TODO + + predicate compatibleTypes(DataFlowType t1, DataFlowType t2) { any() } // TODO + + predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) { any() } // TODO + + // + // Steps + // + predicate simpleLocalFlowStep(Node node1, Node node2, string model) { none() } // TODO + + predicate jumpStep(Node node1, Node node2) { none() } // TODO + + predicate readStep(Node node1, ContentSet c, Node node2) { none() } // TODO + + predicate storeStep(Node node1, ContentSet c, Node node2) { none() } // TODO + + predicate clearsContent(Node n, ContentSet c) { none() } // TODO + + predicate expectsContent(Node n, ContentSet c) { none() } // TODO + + predicate localMustFlowStep(Node node1, Node node2) { none() } // TODO + + // + // Misc + // + predicate nodeIsHidden(Node node) { none() } // TODO + + class DataFlowExpr = Expr; + + Node exprNode(DataFlowExpr e) { none() } // TODO + + predicate forceHighPrecision(Content c) { none() } // TODO + + class NodeRegion extends Void { + NodeRegion() { none() } // TODO + + predicate contains(Node n) { none() } // TODO + + string toString() { none() } // TODO + } + + predicate isUnreachableInCall(NodeRegion nr, DataFlowCall call) { none() } // TODO + + predicate allowParameterReturnInSelf(ParameterNode p) { none() } // TODO + + class LambdaCallKind extends Void { + LambdaCallKind() { none() } // TODO + + string toString() { none() } // TODO + } + + predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) { none() } // TODO + + predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { none() } // TODO + + predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { + none() // TODO + } + + predicate knownSourceModel(Node source, string model) { none() } // TODO + + predicate knownSinkModel(Node sink, string model) { none() } // TODO + + class DataFlowSecondLevelScope extends Void { + DataFlowSecondLevelScope() { none() } // TODO + + string toString() { none() } // TODO + } +} + +module DataFlowOutput = DataFlowMake; diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll new file mode 100644 index 000000000000..294c628aed15 --- /dev/null +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll @@ -0,0 +1,29 @@ +private import unified +private import AllDataFlow + +private newtype TDataFlowNode = TValueNode(Expr expr) + +/** + * A node representing something that can have a value. + */ +class Node extends TDataFlowNode { + /** Holds if this is the result of evaluating `expr`. */ + predicate isResultValue(Expr expr) { this = TValueNode(expr) } + + /** Gets the expression represented by this node. */ + Expr asExpr() { this = TValueNode(result) } + + /** + * Gets the AST node wrapped by this data flow, if any. + */ + AstNode getWrappedAstNode() { result = this.asExpr() } + + /** Get a string representation of this element. */ + string toString() { result = this.asExpr().toString() } + + /** Gets the location of this data flow node. */ + Location getLocation() { result = this.getWrappedAstNode().getLocation() } + + /** Gets the callable containing this data flow node. */ + Callable getEnclosingCallable() { result = this.getWrappedAstNode().getEnclosingCallable() } +} diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPublic.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPublic.qll new file mode 100644 index 000000000000..b25ca7202505 --- /dev/null +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPublic.qll @@ -0,0 +1,13 @@ +private import unified +private import AllDataFlow +private import AllDataFlow as D + +module DataFlow { + class Node = D::Node; + + import DataFlowOutput +} + +module TaintTracking { + import TaintTrackingOutput +} diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/Step.qll b/unified/ql/lib/codeql/unified/internal/dataflow/Step.qll new file mode 100644 index 000000000000..65d2667a464e --- /dev/null +++ b/unified/ql/lib/codeql/unified/internal/dataflow/Step.qll @@ -0,0 +1,51 @@ +private import unified +private import AllDataFlow + +private newtype TStep = + TValueStep() or + TJumpStep() or + TTaintStep() or + TReadStep(ContentSet contents) or + TStoreStep(ContentSet contents) + +/** + * A type of data flow step, used during construction of the data flow graph. + */ +class Step extends TStep { + /** Holds if this represents a value-preserving step. */ + predicate value() { this = TValueStep() } + + /** Holds if this represents a value-preserving jump step (propagating across unrelated call stacks). */ + predicate jump() { this = TJumpStep() } + + /** Holds if this represents a taint-preserving step. */ + predicate taint() { this = TTaintStep() } + + /** Holds if this represents a step reading `contents`. */ + predicate read(ContentSet contents) { this = TReadStep(contents) } + + /** Holds if this represents a step reading the named member `name`. */ + pragma[nomagic] + predicate readName(string name) { this.read(ContentSet::namedMember(name)) } + + /** Holds if this represents a step storing into `contents`. */ + predicate store(ContentSet contents) { this = TStoreStep(contents) } + + /** Holds if this represents a step storing into the named member `name`. */ + pragma[nomagic] + predicate storeName(string name) { this.store(ContentSet::namedMember(name)) } + + string toString() { + this.value() and result = "value" + or + this.jump() and result = "jump" + or + this.taint() and result = "taint" + or + exists(ContentSet contents | + this.read(contents) and result = "read[" + contents + "]" + or + this.store(contents) and result = "store[" + contents + "]" + ) + } +} diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/TaintTrackingInstantiation.qll b/unified/ql/lib/codeql/unified/internal/dataflow/TaintTrackingInstantiation.qll new file mode 100644 index 000000000000..c010e42090fe --- /dev/null +++ b/unified/ql/lib/codeql/unified/internal/dataflow/TaintTrackingInstantiation.qll @@ -0,0 +1,16 @@ +private import unified +private import AllDataFlow +private import codeql.dataflow.TaintTracking + +module TaintTrackingInput implements InputSig { + predicate defaultTaintSanitizer(Node node) { none() } // TODO + + predicate defaultAdditionalTaintStep(Node src, Node sink, string model) { none() } // TODO + + bindingset[node] + predicate defaultImplicitTaintRead(Node node, ContentSet c) { none() } // TODO + + predicate speculativeTaintStep(Node src, Node sink) { none() } // TODO +} + +module TaintTrackingOutput = TaintFlowMake; diff --git a/unified/ql/lib/qlpack.yml b/unified/ql/lib/qlpack.yml index 0167b114ba96..6761ec049d71 100644 --- a/unified/ql/lib/qlpack.yml +++ b/unified/ql/lib/qlpack.yml @@ -7,6 +7,7 @@ library: true upgrades: upgrades dependencies: codeql/controlflow: ${workspace} + codeql/dataflow: ${workspace} codeql/namebinding: ${workspace} codeql/util: ${workspace} warnOnImplicitThis: true diff --git a/unified/ql/lib/unified.qll b/unified/ql/lib/unified.qll index 500de80b64f3..9729b6445812 100644 --- a/unified/ql/lib/unified.qll +++ b/unified/ql/lib/unified.qll @@ -8,3 +8,4 @@ import codeql.unified.internal.Ast::UnifiedFinal import codeql.unified.internal.AstExtra::Public import codeql.unified.internal.ControlFlowGraph import codeql.unified.internal.NameBinding::Public +import codeql.unified.internal.dataflow.DataFlowPublic diff --git a/unified/ql/lib/utils/test/InlineFlowTest.qll b/unified/ql/lib/utils/test/InlineFlowTest.qll new file mode 100644 index 000000000000..967a2446c32c --- /dev/null +++ b/unified/ql/lib/utils/test/InlineFlowTest.qll @@ -0,0 +1,38 @@ +/** + * Inline flow tests for the unified language. + * See `shared/util/codeql/dataflow/test/InlineFlowTest.qll` + */ + +private import unified +private import codeql.dataflow.test.InlineFlowTest +private import codeql.unified.internal.dataflow.AllDataFlow +private import internal.InlineExpectationsTestImpl as InlineExpectationsTestImpl + +private string getCalleeName(CallExpr call) { result = call.getCallee().(Identifier).getValue() } + +private module FlowTestImpl implements InputSig { + predicate defaultSource(DataFlow::Node source) { getCalleeName(source.asExpr()) = "source" } + + predicate defaultSink(DataFlow::Node sink) { + any(CallExpr call | getCalleeName(call) = "sink").getAnArgument().getValue() = sink.asExpr() + } + + private string getSourceArgString(DataFlow::Node src) { + defaultSource(src) and + result = src.asExpr().(CallExpr).getArgument(0).getValue().getStringValue() + } + + bindingset[src, sink] + string getArgString(DataFlow::Node src, DataFlow::Node sink) { + ( + result = getSourceArgString(src) + or + not exists(getSourceArgString(src)) and result = "" + ) and + exists(sink) + } + + predicate interpretModelForTest(QlBuiltins::ExtensionId madId, string model) { none() } +} + +import InlineFlowTestMake diff --git a/unified/ql/test/library-tests/dataflow/test.expected b/unified/ql/test/library-tests/dataflow/test.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/unified/ql/test/library-tests/dataflow/test.ql b/unified/ql/test/library-tests/dataflow/test.ql new file mode 100644 index 000000000000..ee97b8e524e9 --- /dev/null +++ b/unified/ql/test/library-tests/dataflow/test.ql @@ -0,0 +1,3 @@ +private import unified +private import utils.test.InlineFlowTest +import DefaultFlowTest diff --git a/unified/ql/test/library-tests/dataflow/test.swift b/unified/ql/test/library-tests/dataflow/test.swift new file mode 100644 index 000000000000..9212b410b098 --- /dev/null +++ b/unified/ql/test/library-tests/dataflow/test.swift @@ -0,0 +1,8 @@ +func t1() { + sink(source("t1")); // $ hasValueFlow=t1 +} + +func t2() { + sink(source("t2.1") + "blah"); // $ MISSING: hasTaintFlow=t2.1 + sink("blah" + source("t2.2")); // $ MISSING: hasTaintFlow=t2.2 +} From a35466d82808277001de99a596f81e1358789814 Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 8 Sep 2026 10:54:24 +0200 Subject: [PATCH 03/30] unified: Add taint through + --- .../unified/internal/dataflow/DataFlowGraph.qll | 9 ++++++++- .../internal/dataflow/DataFlowInstantiation.qll | 14 ++++++++++---- .../dataflow/TaintTrackingInstantiation.qll | 4 +++- unified/ql/test/library-tests/dataflow/test.swift | 4 ++-- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll index 4d5c15a4169e..6b6ada08487d 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll @@ -2,5 +2,12 @@ private import unified private import AllDataFlow predicate step(Node node1, Step step, Node node2) { - none() // TODO + exists(BinaryExpr expr | + expr.getOperator().getValue() = "+" and + node1.isResultValue([expr.getLeft(), expr.getRight()]) and + step.taint() and + node2.isResultValue(expr) + ) + or + none() // Temporarily disable compilation errors from unsatisfiable types } diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll index 452ee18fed0b..91aedb4290cb 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll @@ -110,13 +110,19 @@ module DataFlowInput implements InputSig { // // Steps // - predicate simpleLocalFlowStep(Node node1, Node node2, string model) { none() } // TODO + predicate simpleLocalFlowStep(Node node1, Node node2, string model) { + step(node1, any(Step s | s.value()), node2) and model = "" + } - predicate jumpStep(Node node1, Node node2) { none() } // TODO + predicate jumpStep(Node node1, Node node2) { step(node1, any(Step s | s.jump()), node2) } - predicate readStep(Node node1, ContentSet c, Node node2) { none() } // TODO + predicate readStep(Node node1, ContentSet c, Node node2) { + step(node1, any(Step s | s.read(c)), node2) + } - predicate storeStep(Node node1, ContentSet c, Node node2) { none() } // TODO + predicate storeStep(Node node1, ContentSet c, Node node2) { + step(node1, any(Step s | s.store(c)), node2) + } predicate clearsContent(Node n, ContentSet c) { none() } // TODO diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/TaintTrackingInstantiation.qll b/unified/ql/lib/codeql/unified/internal/dataflow/TaintTrackingInstantiation.qll index c010e42090fe..ad911bc72224 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/TaintTrackingInstantiation.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/TaintTrackingInstantiation.qll @@ -5,7 +5,9 @@ private import codeql.dataflow.TaintTracking module TaintTrackingInput implements InputSig { predicate defaultTaintSanitizer(Node node) { none() } // TODO - predicate defaultAdditionalTaintStep(Node src, Node sink, string model) { none() } // TODO + predicate defaultAdditionalTaintStep(Node src, Node sink, string model) { + step(src, any(Step s | s.taint()), sink) and model = "" + } bindingset[node] predicate defaultImplicitTaintRead(Node node, ContentSet c) { none() } // TODO diff --git a/unified/ql/test/library-tests/dataflow/test.swift b/unified/ql/test/library-tests/dataflow/test.swift index 9212b410b098..b807a03dcdf2 100644 --- a/unified/ql/test/library-tests/dataflow/test.swift +++ b/unified/ql/test/library-tests/dataflow/test.swift @@ -3,6 +3,6 @@ func t1() { } func t2() { - sink(source("t2.1") + "blah"); // $ MISSING: hasTaintFlow=t2.1 - sink("blah" + source("t2.2")); // $ MISSING: hasTaintFlow=t2.2 + sink(source("t2.1") + "blah"); // $ hasTaintFlow=t2.1 + sink("blah" + source("t2.2")); // $ hasTaintFlow=t2.2 } From 75d215d6adf5af3eff3aa8287d30dbd2095fe6eb Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 8 Sep 2026 11:34:56 +0200 Subject: [PATCH 04/30] unified: Add basic test with tuples --- unified/ql/test/library-tests/dataflow/test.swift | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/unified/ql/test/library-tests/dataflow/test.swift b/unified/ql/test/library-tests/dataflow/test.swift index b807a03dcdf2..f59def9bfe10 100644 --- a/unified/ql/test/library-tests/dataflow/test.swift +++ b/unified/ql/test/library-tests/dataflow/test.swift @@ -6,3 +6,10 @@ func t2() { sink(source("t2.1") + "blah"); // $ hasTaintFlow=t2.1 sink("blah" + source("t2.2")); // $ hasTaintFlow=t2.2 } + +func t3() { + sink((source("t3.1"), "safe").0); // $ MISSING: hasValueFlow=t3.1 + sink((source("t3.2"), "safe").1); // no flow + sink(("safe", source("t3.3")).0); // no flow + sink(("safe", source("t3.4")).1); // $ MISSING: hasValueFlow=t3.4 +} From a1bca719206748b0d34c4946c907cc43a69eaa02 Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 8 Sep 2026 11:36:09 +0200 Subject: [PATCH 05/30] unified: Add steps for member access and tuples --- .../lib/codeql/unified/internal/dataflow/Content.qll | 9 ++++++++- .../unified/internal/dataflow/DataFlowGraph.qll | 12 ++++++++++++ unified/ql/test/library-tests/dataflow/test.swift | 4 ++-- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/Content.qll b/unified/ql/lib/codeql/unified/internal/dataflow/Content.qll index ccaf1100c36d..73a3d7da2c9d 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/Content.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/Content.qll @@ -1,7 +1,14 @@ private import unified private import AllDataFlow -private newtype TContent = TNamedMember(string name) { name = any(Identifier id).getValue() } +private newtype TContent = + TNamedMember(string name) { + name = any(Identifier id).getValue() + or + // 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() + } class Content extends TContent { string asNamedMember() { this = TNamedMember(result) } diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll index 6b6ada08487d..1f55b2c5c92a 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll @@ -9,5 +9,17 @@ predicate step(Node node1, Step step, Node node2) { node2.isResultValue(expr) ) or + exists(TupleExpr expr, int i | + node1.isResultValue(expr.getElement(i).getValue()) and + step.storeName(i.toString()) and + node2.isResultValue(expr) + ) + or + exists(MemberAccessExpr expr | + node1.isResultValue(expr.getBase()) and + step.readName(expr.getMemberName()) and + node2.isResultValue(expr) + ) + or none() // Temporarily disable compilation errors from unsatisfiable types } diff --git a/unified/ql/test/library-tests/dataflow/test.swift b/unified/ql/test/library-tests/dataflow/test.swift index f59def9bfe10..bfa4df953df1 100644 --- a/unified/ql/test/library-tests/dataflow/test.swift +++ b/unified/ql/test/library-tests/dataflow/test.swift @@ -8,8 +8,8 @@ func t2() { } func t3() { - sink((source("t3.1"), "safe").0); // $ MISSING: hasValueFlow=t3.1 + sink((source("t3.1"), "safe").0); // $ hasValueFlow=t3.1 sink((source("t3.2"), "safe").1); // no flow sink(("safe", source("t3.3")).0); // no flow - sink(("safe", source("t3.4")).1); // $ MISSING: hasValueFlow=t3.4 + sink(("safe", source("t3.4")).1); // $ hasValueFlow=t3.4 } From eadbcd10a76cf599e90adc34d874d8d14f660231 Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 8 Sep 2026 11:51:12 +0200 Subject: [PATCH 06/30] unified: Add query for viewing data flow graph --- .../internal/dataflow/DataFlowGraph.qll | 23 +++++++++++++++++++ .../internal/dev/debugDataFlowGraph.ql | 19 +++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 unified/ql/lib/codeql/unified/internal/dev/debugDataFlowGraph.ql diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll index 1f55b2c5c92a..0151daaffcf4 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll @@ -23,3 +23,26 @@ predicate step(Node node1, Step step, Node node2) { or none() // Temporarily disable compilation errors from unsatisfiable types } + +/** Holds if `node` should be included in the debug view. */ +private signature predicate relevantNodeSig(AstNode node); + +module DebugGraph { + private predicate relevantNameBindingNode(Node node) { relevantNode(node.getWrappedAstNode()) } + + query predicate nodes(Node node, string key, string value) { + relevantNameBindingNode(node) and + key = "semmle.label" and + value = node.toString() + } + + query predicate edges(Node node1, Node node2, string key, string value) { + key = "semmle.label" and + relevantNameBindingNode(node1) and + relevantNameBindingNode(node2) and + exists(Step step | + step(node1, step, node2) and + value = step.toString() + ) + } +} diff --git a/unified/ql/lib/codeql/unified/internal/dev/debugDataFlowGraph.ql b/unified/ql/lib/codeql/unified/internal/dev/debugDataFlowGraph.ql new file mode 100644 index 000000000000..7f79b0d151e7 --- /dev/null +++ b/unified/ql/lib/codeql/unified/internal/dev/debugDataFlowGraph.ql @@ -0,0 +1,19 @@ +/** + * @name Debug data flow graph + * @description Renders the data flow graph + * @kind graph + * @id unified/debug-data-flow-graph + */ + +private import unified +private import codeql.unified.internal.dataflow.DataFlowGraph + +/** + * Holds if `node` should be shown in the graph. + */ +predicate relevantNode(AstNode node) { + // Match an ancestor node by location so its whole subtree is shown. + node.getParent*().getLocation().toString().matches("%test.swift@13:%") +} + +import DebugGraph From e8ee1318c7b5711eac3c8f786d54e30573c0ec39 Mon Sep 17 00:00:00 2001 From: Asger F Date: Sat, 12 Sep 2026 07:25:12 +0200 Subject: [PATCH 07/30] unified: Add incoming-value nodes --- .../internal/dataflow/DataFlowGraph.qll | 16 ++++++++++ .../internal/dataflow/DataFlowNode.qll | 30 ++++++++++++++++--- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll index 0151daaffcf4..2896dfcdb805 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll @@ -2,6 +2,18 @@ private import unified private import AllDataFlow predicate step(Node node1, Step step, Node node2) { + exists(VariableDeclaration decl | + node1.isResultValue(decl.getValue()) and + step.value() and + node2.isIncomingValue(decl.getPattern()) + ) + or + exists(AssignExpr assign | + node1.isResultValue(assign.getValue()) and + step.value() and + node2.isIncomingValue(assign.getTarget()) + ) + or exists(BinaryExpr expr | expr.getOperator().getValue() = "+" and node1.isResultValue([expr.getLeft(), expr.getRight()]) and @@ -13,6 +25,10 @@ predicate step(Node node1, Step step, Node node2) { node1.isResultValue(expr.getElement(i).getValue()) and step.storeName(i.toString()) and node2.isResultValue(expr) + or + node1.isIncomingValue(expr) and + step.readName(i.toString()) and + node2.isIncomingValue(expr.getElement(i).getValue()) ) or exists(MemberAccessExpr expr | diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll index 294c628aed15..62debf2b1f35 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll @@ -1,14 +1,29 @@ private import unified private import AllDataFlow +private import codeql.unified.internal.ExprPositions -private newtype TDataFlowNode = TValueNode(Expr expr) +private newtype TDataFlowNode = + TValueNode(Expr expr) { hasResultValue(expr) or hasIncomingValue(expr, _) } or + TStrictlyIncomingValue(Expr expr) { hasResultValue(expr) and hasIncomingValue(expr, _) } /** * A node representing something that can have a value. */ class Node extends TDataFlowNode { /** Holds if this is the result of evaluating `expr`. */ - predicate isResultValue(Expr expr) { this = TValueNode(expr) } + pragma[nomagic] + predicate isResultValue(Expr expr) { hasResultValue(expr) and this = TValueNode(expr) } + + /** Holds if this represents the value about to be assigned to `expr` or pattern-matched against `expr`. */ + pragma[nomagic] + predicate isIncomingValue(Expr expr) { + // Use the TValueNode when it is not needed for representing the result value + hasIncomingValue(expr, _) and + not hasResultValue(expr) and + this = TValueNode(expr) + or + this = TStrictlyIncomingValue(expr) + } /** Gets the expression represented by this node. */ Expr asExpr() { this = TValueNode(result) } @@ -16,10 +31,17 @@ class Node extends TDataFlowNode { /** * Gets the AST node wrapped by this data flow, if any. */ - AstNode getWrappedAstNode() { result = this.asExpr() } + AstNode getWrappedAstNode() { result = this.asExpr() or this = TStrictlyIncomingValue(result) } /** Get a string representation of this element. */ - string toString() { result = this.asExpr().toString() } + string toString() { + result = this.asExpr().toString() + or + exists(Expr expr | + this = TStrictlyIncomingValue(expr) and + result = "[incoming] " + expr.toString() + ) + } /** Gets the location of this data flow node. */ Location getLocation() { result = this.getWrappedAstNode().getLocation() } From a87414cf5a59e74ce46b1d597bd81f813415706d Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 9 Sep 2026 22:06:04 +0200 Subject: [PATCH 08/30] unified: CFG-insensitive variable flow --- .../internal/dataflow/DataFlowGraph.qll | 10 +++++++ .../internal/dataflow/DataFlowNode.qll | 26 ++++++++++++++++--- .../ql/test/library-tests/dataflow/test.swift | 22 ++++++++++++++++ 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll index 2896dfcdb805..d8a4a8bdb5ba 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll @@ -14,6 +14,16 @@ predicate step(Node node1, Step step, Node node2) { node2.isIncomingValue(assign.getTarget()) ) or + exists(LocalVariableAccess access | + node1.isLocalVariable(access.getLocalVariable()) and + step.value() and + node2.isResultValue(access) + or + node1.isIncomingValue(access) and + step.value() and + node2.isLocalVariable(access.getLocalVariable()) + ) + or exists(BinaryExpr expr | expr.getOperator().getValue() = "+" and node1.isResultValue([expr.getLeft(), expr.getRight()]) and diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll index 62debf2b1f35..9b746152461f 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll @@ -4,7 +4,8 @@ private import codeql.unified.internal.ExprPositions private newtype TDataFlowNode = TValueNode(Expr expr) { hasResultValue(expr) or hasIncomingValue(expr, _) } or - TStrictlyIncomingValue(Expr expr) { hasResultValue(expr) and hasIncomingValue(expr, _) } + TStrictlyIncomingValue(Expr expr) { hasResultValue(expr) and hasIncomingValue(expr, _) } or + TLocalVariableNode(LocalVariable v) /** * A node representing something that can have a value. @@ -25,6 +26,9 @@ class Node extends TDataFlowNode { this = TStrictlyIncomingValue(expr) } + /** Holds if this represents the value stored in the given local variable. */ + predicate isLocalVariable(LocalVariable v) { this = TLocalVariableNode(v) } + /** Gets the expression represented by this node. */ Expr asExpr() { this = TValueNode(result) } @@ -41,11 +45,27 @@ class Node extends TDataFlowNode { this = TStrictlyIncomingValue(expr) and result = "[incoming] " + expr.toString() ) + or + exists(LocalVariable v | + this.isLocalVariable(v) and + result = "[variable] " + v.toString() + ) } /** Gets the location of this data flow node. */ - Location getLocation() { result = this.getWrappedAstNode().getLocation() } + Location getLocation() { + result = this.getWrappedAstNode().getLocation() + or + exists(LocalVariable v | this.isLocalVariable(v) and result = v.getLocation()) + } /** Gets the callable containing this data flow node. */ - Callable getEnclosingCallable() { result = this.getWrappedAstNode().getEnclosingCallable() } + Callable getEnclosingCallable() { + result = this.getWrappedAstNode().getEnclosingCallable() + or + exists(LocalVariable v | + this.isLocalVariable(v) and + result = v.getABinding().getEnclosingCallable() + ) + } } diff --git a/unified/ql/test/library-tests/dataflow/test.swift b/unified/ql/test/library-tests/dataflow/test.swift index bfa4df953df1..d2b5ea1fd742 100644 --- a/unified/ql/test/library-tests/dataflow/test.swift +++ b/unified/ql/test/library-tests/dataflow/test.swift @@ -13,3 +13,25 @@ func t3() { sink(("safe", source("t3.3")).0); // no flow sink(("safe", source("t3.4")).1); // $ hasValueFlow=t3.4 } + +func t4() { + let a = source("t4.1"); + sink(a); // $ hasValueFlow=t4.1 +} + +func t5() { + let (a, b) = (source("t5.1"), "safe"); + sink(a); // $ hasValueFlow=t5.1 + sink(b); // no flow + + let (c, d) = ("safe", source("t5.2")); + sink(c); // no flow + sink(d); // $ hasValueFlow=t5.2 +} + +func t6() { + var a = source("t6.1"); + sink(a); // $ hasValueFlow=t6.1 + a = "safe"; + sink(a); // $ SPURIOUS: hasValueFlow=t6.1 +} From dfd1a508ed1785d099338e0311061e0a0aba4e73 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 9 Sep 2026 22:21:30 +0200 Subject: [PATCH 09/30] unified: Add post-update nodes --- .../internal/dataflow/DataFlowGraph.qll | 8 ++++++ .../dataflow/DataFlowInstantiation.qll | 4 +-- .../internal/dataflow/DataFlowNode.qll | 27 ++++++++++++++++++- .../ql/test/library-tests/dataflow/test.swift | 25 +++++++++++++++++ 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll index d8a4a8bdb5ba..2463bf6af48b 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll @@ -22,6 +22,10 @@ predicate step(Node node1, Step step, Node node2) { node1.isIncomingValue(access) and step.value() and node2.isLocalVariable(access.getLocalVariable()) + or + node1.isPostUpdate(access) and + step.value() and + node2.isLocalVariable(access.getLocalVariable()) ) or exists(BinaryExpr expr | @@ -45,6 +49,10 @@ predicate step(Node node1, Step step, Node node2) { node1.isResultValue(expr.getBase()) and step.readName(expr.getMemberName()) and node2.isResultValue(expr) + or + (node1.isIncomingValue(expr) or node1.isPostUpdate(expr)) and + step.storeName(expr.getMemberName()) and + node2.isPostUpdate(expr.getBase()) ) or none() // Temporarily disable compilation errors from unsatisfiable types diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll index 91aedb4290cb..18c5424d24b5 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll @@ -87,9 +87,9 @@ module DataFlowInput implements InputSig { // Post-update nodes // class PostUpdateNode extends Node { - PostUpdateNode() { none() } // TODO + PostUpdateNode() { this = getPostUpdateNode(_) } - Node getPreUpdateNode() { none() } // TODO + Node getPreUpdateNode() { this = getPostUpdateNode(result) } } // diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll index 9b746152461f..6b1328b23f85 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll @@ -2,9 +2,17 @@ private import unified private import AllDataFlow private import codeql.unified.internal.ExprPositions +private predicate hasPostUpdate(Expr expr) { + exists(MemberAccessExpr member | + (hasIncomingValue(member, _) or hasPostUpdate(member)) and + expr = member.getBase() + ) +} + private newtype TDataFlowNode = TValueNode(Expr expr) { hasResultValue(expr) or hasIncomingValue(expr, _) } or TStrictlyIncomingValue(Expr expr) { hasResultValue(expr) and hasIncomingValue(expr, _) } or + TPostUpdateNode(Expr expr) { hasPostUpdate(expr) } or TLocalVariableNode(LocalVariable v) /** @@ -29,13 +37,20 @@ class Node extends TDataFlowNode { /** Holds if this represents the value stored in the given local variable. */ predicate isLocalVariable(LocalVariable v) { this = TLocalVariableNode(v) } + /** Holds if this represents the updated state of the value returned by `expr` after it has been mutated by the surrounding assignment or call. */ + predicate isPostUpdate(Expr expr) { this = TPostUpdateNode(expr) } + /** Gets the expression represented by this node. */ Expr asExpr() { this = TValueNode(result) } /** * Gets the AST node wrapped by this data flow, if any. */ - AstNode getWrappedAstNode() { result = this.asExpr() or this = TStrictlyIncomingValue(result) } + AstNode getWrappedAstNode() { + result = this.asExpr() or + this = TStrictlyIncomingValue(result) or + this = TPostUpdateNode(result) + } /** Get a string representation of this element. */ string toString() { @@ -44,6 +59,9 @@ class Node extends TDataFlowNode { exists(Expr expr | this = TStrictlyIncomingValue(expr) and result = "[incoming] " + expr.toString() + or + this = TPostUpdateNode(expr) and + result = "[post] " + expr.toString() ) or exists(LocalVariable v | @@ -69,3 +87,10 @@ class Node extends TDataFlowNode { ) } } + +Node getPostUpdateNode(Node pre) { + exists(Expr expr | + pre.isResultValue(expr) and + result.isPostUpdate(expr) + ) +} diff --git a/unified/ql/test/library-tests/dataflow/test.swift b/unified/ql/test/library-tests/dataflow/test.swift index d2b5ea1fd742..d5f0ac64f8f1 100644 --- a/unified/ql/test/library-tests/dataflow/test.swift +++ b/unified/ql/test/library-tests/dataflow/test.swift @@ -35,3 +35,28 @@ func t6() { a = "safe"; sink(a); // $ SPURIOUS: hasValueFlow=t6.1 } + +func t7() { + var tuple = ("safe", "safe") + tuple.0 = source("t7.1"); + sink(tuple.0); // $ hasValueFlow=t7.1 + sink(tuple.1); // no flow +} + +func t8() { + var deep_tuple = (("safe", "safe"), ("safe", "safe")) + deep_tuple.1.0 = source("t8.1"); + sink(deep_tuple); // no flow + sink(deep_tuple.0); // no flow + sink(deep_tuple.1); // no flow + sink(deep_tuple.0.1); // no flow + sink(deep_tuple.1.0); // $ hasValueFlow=t8.1 + sink(deep_tuple.1.1); // no flow +} + +func t9() { + var tuple = ("safe", "safe") + (tuple.1, _) = (source("t9.1"), source("t9.2")); + sink(tuple.0); // no flow + sink(tuple.1); // $ hasValueFlow=t9.1 +} From 707238e0f860f92e9a5da1d7a00101d9a4d8f62a Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 9 Sep 2026 23:41:20 +0200 Subject: [PATCH 10/30] unified: Add variable-ref nodes --- .../unified/internal/dataflow/AllDataFlow.qll | 1 + .../internal/dataflow/DataFlowGraph.qll | 17 +++++- .../internal/dataflow/DataFlowNode.qll | 60 ++++++++++++------- .../internal/dataflow/VariableRefKind.qll | 22 +++++++ 4 files changed, 77 insertions(+), 23 deletions(-) create mode 100644 unified/ql/lib/codeql/unified/internal/dataflow/VariableRefKind.qll diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/AllDataFlow.qll b/unified/ql/lib/codeql/unified/internal/dataflow/AllDataFlow.qll index dda7b2e472e0..956164fae378 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/AllDataFlow.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/AllDataFlow.qll @@ -6,3 +6,4 @@ import DataFlowInstantiation import DataFlowNode import Step import TaintTrackingInstantiation +import VariableRefKind diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll index 2463bf6af48b..81293243c88d 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll @@ -15,17 +15,28 @@ predicate step(Node node1, Step step, Node node2) { ) or exists(LocalVariableAccess access | - node1.isLocalVariable(access.getLocalVariable()) and + node1.isLocalVariableRead(access, access.getLocalVariable()) and step.value() and node2.isResultValue(access) or node1.isIncomingValue(access) and step.value() and - node2.isLocalVariable(access.getLocalVariable()) + node2.isLocalVariableWrite(access, access.getLocalVariable()) or node1.isPostUpdate(access) and step.value() and - node2.isLocalVariable(access.getLocalVariable()) + node2.isLocalVariablePostUpdate(access, access.getLocalVariable()) + ) + or + // dummy implementation: make all writes and post-updates flow to all reads + step.value() and + exists(LocalVariable v | + ( + node1.isLocalVariableWrite(_, v) + or + node1.isLocalVariablePostUpdate(_, v) + ) and + node2.isLocalVariableRead(_, v) ) or exists(BinaryExpr expr | diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll index 6b1328b23f85..baafd8a8c724 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll @@ -9,11 +9,29 @@ private predicate hasPostUpdate(Expr expr) { ) } +/** + * Holds if `expr` performs an access to `var` of the given `kind` at `cfgNode`. + */ +predicate performsVariableAccess( + Expr expr, LocalVariable var, VariableRefKind kind, ControlFlowNode cfgNode +) { + // TODO: add UnqualifiedMemberAccess here + exists(LocalVariableAccess access | var = access.getLocalVariable() and expr = access | + hasResultValue(access) and kind.isRead() and cfgNode.isAfter(expr) + or + hasIncomingValue(access, _) and kind.isWrite() and cfgNode.asExpr() = expr // TODO: use more precise CFG node + or + hasPostUpdate(access) and kind.isPostUpdate() and cfgNode.asExpr() = expr // TODO: use more precise CFG node + ) +} + private newtype TDataFlowNode = TValueNode(Expr expr) { hasResultValue(expr) or hasIncomingValue(expr, _) } or TStrictlyIncomingValue(Expr expr) { hasResultValue(expr) and hasIncomingValue(expr, _) } or TPostUpdateNode(Expr expr) { hasPostUpdate(expr) } or - TLocalVariableNode(LocalVariable v) + TLocalVariableRefNode(Expr expr, LocalVariable var, VariableRefKind kind) { + performsVariableAccess(expr, var, kind, _) + } /** * A node representing something that can have a value. @@ -34,8 +52,20 @@ class Node extends TDataFlowNode { this = TStrictlyIncomingValue(expr) } - /** Holds if this represents the value stored in the given local variable. */ - predicate isLocalVariable(LocalVariable v) { this = TLocalVariableNode(v) } + /** Holds if this represents the value read from `v` at `access`. */ + predicate isLocalVariableRead(Expr access, LocalVariable v) { + this = TLocalVariableRefNode(access, v, any(VariableRefKind k | k.isRead())) + } + + /** Holds if this represents the value written to `v` at `access`. */ + predicate isLocalVariableWrite(Expr access, LocalVariable v) { + this = TLocalVariableRefNode(access, v, any(VariableRefKind k | k.isWrite())) + } + + /** Holds if this represents the updated state of the value held in `v` after it has been mutated by the surrounding assignment or call. */ + predicate isLocalVariablePostUpdate(Expr access, LocalVariable v) { + this = TLocalVariableRefNode(access, v, any(VariableRefKind k | k.isPostUpdate())) + } /** Holds if this represents the updated state of the value returned by `expr` after it has been mutated by the surrounding assignment or call. */ predicate isPostUpdate(Expr expr) { this = TPostUpdateNode(expr) } @@ -49,7 +79,8 @@ class Node extends TDataFlowNode { AstNode getWrappedAstNode() { result = this.asExpr() or this = TStrictlyIncomingValue(result) or - this = TPostUpdateNode(result) + this = TPostUpdateNode(result) or + this = TLocalVariableRefNode(result, _, _) } /** Get a string representation of this element. */ @@ -64,28 +95,17 @@ class Node extends TDataFlowNode { result = "[post] " + expr.toString() ) or - exists(LocalVariable v | - this.isLocalVariable(v) and - result = "[variable] " + v.toString() + exists(LocalVariable v, VariableRefKind kind | + this = TLocalVariableRefNode(_, v, kind) and + result = "[variable " + kind + "] " + v.toString() ) } /** Gets the location of this data flow node. */ - Location getLocation() { - result = this.getWrappedAstNode().getLocation() - or - exists(LocalVariable v | this.isLocalVariable(v) and result = v.getLocation()) - } + Location getLocation() { result = this.getWrappedAstNode().getLocation() } /** Gets the callable containing this data flow node. */ - Callable getEnclosingCallable() { - result = this.getWrappedAstNode().getEnclosingCallable() - or - exists(LocalVariable v | - this.isLocalVariable(v) and - result = v.getABinding().getEnclosingCallable() - ) - } + Callable getEnclosingCallable() { result = this.getWrappedAstNode().getEnclosingCallable() } } Node getPostUpdateNode(Node pre) { diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/VariableRefKind.qll b/unified/ql/lib/codeql/unified/internal/dataflow/VariableRefKind.qll new file mode 100644 index 000000000000..326b517c5f52 --- /dev/null +++ b/unified/ql/lib/codeql/unified/internal/dataflow/VariableRefKind.qll @@ -0,0 +1,22 @@ +private import unified + +private newtype TVariableRefKind = + TRead() or + TWrite() or + TPostUpdate() + +class VariableRefKind extends TVariableRefKind { + predicate isRead() { this = TRead() } + + predicate isWrite() { this = TWrite() } + + predicate isPostUpdate() { this = TPostUpdate() } + + string toString() { + this.isRead() and result = "read" + or + this.isWrite() and result = "write" + or + this.isPostUpdate() and result = "post-update" + } +} From b5c2ff658edb36b8313cbd3abed44ea50991c126 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 10 Sep 2026 01:01:03 +0200 Subject: [PATCH 11/30] unified: Update debug graph --- .../internal/dataflow/DataFlowGraph.qll | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll index 81293243c88d..e48d724f93d4 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll @@ -73,21 +73,37 @@ predicate step(Node node1, Step step, Node node2) { private signature predicate relevantNodeSig(AstNode node); module DebugGraph { - private predicate relevantNameBindingNode(Node node) { relevantNode(node.getWrappedAstNode()) } + private Node adjacent(Node n) { + step(n, _, result) + or + step(result, _, n) + } + + private predicate relevantDataFlowNode(Node node) { + relevantNode(node.getWrappedAstNode()) + or + not exists(node.getWrappedAstNode()) and + relevantDataFlowNode(adjacent(node)) + } query predicate nodes(Node node, string key, string value) { - relevantNameBindingNode(node) and + relevantDataFlowNode(node) and key = "semmle.label" and value = node.toString() } query predicate edges(Node node1, Node node2, string key, string value) { key = "semmle.label" and - relevantNameBindingNode(node1) and - relevantNameBindingNode(node2) and - exists(Step step | - step(node1, step, node2) and - value = step.toString() + relevantDataFlowNode(node1) and + relevantDataFlowNode(node2) and + ( + exists(Step step | + step(node1, step, node2) and + value = step.toString() + ) + or + node2 = getPostUpdateNode(node1) and + value = "post-update" ) } } From d0e5d1b5b899845ec8e77a036f78494038762446 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 10 Sep 2026 08:47:35 +0200 Subject: [PATCH 12/30] unified: Add LocalSsa --- .../unified/internal/dataflow/AllDataFlow.qll | 1 + .../internal/dataflow/DataFlowGraph.qll | 11 --- .../dataflow/DataFlowInstantiation.qll | 4 +- .../internal/dataflow/DataFlowNode.qll | 44 ++++++++-- .../unified/internal/dataflow/LocalSsa.qll | 85 +++++++++++++++++++ .../internal/dataflow/VariableRefKind.qll | 2 +- unified/ql/lib/qlpack.yml | 1 + .../ql/test/library-tests/dataflow/test.swift | 62 +++++++++++++- 8 files changed, 189 insertions(+), 21 deletions(-) create mode 100644 unified/ql/lib/codeql/unified/internal/dataflow/LocalSsa.qll diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/AllDataFlow.qll b/unified/ql/lib/codeql/unified/internal/dataflow/AllDataFlow.qll index 956164fae378..53b53956cb23 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/AllDataFlow.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/AllDataFlow.qll @@ -5,5 +5,6 @@ import DataFlowGraph import DataFlowInstantiation import DataFlowNode import Step +import LocalSsa import TaintTrackingInstantiation import VariableRefKind diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll index e48d724f93d4..dee49e4ba696 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll @@ -28,17 +28,6 @@ predicate step(Node node1, Step step, Node node2) { node2.isLocalVariablePostUpdate(access, access.getLocalVariable()) ) or - // dummy implementation: make all writes and post-updates flow to all reads - step.value() and - exists(LocalVariable v | - ( - node1.isLocalVariableWrite(_, v) - or - node1.isLocalVariablePostUpdate(_, v) - ) and - node2.isLocalVariableRead(_, v) - ) - or exists(BinaryExpr expr | expr.getOperator().getValue() = "+" and node1.isResultValue([expr.getLeft(), expr.getRight()]) and diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll index 18c5424d24b5..a51ab0cd25f9 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll @@ -112,6 +112,8 @@ module DataFlowInput implements InputSig { // predicate simpleLocalFlowStep(Node node1, Node node2, string model) { step(node1, any(Step s | s.value()), node2) and model = "" + or + localSsaStep(node1, node2, _) and model = "" } predicate jumpStep(Node node1, Node node2) { step(node1, any(Step s | s.jump()), node2) } @@ -128,7 +130,7 @@ module DataFlowInput implements InputSig { predicate expectsContent(Node n, ContentSet c) { none() } // TODO - predicate localMustFlowStep(Node node1, Node node2) { none() } // TODO + predicate localMustFlowStep(Node node1, Node node2) { localSsaMustFlowStep(node1, node2) } // TODO // // Misc diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll index baafd8a8c724..afe603912a25 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll @@ -25,13 +25,14 @@ predicate performsVariableAccess( ) } -private newtype TDataFlowNode = +newtype TDataFlowNode = TValueNode(Expr expr) { hasResultValue(expr) or hasIncomingValue(expr, _) } or TStrictlyIncomingValue(Expr expr) { hasResultValue(expr) and hasIncomingValue(expr, _) } or TPostUpdateNode(Expr expr) { hasPostUpdate(expr) } or TLocalVariableRefNode(Expr expr, LocalVariable var, VariableRefKind kind) { performsVariableAccess(expr, var, kind, _) - } + } or + TLocalSsaNode(LocalSsaDataFlowOutput::SsaNode node) /** * A node representing something that can have a value. @@ -52,19 +53,24 @@ class Node extends TDataFlowNode { this = TStrictlyIncomingValue(expr) } + /** Holds if this represents the reference to `v` at `access`. */ + predicate isLocalVariableRef(Expr access, LocalVariable v, VariableRefKind kind) { + this = TLocalVariableRefNode(access, v, kind) + } + /** Holds if this represents the value read from `v` at `access`. */ predicate isLocalVariableRead(Expr access, LocalVariable v) { - this = TLocalVariableRefNode(access, v, any(VariableRefKind k | k.isRead())) + this.isLocalVariableRef(access, v, TRead()) } /** Holds if this represents the value written to `v` at `access`. */ predicate isLocalVariableWrite(Expr access, LocalVariable v) { - this = TLocalVariableRefNode(access, v, any(VariableRefKind k | k.isWrite())) + this.isLocalVariableRef(access, v, TWrite()) } /** Holds if this represents the updated state of the value held in `v` after it has been mutated by the surrounding assignment or call. */ predicate isLocalVariablePostUpdate(Expr access, LocalVariable v) { - this = TLocalVariableRefNode(access, v, any(VariableRefKind k | k.isPostUpdate())) + this.isLocalVariableRef(access, v, TPostUpdate()) } /** Holds if this represents the updated state of the value returned by `expr` after it has been mutated by the surrounding assignment or call. */ @@ -99,13 +105,32 @@ class Node extends TDataFlowNode { this = TLocalVariableRefNode(_, v, kind) and result = "[variable " + kind + "] " + v.toString() ) + or + exists(LocalSsaDataFlowOutput::SsaNode node | + this = TLocalSsaNode(node) and + result = node.toString() + ) } /** Gets the location of this data flow node. */ - Location getLocation() { result = this.getWrappedAstNode().getLocation() } + Location getLocation() { + result = this.getWrappedAstNode().getLocation() + or + exists(LocalSsaDataFlowOutput::SsaNode node | + this = TLocalSsaNode(node) and + result = node.getLocation() + ) + } /** Gets the callable containing this data flow node. */ - Callable getEnclosingCallable() { result = this.getWrappedAstNode().getEnclosingCallable() } + Callable getEnclosingCallable() { + result = this.getWrappedAstNode().getEnclosingCallable() + or + exists(LocalSsaDataFlowOutput::SsaNode node | + this = TLocalSsaNode(node) and + result = node.getSourceVariable().getDeclaringCallable() + ) + } } Node getPostUpdateNode(Node pre) { @@ -113,4 +138,9 @@ Node getPostUpdateNode(Node pre) { pre.isResultValue(expr) and result.isPostUpdate(expr) ) + or + exists(Expr expr, LocalVariable var | + pre.isLocalVariableRead(expr, var) and + result.isLocalVariablePostUpdate(expr, var) + ) } diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/LocalSsa.qll b/unified/ql/lib/codeql/unified/internal/dataflow/LocalSsa.qll new file mode 100644 index 000000000000..d705be61550d --- /dev/null +++ b/unified/ql/lib/codeql/unified/internal/dataflow/LocalSsa.qll @@ -0,0 +1,85 @@ +private import unified +private import unified as U +private import AllDataFlow +private import codeql.ssa.Ssa +private import codeql.util.Void + +module LocalSsaInput implements InputSig { + class SourceVariable extends LocalVariable { + SourceVariable() { not this.isCaptured() } + } + + predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) { + certain = true and + performsVariableAccess(_, v, TWrite(), bb.getNode(i)) + } + + predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) { + certain = true and + performsVariableAccess(_, v, TRead(), bb.getNode(i)) + } +} + +module LocalSsaOutput = Make; + +private import LocalSsaOutput + +module LocalSsaDataFlowInput implements DataFlowIntegrationInputSig { + class Expr extends TLocalVariableRefNode { + predicate hasCfgNode(BasicBlock bb, int i) { + exists(U::Expr expr, LocalVariable var, VariableRefKind kind | + this = TLocalVariableRefNode(expr, var, kind) and + kind.isRead() and + performsVariableAccess(expr, var, kind, bb.getNode(i)) + ) + } + + string toString() { result = this.(Node).toString() } + } + + class GuardValue = Void; + + class Guard extends Void { + string toString() { none() } + + predicate hasValueBranchEdge(BasicBlock bb1, BasicBlock bb2, GuardValue val) { none() } + + predicate valueControlsBranchEdge(BasicBlock bb1, BasicBlock bb2, GuardValue val) { none() } + } + + predicate guardDirectlyControlsBlock(Guard guard, BasicBlock bb, GuardValue val) { none() } +} + +module LocalSsaDataFlowOutput = DataFlowIntegration; + +private module Ssa = LocalSsaDataFlowOutput; + +Node getNodeFromLocalSsaNode(Ssa::Node n) { + result = TLocalSsaNode(n) + or + result = n.(Ssa::ExprNode).getExpr() + or + result = getPostUpdateNode(n.(Ssa::ExprPostUpdateNode).getExpr()) + or + exists(LocalVariable v, BasicBlock bb, int i, Expr expr | + n.(Ssa::WriteDefSourceNode).getDefinition().definesAt(v, bb, i) and + performsVariableAccess(expr, v, TWrite(), bb.getNode(i)) and + result.isLocalVariableWrite(expr, v) + ) +} + +predicate localSsaStep(Node node1, Node node2, boolean isUseStep) { + exists(Ssa::Node ssa1, Ssa::Node ssa2 | + Ssa::localFlowStep(_, ssa1, ssa2, isUseStep) and + node1 = getNodeFromLocalSsaNode(ssa1) and + node2 = getNodeFromLocalSsaNode(ssa2) + ) +} + +predicate localSsaMustFlowStep(Node node1, Node node2) { + exists(Ssa::Node ssa1, Ssa::Node ssa2 | + Ssa::localMustFlowStep(_, ssa1, ssa2) and + node1 = getNodeFromLocalSsaNode(ssa1) and + node2 = getNodeFromLocalSsaNode(ssa2) + ) +} diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/VariableRefKind.qll b/unified/ql/lib/codeql/unified/internal/dataflow/VariableRefKind.qll index 326b517c5f52..1ae34f665704 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/VariableRefKind.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/VariableRefKind.qll @@ -1,6 +1,6 @@ private import unified -private newtype TVariableRefKind = +newtype TVariableRefKind = TRead() or TWrite() or TPostUpdate() diff --git a/unified/ql/lib/qlpack.yml b/unified/ql/lib/qlpack.yml index 6761ec049d71..2416cff78e94 100644 --- a/unified/ql/lib/qlpack.yml +++ b/unified/ql/lib/qlpack.yml @@ -7,6 +7,7 @@ library: true upgrades: upgrades dependencies: codeql/controlflow: ${workspace} + codeql/ssa: ${workspace} codeql/dataflow: ${workspace} codeql/namebinding: ${workspace} codeql/util: ${workspace} diff --git a/unified/ql/test/library-tests/dataflow/test.swift b/unified/ql/test/library-tests/dataflow/test.swift index d5f0ac64f8f1..c112468c629b 100644 --- a/unified/ql/test/library-tests/dataflow/test.swift +++ b/unified/ql/test/library-tests/dataflow/test.swift @@ -33,7 +33,7 @@ func t6() { var a = source("t6.1"); sink(a); // $ hasValueFlow=t6.1 a = "safe"; - sink(a); // $ SPURIOUS: hasValueFlow=t6.1 + sink(a); } func t7() { @@ -60,3 +60,63 @@ func t9() { sink(tuple.0); // no flow sink(tuple.1); // $ hasValueFlow=t9.1 } + +func t10() { + var tuple = ("safe", "safe") + sink(tuple.0); // no flow + sink(tuple.1); // no flow + + tuple.0 = source("t10.1"); + sink(tuple.0); // $ hasValueFlow=t10.1 + sink(tuple.1); // no flow + + tuple = ("safe", "safe"); + sink(tuple.0); // no flow + sink(tuple.1); // no flow +} + +func t11() { + var x = "safe"; + if (foo()) { + x = source("t11.1"); + } else { + sink(x); // no flow + } + sink(x); // $ hasValueFlow=t11.1 + + var y = "safe"; + if (foo()) { + y = source("t11.2"); + } + sink(y); // $ hasValueFlow=t11.2 + + if (foo()) { + sink(x); // $ hasValueFlow=t11.1 + sink(y); // $ hasValueFlow=t11.2 + } +} + +func t12() { + var tuple = ("safe", "safe"); + if (foo()) { + tuple.0 = source("t12.1"); + } else { + sink(tuple.0); // no flow + sink(tuple.1); // no flow + } + sink(tuple.0); // $ hasValueFlow=t12.1 + sink(tuple.1); // no flow +} + +func t13() { + var tuple = (source("t13.1"), source("t13.2")); + var (a,b) = ("safe", "safe") + if (foo()) { + (a,b) = tuple + } else { + sink(a); // no flow + sink(b); // no flow + } + sink(a); // $ hasValueFlow=t13.1 + sink(b); // $ hasValueFlow=t13.2 +} From e5efdbd74fb4d34e949aa7251dc2231177ecc5b3 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 10 Sep 2026 08:48:14 +0200 Subject: [PATCH 13/30] unified: Include SSA steps in debug view --- .../codeql/unified/internal/dataflow/DataFlowGraph.qll | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll index dee49e4ba696..3a20cd96d02c 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll @@ -66,6 +66,10 @@ module DebugGraph { step(n, _, result) or step(result, _, n) + or + localSsaStep(n, result, _) + or + localSsaStep(result, n, _) } private predicate relevantDataFlowNode(Node node) { @@ -91,6 +95,11 @@ module DebugGraph { value = step.toString() ) or + exists(boolean isUseStep | + localSsaStep(node1, node2, isUseStep) and + if isUseStep = true then value = "use-use" else value = "def-use" + ) + or node2 = getPostUpdateNode(node1) and value = "post-update" ) From bf3aae681f26767fe05179e5e225864c2db109e2 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 10 Sep 2026 10:23:56 +0200 Subject: [PATCH 14/30] unified: Handle implicit 'self' references --- .../unified/internal/StaticNameBinding.qll | 3 ++ .../internal/dataflow/DataFlowGraph.qll | 10 ++++ .../internal/dataflow/DataFlowNode.qll | 11 ++++- .../unified/internal/dataflow/LocalSsa.qll | 8 +++- .../dataflow/implicit-self.swift | 48 +++++++++++++++++++ 5 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 unified/ql/test/library-tests/dataflow/implicit-self.swift diff --git a/unified/ql/lib/codeql/unified/internal/StaticNameBinding.qll b/unified/ql/lib/codeql/unified/internal/StaticNameBinding.qll index 38060266c258..cf8e216bd7e0 100644 --- a/unified/ql/lib/codeql/unified/internal/StaticNameBinding.qll +++ b/unified/ql/lib/codeql/unified/internal/StaticNameBinding.qll @@ -667,6 +667,9 @@ module Public { LocalVariable getImplicitQualifierVariable() { ResolveImplicitReceiverAccess::access(this, result) } + + /** Gets the simple name of this identifier, that is, the name of the member being accessed. */ + string getName() { result = this.getValue() } } } diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll index 3a20cd96d02c..f3cf1f8facf2 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll @@ -28,6 +28,16 @@ predicate step(Node node1, Step step, Node node2) { node2.isLocalVariablePostUpdate(access, access.getLocalVariable()) ) or + exists(UnqualifiedMemberAccess access | access.isInstanceAccess() | + node1.isLocalVariableRead(access, access.getImplicitQualifierVariable()) and + step.readName(access.getName()) and + node2.isResultValue(access) + or + (node1.isIncomingValue(access) or node1.isPostUpdate(access)) and + step.storeName(access.getName()) and + node2.isLocalVariablePostUpdate(access, access.getImplicitQualifierVariable()) + ) + or exists(BinaryExpr expr | expr.getOperator().getValue() = "+" and node1.isResultValue([expr.getLeft(), expr.getRight()]) and diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll index afe603912a25..6872e69973d5 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll @@ -15,7 +15,6 @@ private predicate hasPostUpdate(Expr expr) { predicate performsVariableAccess( Expr expr, LocalVariable var, VariableRefKind kind, ControlFlowNode cfgNode ) { - // TODO: add UnqualifiedMemberAccess here exists(LocalVariableAccess access | var = access.getLocalVariable() and expr = access | hasResultValue(access) and kind.isRead() and cfgNode.isAfter(expr) or @@ -23,6 +22,16 @@ predicate performsVariableAccess( or hasPostUpdate(access) and kind.isPostUpdate() and cfgNode.asExpr() = expr // TODO: use more precise CFG node ) + or + exists(UnqualifiedMemberAccess access | + access.isInstanceAccess() and var = access.getImplicitQualifierVariable() and expr = access + | + kind.isRead() and cfgNode.isBefore(access) + or + (hasIncomingValue(access, _) or hasPostUpdate(access)) and + kind.isPostUpdate() and + cfgNode.asExpr() = access // TODO: use more precise CFG node + ) } newtype TDataFlowNode = diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/LocalSsa.qll b/unified/ql/lib/codeql/unified/internal/dataflow/LocalSsa.qll index d705be61550d..c08eeb29a9b5 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/LocalSsa.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/LocalSsa.qll @@ -11,7 +11,13 @@ module LocalSsaInput implements InputSig { predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) { certain = true and - performsVariableAccess(_, v, TWrite(), bb.getNode(i)) + ( + performsVariableAccess(_, v, TWrite(), bb.getNode(i)) + or + // Add implicit initialization of all variables at index -1 before the entry block + bb.(EntryBasicBlock).getEnclosingCallable() = v.getDeclaringCallable() and + i = -1 + ) } predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) { diff --git a/unified/ql/test/library-tests/dataflow/implicit-self.swift b/unified/ql/test/library-tests/dataflow/implicit-self.swift new file mode 100644 index 000000000000..61b4ab17f292 --- /dev/null +++ b/unified/ql/test/library-tests/dataflow/implicit-self.swift @@ -0,0 +1,48 @@ +class Box { + var x: String = "" +} + +class C { + var x: String = "" + var box = Box() + + func t1() { + self.x = source("t1.1"); + sink(self.x); // $ hasValueFlow=t1.1 + } + + func t2() { + x = source("t2.1"); + sink(x); // $ hasValueFlow=t2.1 + } + + func t3() { + x = source("t3.1"); + sink(self.x); // $ hasValueFlow=t3.1 + } + + func t4() { + self.x = source("t4.1"); + sink(x); // $ hasValueFlow=t4.1 + } + + func t5() { + self.box.x = source("t5.1"); + sink(self.box.x); // $ hasValueFlow=t5.1 + } + + func t6() { + box.x = source("t6.1"); + sink(box.x); // $ hasValueFlow=t6.1 + } + + func t7() { + box.x = source("t7.1"); + sink(self.box.x); // $ hasValueFlow=t7.1 + } + + func t8() { + self.box.x = source("t8.1"); + sink(box.x); // $ hasValueFlow=t8.1 + } +} From ff8e0427078077904032379eef265cab63cd0408 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 10 Sep 2026 15:29:17 +0200 Subject: [PATCH 15/30] unified: Add some tests with assignment-timing --- .../ql/test/library-tests/dataflow/test.swift | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/unified/ql/test/library-tests/dataflow/test.swift b/unified/ql/test/library-tests/dataflow/test.swift index c112468c629b..1fd40237559b 100644 --- a/unified/ql/test/library-tests/dataflow/test.swift +++ b/unified/ql/test/library-tests/dataflow/test.swift @@ -120,3 +120,21 @@ func t13() { sink(a); // $ hasValueFlow=t13.1 sink(b); // $ hasValueFlow=t13.2 } + +func t14() { + var a = "safe"; + a = sink(a) + source("t14.1"); // $ SPURIOUS: hasTaintFlow=t14.1 + sink(a); // $ hasTaintFlow=t14.1 +} + +func t15() { + var a = "safe"; + a += source("t15.1"); + sink(a); // $ MISSING: hasTaintFlow=t15.1 +} + +func t16() { + var a = "safe"; + a += sink(a) + source("t16.1"); + sink(a); // $ MISSING: hasTaintFlow=t16.1 +} From 098e7e074b673b62006a0a78fce6e353f6993cda Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 10 Sep 2026 01:20:37 +0200 Subject: [PATCH 16/30] unified: Instantiate DataFlowConsistency --- unified/ql/consistency-queries/DataFlowConsistency.ql | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 unified/ql/consistency-queries/DataFlowConsistency.ql diff --git a/unified/ql/consistency-queries/DataFlowConsistency.ql b/unified/ql/consistency-queries/DataFlowConsistency.ql new file mode 100644 index 000000000000..267abd674cb5 --- /dev/null +++ b/unified/ql/consistency-queries/DataFlowConsistency.ql @@ -0,0 +1,10 @@ +private import unified +private import codeql.unified.internal.dataflow.AllDataFlow +private import codeql.dataflow.internal.DataFlowImplConsistency + +module ConsistencyInput implements InputSig { } + +module ConsistencyOutput = + MakeConsistency; + +import ConsistencyOutput From 09cfe5d39018d32aff6f25d2adaa286bc72c2e1c Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 14 Sep 2026 13:39:28 +0200 Subject: [PATCH 17/30] unified: Add flow through string interpolation --- .../lib/codeql/unified/internal/FacadeAst.qll | 3 +++ .../unified/internal/dataflow/DataFlowGraph.qll | 17 +++++++++++++++++ .../ql/test/library-tests/dataflow/test.swift | 5 +++++ 3 files changed, 25 insertions(+) diff --git a/unified/ql/lib/codeql/unified/internal/FacadeAst.qll b/unified/ql/lib/codeql/unified/internal/FacadeAst.qll index 5435876b3646..54efc88a473d 100644 --- a/unified/ql/lib/codeql/unified/internal/FacadeAst.qll +++ b/unified/ql/lib/codeql/unified/internal/FacadeAst.qll @@ -197,5 +197,8 @@ module Unified { result = arg.getValue() ) } + + /** Gets the number of arguments passed to this call, not counting implicit arguments like receiver. */ + int getNumberOfArguments() { result = count(this.getAnArgument()) } } } diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll index f3cf1f8facf2..7f7483c06a5b 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll @@ -45,6 +45,23 @@ predicate step(Node node1, Step step, Node node2) { node2.isResultValue(expr) ) or + exists(StringInterpolationExpr expr | + node1.isResultValue(expr.getAnElement()) and + step.taint() and + node2.isResultValue(expr) + ) + or + exists(CallExpr call | + // String interpolations in Swift currently insert a call to a built-in called "interpolation". + // Add taint through plain 1-argument calls to this built-in. + call.getCallee().(BuiltinExpr).getValue() = "interpolation" and + call.getNumberOfArguments() = 1 and + not exists(call.getArgument(0).getName()) and + node1.isResultValue(call.getArgument(0).getValue()) and + step.value() and + node2.isResultValue(call) + ) + or exists(TupleExpr expr, int i | node1.isResultValue(expr.getElement(i).getValue()) and step.storeName(i.toString()) and diff --git a/unified/ql/test/library-tests/dataflow/test.swift b/unified/ql/test/library-tests/dataflow/test.swift index 1fd40237559b..132afed56490 100644 --- a/unified/ql/test/library-tests/dataflow/test.swift +++ b/unified/ql/test/library-tests/dataflow/test.swift @@ -5,6 +5,11 @@ func t1() { func t2() { sink(source("t2.1") + "blah"); // $ hasTaintFlow=t2.1 sink("blah" + source("t2.2")); // $ hasTaintFlow=t2.2 + + sink("\(source("t2.3")) blah"); // $ hasTaintFlow=t2.3 + sink("blah \(source("t2.4"))"); // $ hasTaintFlow=t2.4 + sink("blah \(source("t2.5")) blah"); // $ hasTaintFlow=t2.5 + sink("blah \(escape: source("t2.6")) blah"); // no flow } func t3() { From a03cd48a86a46fc33389dc3cf966c3b89159b97a Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 14 Sep 2026 13:42:51 +0200 Subject: [PATCH 18/30] unified: Add dumb version of CleartextLogging --- unified/ql/lib/qlpack.yml | 1 + .../security/CWE-312/CleartextLogging.ql | 52 +++++++++++++++++++ .../CleartextLogging.expected | 8 +++ .../CleartextLogging/CleartextLogging.qlref | 2 + .../CleartextLoggingBad.swift | 2 + .../CleartextLoggingGood.swift | 2 + 6 files changed, 67 insertions(+) create mode 100644 unified/ql/src/queries/security/CWE-312/CleartextLogging.ql create mode 100644 unified/ql/test/query-tests/security/CWE-312/CleartextLogging/CleartextLogging.expected create mode 100644 unified/ql/test/query-tests/security/CWE-312/CleartextLogging/CleartextLogging.qlref create mode 100644 unified/ql/test/query-tests/security/CWE-312/CleartextLogging/CleartextLoggingBad.swift create mode 100644 unified/ql/test/query-tests/security/CWE-312/CleartextLogging/CleartextLoggingGood.swift diff --git a/unified/ql/lib/qlpack.yml b/unified/ql/lib/qlpack.yml index 2416cff78e94..84261b14adc7 100644 --- a/unified/ql/lib/qlpack.yml +++ b/unified/ql/lib/qlpack.yml @@ -6,6 +6,7 @@ extractor: unified library: true upgrades: upgrades dependencies: + codeql/concepts: ${workspace} codeql/controlflow: ${workspace} codeql/ssa: ${workspace} codeql/dataflow: ${workspace} diff --git a/unified/ql/src/queries/security/CWE-312/CleartextLogging.ql b/unified/ql/src/queries/security/CWE-312/CleartextLogging.ql new file mode 100644 index 000000000000..4aa751a1e91a --- /dev/null +++ b/unified/ql/src/queries/security/CWE-312/CleartextLogging.ql @@ -0,0 +1,52 @@ +/** + * @name Cleartext logging of sensitive information + * @description Logging sensitive information in plaintext can + * expose it to an attacker. + * @kind path-problem + * @problem.severity error + * @security-severity 7.5 + * @precision high + * @id unified/swift/cleartext-logging + * @tags security + * external/cwe/cwe-312 + * external/cwe/cwe-359 + * external/cwe/cwe-532 + */ + +// +// FIXME: This is a deliberately dumb and noisy version of the query used to exercise data flow early on. +// +import unified +import codeql.concepts.internal.SensitiveDataHeuristics + +private string getNameFromExpr(Expr e) { + result = e.(IdentifierExpr).getValue() + or + result = e.(MemberAccessExpr).getMemberName() +} + +module DummyConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node node) { + exists(string name | + name = getNameFromExpr(node.asExpr()) and + HeuristicNames::nameIndicatesSensitiveData(name) + ) + } + + predicate isSink(DataFlow::Node node) { + exists(CallExpr call | + getNameFromExpr(call.getCallee()).regexpMatch("(?i)(ns)?(log|warn(ing)?|error|print).*") and + node.asExpr() = call.getAnArgument().getValue() + ) + } + + predicate isBarrierIn(DataFlow::Node node) { isSource(node) } +} + +module DummyFlow = TaintTracking::Global; + +import DummyFlow::PathGraph + +from DummyFlow::PathNode source, DummyFlow::PathNode sink +where DummyFlow::flowPath(source, sink) +select sink.getNode(), source, sink, "Logging of $@", source.getNode(), "sensitive data" diff --git a/unified/ql/test/query-tests/security/CWE-312/CleartextLogging/CleartextLogging.expected b/unified/ql/test/query-tests/security/CWE-312/CleartextLogging/CleartextLogging.expected new file mode 100644 index 000000000000..cf60bed522d7 --- /dev/null +++ b/unified/ql/test/query-tests/security/CWE-312/CleartextLogging/CleartextLogging.expected @@ -0,0 +1,8 @@ +#select +| CleartextLoggingBad.swift:2:7:2:44 | StringInterpolationExpr | CleartextLoggingBad.swift:2:35:2:42 | password : unit | CleartextLoggingBad.swift:2:7:2:44 | StringInterpolationExpr | Logging of $@ | CleartextLoggingBad.swift:2:35:2:42 | password | sensitive data | +edges +| CleartextLoggingBad.swift:2:35:2:42 | password : unit | CleartextLoggingBad.swift:2:7:2:44 | StringInterpolationExpr | provenance | | +nodes +| CleartextLoggingBad.swift:2:7:2:44 | StringInterpolationExpr | semmle.label | StringInterpolationExpr | +| CleartextLoggingBad.swift:2:35:2:42 | password : unit | semmle.label | password : unit | +subpaths diff --git a/unified/ql/test/query-tests/security/CWE-312/CleartextLogging/CleartextLogging.qlref b/unified/ql/test/query-tests/security/CWE-312/CleartextLogging/CleartextLogging.qlref new file mode 100644 index 000000000000..53e950061f85 --- /dev/null +++ b/unified/ql/test/query-tests/security/CWE-312/CleartextLogging/CleartextLogging.qlref @@ -0,0 +1,2 @@ +query: queries/security/CWE-312/CleartextLogging.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/unified/ql/test/query-tests/security/CWE-312/CleartextLogging/CleartextLoggingBad.swift b/unified/ql/test/query-tests/security/CWE-312/CleartextLogging/CleartextLoggingBad.swift new file mode 100644 index 000000000000..bb6e84d1892b --- /dev/null +++ b/unified/ql/test/query-tests/security/CWE-312/CleartextLogging/CleartextLoggingBad.swift @@ -0,0 +1,2 @@ +let password = "P@ssw0rd" +NSLog("User password changed to \(password)") // $ Alert diff --git a/unified/ql/test/query-tests/security/CWE-312/CleartextLogging/CleartextLoggingGood.swift b/unified/ql/test/query-tests/security/CWE-312/CleartextLogging/CleartextLoggingGood.swift new file mode 100644 index 000000000000..1d90ac5e5656 --- /dev/null +++ b/unified/ql/test/query-tests/security/CWE-312/CleartextLogging/CleartextLoggingGood.swift @@ -0,0 +1,2 @@ +let password = "P@ssw0rd" +NSLog("User password changed") From 2d1a99c58a408930c7c81aa7bf983f43ae8d545a Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 14 Sep 2026 13:43:09 +0200 Subject: [PATCH 19/30] unified: Copy query help from old swift --- .../security/CWE-312/CleartextLogging.qhelp | 46 +++++++++++++++++++ .../CWE-312/CleartextLoggingBad.swift | 2 + .../CWE-312/CleartextLoggingGood.swift | 2 + 3 files changed, 50 insertions(+) create mode 100644 unified/ql/src/queries/security/CWE-312/CleartextLogging.qhelp create mode 100644 unified/ql/src/queries/security/CWE-312/CleartextLoggingBad.swift create mode 100644 unified/ql/src/queries/security/CWE-312/CleartextLoggingGood.swift diff --git a/unified/ql/src/queries/security/CWE-312/CleartextLogging.qhelp b/unified/ql/src/queries/security/CWE-312/CleartextLogging.qhelp new file mode 100644 index 000000000000..5959fe5ef8df --- /dev/null +++ b/unified/ql/src/queries/security/CWE-312/CleartextLogging.qhelp @@ -0,0 +1,46 @@ + + + + +

+Attackers could gain access to sensitive information that is logged unencrypted. +

+
+ + +

+Always make sure to encrypt or obfuscate sensitive information before you log it. +

+ +

+Generally, you should decrypt sensitive information only at the point where it is necessary for it to be used in cleartext. +

+ +

+Be aware that external processes often store the standard output and +standard error streams of the application. This will include logged sensitive information. +

+
+ + +

+The following example code logs user credentials (in this case, their password) +in plaintext: +

+ +

+Instead, you should encrypt or obfuscate the credentials, or omit them entirely: +

+ +
+ + + +
  • M. Dowd, J. McDonald and J. Schuhm, The Art of Software Security Assessment, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.
  • +
  • M. Howard and D. LeBlanc, Writing Secure Code, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.
  • +
  • OWASP: Password Plaintext Storage.
  • + +
    +
    diff --git a/unified/ql/src/queries/security/CWE-312/CleartextLoggingBad.swift b/unified/ql/src/queries/security/CWE-312/CleartextLoggingBad.swift new file mode 100644 index 000000000000..036001e87179 --- /dev/null +++ b/unified/ql/src/queries/security/CWE-312/CleartextLoggingBad.swift @@ -0,0 +1,2 @@ +let password = "P@ssw0rd" +NSLog("User password changed to \(password)") diff --git a/unified/ql/src/queries/security/CWE-312/CleartextLoggingGood.swift b/unified/ql/src/queries/security/CWE-312/CleartextLoggingGood.swift new file mode 100644 index 000000000000..1d90ac5e5656 --- /dev/null +++ b/unified/ql/src/queries/security/CWE-312/CleartextLoggingGood.swift @@ -0,0 +1,2 @@ +let password = "P@ssw0rd" +NSLog("User password changed") From 02564e913bcb05ed340467257ec1dd776b0693f3 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 14 Sep 2026 14:41:04 +0200 Subject: [PATCH 20/30] Add support for AssociatedTypeDeclaration return type Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- unified/ql/lib/codeql/unified/internal/ExprPositions.qll | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/unified/ql/lib/codeql/unified/internal/ExprPositions.qll b/unified/ql/lib/codeql/unified/internal/ExprPositions.qll index 4c38c5d241c9..2e64053642eb 100644 --- a/unified/ql/lib/codeql/unified/internal/ExprPositions.qll +++ b/unified/ql/lib/codeql/unified/internal/ExprPositions.qll @@ -13,6 +13,8 @@ predicate isInTypeContext(Expr expr) { or expr = any(FunctionDeclaration n).getReturnType() or + expr = any(FunctionExpr n).getReturnType() + or expr = any(AccessorDeclaration n).getType() or expr = any(Parameter n).getType() @@ -23,6 +25,8 @@ predicate isInTypeContext(Expr expr) { or expr = any(TypeParameter n).getBound() or + expr = any(AssociatedTypeDeclaration n).getBound() + or expr.getParent() instanceof TypeConstraint or isInTypeContext(expr.getEnclosingExpr()) From 3c332a39146e692028b4fadda30b8c9382af71f0 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 14 Sep 2026 17:43:48 +0200 Subject: [PATCH 21/30] Update unified/ql/src/queries/security/CWE-312/CleartextLogging.qhelp Co-authored-by: Geoffrey White <40627776+geoffw0@users.noreply.github.com> --- unified/ql/src/queries/security/CWE-312/CleartextLogging.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unified/ql/src/queries/security/CWE-312/CleartextLogging.qhelp b/unified/ql/src/queries/security/CWE-312/CleartextLogging.qhelp index 5959fe5ef8df..8de3f21878f0 100644 --- a/unified/ql/src/queries/security/CWE-312/CleartextLogging.qhelp +++ b/unified/ql/src/queries/security/CWE-312/CleartextLogging.qhelp @@ -40,7 +40,7 @@ Instead, you should encrypt or obfuscate the credentials, or omit them entirely:
  • M. Dowd, J. McDonald and J. Schuhm, The Art of Software Security Assessment, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.
  • M. Howard and D. LeBlanc, Writing Secure Code, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.
  • -
  • OWASP: Password Plaintext Storage.
  • +
  • OWASP: Logging Cheat Sheet.
  • From 0b48cd4ec21038ee3a50f0a1b05595ecf3409d0d Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 15 Sep 2026 10:42:35 +0200 Subject: [PATCH 22/30] unified: Add plugin --- .../unified/internal/dataflow/AllDataFlow.qll | 1 + .../internal/dataflow/DataFlowGraph.qll | 20 ++----------- .../internal/dataflow/DataFlowPlugin.qll | 13 +++++++++ .../internal/dataflow/DataFlowPluginSwift.qll | 29 +++++++++++++++++++ 4 files changed, 45 insertions(+), 18 deletions(-) create mode 100644 unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPlugin.qll create mode 100644 unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/AllDataFlow.qll b/unified/ql/lib/codeql/unified/internal/dataflow/AllDataFlow.qll index 53b53956cb23..893e62d3b140 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/AllDataFlow.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/AllDataFlow.qll @@ -4,6 +4,7 @@ import Content import DataFlowGraph import DataFlowInstantiation import DataFlowNode +import DataFlowPlugin import Step import LocalSsa import TaintTrackingInstantiation diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll index 7f7483c06a5b..1edff6eea04b 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll @@ -2,6 +2,8 @@ private import unified private import AllDataFlow predicate step(Node node1, Step step, Node node2) { + any(DataFlowPlugin p).step(node1, step, node2) + or exists(VariableDeclaration decl | node1.isResultValue(decl.getValue()) and step.value() and @@ -38,30 +40,12 @@ predicate step(Node node1, Step step, Node node2) { node2.isLocalVariablePostUpdate(access, access.getImplicitQualifierVariable()) ) or - exists(BinaryExpr expr | - expr.getOperator().getValue() = "+" and - node1.isResultValue([expr.getLeft(), expr.getRight()]) and - step.taint() and - node2.isResultValue(expr) - ) - or exists(StringInterpolationExpr expr | node1.isResultValue(expr.getAnElement()) and step.taint() and node2.isResultValue(expr) ) or - exists(CallExpr call | - // String interpolations in Swift currently insert a call to a built-in called "interpolation". - // Add taint through plain 1-argument calls to this built-in. - call.getCallee().(BuiltinExpr).getValue() = "interpolation" and - call.getNumberOfArguments() = 1 and - not exists(call.getArgument(0).getName()) and - node1.isResultValue(call.getArgument(0).getValue()) and - step.value() and - node2.isResultValue(call) - ) - or exists(TupleExpr expr, int i | node1.isResultValue(expr.getElement(i).getValue()) and step.storeName(i.toString()) and diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPlugin.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPlugin.qll new file mode 100644 index 000000000000..7bf02b5a7524 --- /dev/null +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPlugin.qll @@ -0,0 +1,13 @@ +/** + * Provides an interface for language-specific data flow rules. + */ + +private import unified +private import AllDataFlow +private import codeql.util.Unit +private import DataFlowPluginSwift // ensure overrides are seen + +class DataFlowPlugin extends Unit { + /** Holds if there is a language-specific step from `node1 -> step -> node2`. */ + predicate step(Node node1, Step step, Node node2) { none() } +} diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll new file mode 100644 index 000000000000..6cf9b5d487b8 --- /dev/null +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll @@ -0,0 +1,29 @@ +/** + * Provides Swift-specific data flow rules. + */ + +private import unified +private import AllDataFlow + +private class SwiftDataFlowPlugin extends DataFlowPlugin { + // Note: For now we assume all code is Swift, but in the future we must restrict these rules to Swift-files + override predicate step(Node node1, Step step, Node node2) { + exists(BinaryExpr expr | + expr.getOperator().getValue() = "+" and + node1.isResultValue([expr.getLeft(), expr.getRight()]) and + step.taint() and + node2.isResultValue(expr) + ) + or + exists(CallExpr call | + // String interpolations in Swift currently insert a call to a built-in called "interpolation". + // Add taint through plain 1-argument calls to this built-in. + call.getCallee().(BuiltinExpr).getValue() = "interpolation" and + call.getNumberOfArguments() = 1 and + not exists(call.getArgument(0).getName()) and + node1.isResultValue(call.getArgument(0).getValue()) and + step.value() and + node2.isResultValue(call) + ) + } +} From 2acd6da2fef4fe70a0681bae81ef0513e50dfdcc Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 15 Sep 2026 11:15:41 +0200 Subject: [PATCH 23/30] unified: Add local SSA consistency-query --- unified/ql/consistency-queries/LocalSsaConsistency.ql | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 unified/ql/consistency-queries/LocalSsaConsistency.ql diff --git a/unified/ql/consistency-queries/LocalSsaConsistency.ql b/unified/ql/consistency-queries/LocalSsaConsistency.ql new file mode 100644 index 000000000000..209adfca340a --- /dev/null +++ b/unified/ql/consistency-queries/LocalSsaConsistency.ql @@ -0,0 +1,3 @@ +private import unified +private import codeql.unified.internal.dataflow.LocalSsa +import LocalSsaOutput::Consistency From 6fe1db031d99de6b27acd56d81a69b743c492f65 Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 15 Sep 2026 11:25:55 +0200 Subject: [PATCH 24/30] unified: Add sink calls without flow --- .../ql/test/library-tests/dataflow/implicit-self.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/unified/ql/test/library-tests/dataflow/implicit-self.swift b/unified/ql/test/library-tests/dataflow/implicit-self.swift index 61b4ab17f292..8e13479d51fe 100644 --- a/unified/ql/test/library-tests/dataflow/implicit-self.swift +++ b/unified/ql/test/library-tests/dataflow/implicit-self.swift @@ -7,41 +7,49 @@ class C { var box = Box() func t1() { + sink(self.x); // no flow self.x = source("t1.1"); sink(self.x); // $ hasValueFlow=t1.1 } func t2() { + sink(x); // no flow x = source("t2.1"); sink(x); // $ hasValueFlow=t2.1 } func t3() { + sink(self.x); // no flow x = source("t3.1"); sink(self.x); // $ hasValueFlow=t3.1 } func t4() { + sink(x); // no flow self.x = source("t4.1"); sink(x); // $ hasValueFlow=t4.1 } func t5() { + sink(self.box.x); // no flow self.box.x = source("t5.1"); sink(self.box.x); // $ hasValueFlow=t5.1 } func t6() { + sink(box.x); // no flow box.x = source("t6.1"); sink(box.x); // $ hasValueFlow=t6.1 } func t7() { + sink(self.box.x); // no flow box.x = source("t7.1"); sink(self.box.x); // $ hasValueFlow=t7.1 } func t8() { + sink(box.x); // no flow self.box.x = source("t8.1"); sink(box.x); // $ hasValueFlow=t8.1 } From dea32194d0a1537a3ebf99330dd4f9c9829eba1a Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 15 Sep 2026 11:42:40 +0200 Subject: [PATCH 25/30] unified: Explain the "local" in "local SSA" --- unified/ql/lib/codeql/unified/internal/dataflow/LocalSsa.qll | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/LocalSsa.qll b/unified/ql/lib/codeql/unified/internal/dataflow/LocalSsa.qll index c08eeb29a9b5..6b4fe6c376c1 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/LocalSsa.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/LocalSsa.qll @@ -1,3 +1,7 @@ +/** + * SSA for non-captured variables. + */ + private import unified private import unified as U private import AllDataFlow From 5f2b8e804a0ed1a0ea3bc2e2a02f654c4b8accf8 Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 15 Sep 2026 11:48:10 +0200 Subject: [PATCH 26/30] unified: Rename to TExprPostUpdateNode for clarity This is not the only kind of post-update node (there is also TLocalVariableRef of kind post-update) --- .../lib/codeql/unified/internal/dataflow/DataFlowNode.qll | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll index 6872e69973d5..bc4bec52b948 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll @@ -37,7 +37,7 @@ predicate performsVariableAccess( newtype TDataFlowNode = TValueNode(Expr expr) { hasResultValue(expr) or hasIncomingValue(expr, _) } or TStrictlyIncomingValue(Expr expr) { hasResultValue(expr) and hasIncomingValue(expr, _) } or - TPostUpdateNode(Expr expr) { hasPostUpdate(expr) } or + TExprPostUpdateNode(Expr expr) { hasPostUpdate(expr) } or TLocalVariableRefNode(Expr expr, LocalVariable var, VariableRefKind kind) { performsVariableAccess(expr, var, kind, _) } or @@ -83,7 +83,7 @@ class Node extends TDataFlowNode { } /** Holds if this represents the updated state of the value returned by `expr` after it has been mutated by the surrounding assignment or call. */ - predicate isPostUpdate(Expr expr) { this = TPostUpdateNode(expr) } + predicate isPostUpdate(Expr expr) { this = TExprPostUpdateNode(expr) } /** Gets the expression represented by this node. */ Expr asExpr() { this = TValueNode(result) } @@ -94,7 +94,7 @@ class Node extends TDataFlowNode { AstNode getWrappedAstNode() { result = this.asExpr() or this = TStrictlyIncomingValue(result) or - this = TPostUpdateNode(result) or + this = TExprPostUpdateNode(result) or this = TLocalVariableRefNode(result, _, _) } @@ -106,7 +106,7 @@ class Node extends TDataFlowNode { this = TStrictlyIncomingValue(expr) and result = "[incoming] " + expr.toString() or - this = TPostUpdateNode(expr) and + this = TExprPostUpdateNode(expr) and result = "[post] " + expr.toString() ) or From 535b215b24203c573d56279e34445a222358830c Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 15 Sep 2026 12:44:31 +0200 Subject: [PATCH 27/30] unified: Fill in nodeIsVisible and neverSkipInPathGraph --- .../internal/dataflow/DataFlowInstantiation.qll | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll index a51ab0cd25f9..d4b03b4b59c4 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll @@ -135,7 +135,20 @@ module DataFlowInput implements InputSig { // // Misc // - predicate nodeIsHidden(Node node) { none() } // TODO + additional predicate nodeIsVisible(Node node) { + node instanceof TValueNode + or + node instanceof TStrictlyIncomingValue + or + node instanceof TExprPostUpdateNode + } + + predicate nodeIsHidden(Node node) { not nodeIsVisible(node) } + + predicate neverSkipInPathGraph(Node n) { + n.isIncomingValue(_) or // Never skip assignment target + n.asExpr() instanceof LocalVariableAccess // Never skip a variable reference + } class DataFlowExpr = Expr; From 6f0f14a3d301bf48d75221c3199dc529aa1a2577 Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 15 Sep 2026 12:53:33 +0200 Subject: [PATCH 28/30] unified: Include path problem output tuples --- .../test/library-tests/dataflow/test.expected | 265 ++++++++++++++++++ .../ql/test/library-tests/dataflow/test.ql | 12 + 2 files changed, 277 insertions(+) diff --git a/unified/ql/test/library-tests/dataflow/test.expected b/unified/ql/test/library-tests/dataflow/test.expected index e69de29bb2d1..20a3141892fb 100644 --- a/unified/ql/test/library-tests/dataflow/test.expected +++ b/unified/ql/test/library-tests/dataflow/test.expected @@ -0,0 +1,265 @@ +models +edges +| implicit-self.swift:11:9:11:12 | [post] self : unit [x] : unit | implicit-self.swift:12:14:12:17 | self : unit [x] : unit | provenance | | +| implicit-self.swift:11:9:11:14 | MemberAccessExpr : unit | implicit-self.swift:11:9:11:12 | [post] self : unit [x] : unit | provenance | | +| implicit-self.swift:11:18:11:31 | CallExpr : unit | implicit-self.swift:11:9:11:14 | MemberAccessExpr : unit | provenance | | +| implicit-self.swift:12:14:12:17 | self : unit [x] : unit | implicit-self.swift:12:14:12:19 | MemberAccessExpr | provenance | | +| implicit-self.swift:17:9:17:9 | x : unit | implicit-self.swift:18:14:18:14 | x | provenance | | +| implicit-self.swift:17:13:17:26 | CallExpr : unit | implicit-self.swift:17:9:17:9 | x : unit | provenance | | +| implicit-self.swift:23:9:23:9 | x : unit | implicit-self.swift:24:14:24:17 | self : unit [x] : unit | provenance | | +| implicit-self.swift:23:13:23:26 | CallExpr : unit | implicit-self.swift:23:9:23:9 | x : unit | provenance | | +| implicit-self.swift:24:14:24:17 | self : unit [x] : unit | implicit-self.swift:24:14:24:19 | MemberAccessExpr | provenance | | +| implicit-self.swift:29:9:29:12 | [post] self : unit [x] : unit | implicit-self.swift:30:14:30:14 | x | provenance | | +| implicit-self.swift:29:9:29:14 | MemberAccessExpr : unit | implicit-self.swift:29:9:29:12 | [post] self : unit [x] : unit | provenance | | +| implicit-self.swift:29:18:29:31 | CallExpr : unit | implicit-self.swift:29:9:29:14 | MemberAccessExpr : unit | provenance | | +| implicit-self.swift:35:9:35:12 | [post] self : unit [box, x] : unit | implicit-self.swift:36:14:36:17 | self : unit [box, x] : unit | provenance | | +| implicit-self.swift:35:9:35:16 | [post] MemberAccessExpr : unit [x] : unit | implicit-self.swift:35:9:35:12 | [post] self : unit [box, x] : unit | provenance | | +| implicit-self.swift:35:9:35:18 | MemberAccessExpr : unit | implicit-self.swift:35:9:35:16 | [post] MemberAccessExpr : unit [x] : unit | provenance | | +| implicit-self.swift:35:22:35:35 | CallExpr : unit | implicit-self.swift:35:9:35:18 | MemberAccessExpr : unit | provenance | | +| implicit-self.swift:36:14:36:17 | self : unit [box, x] : unit | implicit-self.swift:36:14:36:21 | MemberAccessExpr : unit [x] : unit | provenance | | +| implicit-self.swift:36:14:36:21 | MemberAccessExpr : unit [x] : unit | implicit-self.swift:36:14:36:23 | MemberAccessExpr | provenance | | +| implicit-self.swift:41:9:41:11 | [post] box : unit [x] : unit | implicit-self.swift:42:14:42:16 | box : unit [x] : unit | provenance | | +| implicit-self.swift:41:9:41:13 | MemberAccessExpr : unit | implicit-self.swift:41:9:41:11 | [post] box : unit [x] : unit | provenance | | +| implicit-self.swift:41:17:41:30 | CallExpr : unit | implicit-self.swift:41:9:41:13 | MemberAccessExpr : unit | provenance | | +| implicit-self.swift:42:14:42:16 | box : unit [x] : unit | implicit-self.swift:42:14:42:18 | MemberAccessExpr | provenance | | +| implicit-self.swift:47:9:47:11 | [post] box : unit [x] : unit | implicit-self.swift:48:14:48:17 | self : unit [box, x] : unit | provenance | | +| implicit-self.swift:47:9:47:13 | MemberAccessExpr : unit | implicit-self.swift:47:9:47:11 | [post] box : unit [x] : unit | provenance | | +| implicit-self.swift:47:17:47:30 | CallExpr : unit | implicit-self.swift:47:9:47:13 | MemberAccessExpr : unit | provenance | | +| implicit-self.swift:48:14:48:17 | self : unit [box, x] : unit | implicit-self.swift:48:14:48:21 | MemberAccessExpr : unit [x] : unit | provenance | | +| implicit-self.swift:48:14:48:21 | MemberAccessExpr : unit [x] : unit | implicit-self.swift:48:14:48:23 | MemberAccessExpr | provenance | | +| implicit-self.swift:53:9:53:12 | [post] self : unit [box, x] : unit | implicit-self.swift:54:14:54:16 | box : unit [x] : unit | provenance | | +| implicit-self.swift:53:9:53:16 | [post] MemberAccessExpr : unit [x] : unit | implicit-self.swift:53:9:53:12 | [post] self : unit [box, x] : unit | provenance | | +| implicit-self.swift:53:9:53:18 | MemberAccessExpr : unit | implicit-self.swift:53:9:53:16 | [post] MemberAccessExpr : unit [x] : unit | provenance | | +| implicit-self.swift:53:22:53:35 | CallExpr : unit | implicit-self.swift:53:9:53:18 | MemberAccessExpr : unit | provenance | | +| implicit-self.swift:54:14:54:16 | box : unit [x] : unit | implicit-self.swift:54:14:54:18 | MemberAccessExpr | provenance | | +| test.swift:6:10:6:23 | CallExpr : unit | test.swift:6:10:6:32 | BinaryExpr | provenance | | +| test.swift:7:19:7:32 | CallExpr : unit | test.swift:7:10:7:32 | BinaryExpr | provenance | | +| test.swift:9:13:9:26 | CallExpr : unit | test.swift:9:10:9:33 | StringInterpolationExpr | provenance | | +| test.swift:10:18:10:31 | CallExpr : unit | test.swift:10:10:10:33 | StringInterpolationExpr | provenance | | +| test.swift:11:18:11:31 | CallExpr : unit | test.swift:11:10:11:38 | StringInterpolationExpr | provenance | | +| test.swift:16:10:16:33 | TupleExpr : unit [0] : unit | test.swift:16:10:16:35 | MemberAccessExpr | provenance | | +| test.swift:16:11:16:25 | CallExpr : unit | test.swift:16:10:16:33 | TupleExpr : unit [0] : unit | provenance | | +| test.swift:19:10:19:33 | TupleExpr : unit [1] : unit | test.swift:19:10:19:35 | MemberAccessExpr | provenance | | +| test.swift:19:19:19:32 | CallExpr : unit | test.swift:19:10:19:33 | TupleExpr : unit [1] : unit | provenance | | +| test.swift:23:9:23:9 | a : unit | test.swift:24:10:24:10 | a | provenance | | +| test.swift:23:13:23:26 | CallExpr : unit | test.swift:23:9:23:9 | a : unit | provenance | | +| test.swift:28:9:28:14 | TupleExpr : unit [0] : unit | test.swift:28:10:28:10 | a : unit | provenance | | +| test.swift:28:10:28:10 | a : unit | test.swift:29:10:29:10 | a | provenance | | +| test.swift:28:18:28:41 | TupleExpr : unit [0] : unit | test.swift:28:9:28:14 | TupleExpr : unit [0] : unit | provenance | | +| test.swift:28:19:28:33 | CallExpr : unit | test.swift:28:18:28:41 | TupleExpr : unit [0] : unit | provenance | | +| test.swift:32:9:32:14 | TupleExpr : unit [1] : unit | test.swift:32:13:32:13 | d : unit | provenance | | +| test.swift:32:13:32:13 | d : unit | test.swift:34:10:34:10 | d | provenance | | +| test.swift:32:18:32:41 | TupleExpr : unit [1] : unit | test.swift:32:9:32:14 | TupleExpr : unit [1] : unit | provenance | | +| test.swift:32:27:32:40 | CallExpr : unit | test.swift:32:18:32:41 | TupleExpr : unit [1] : unit | provenance | | +| test.swift:38:9:38:9 | a : unit | test.swift:39:10:39:10 | a | provenance | | +| test.swift:38:13:38:26 | CallExpr : unit | test.swift:38:9:38:9 | a : unit | provenance | | +| test.swift:46:5:46:9 | [post] tuple : unit [0] : unit | test.swift:47:10:47:14 | tuple : unit [0] : unit | provenance | | +| test.swift:46:5:46:11 | MemberAccessExpr : unit | test.swift:46:5:46:9 | [post] tuple : unit [0] : unit | provenance | | +| test.swift:46:15:46:28 | CallExpr : unit | test.swift:46:5:46:11 | MemberAccessExpr : unit | provenance | | +| test.swift:47:10:47:14 | tuple : unit [0] : unit | test.swift:47:10:47:16 | MemberAccessExpr | provenance | | +| test.swift:53:5:53:14 | [post] deep_tuple : unit [1, 0] : unit | test.swift:58:10:58:19 | deep_tuple : unit [1, 0] : unit | provenance | | +| test.swift:53:5:53:16 | [post] MemberAccessExpr : unit [0] : unit | test.swift:53:5:53:14 | [post] deep_tuple : unit [1, 0] : unit | provenance | | +| test.swift:53:5:53:18 | MemberAccessExpr : unit | test.swift:53:5:53:16 | [post] MemberAccessExpr : unit [0] : unit | provenance | | +| test.swift:53:22:53:35 | CallExpr : unit | test.swift:53:5:53:18 | MemberAccessExpr : unit | provenance | | +| test.swift:58:10:58:19 | deep_tuple : unit [1, 0] : unit | test.swift:58:10:58:21 | MemberAccessExpr : unit [0] : unit | provenance | | +| test.swift:58:10:58:21 | MemberAccessExpr : unit [0] : unit | test.swift:58:10:58:23 | MemberAccessExpr | provenance | | +| test.swift:64:5:64:16 | TupleExpr : unit [0] : unit | test.swift:64:6:64:12 | MemberAccessExpr : unit | provenance | | +| test.swift:64:6:64:10 | [post] tuple : unit [1] : unit | test.swift:66:10:66:14 | tuple : unit [1] : unit | provenance | | +| test.swift:64:6:64:12 | MemberAccessExpr : unit | test.swift:64:6:64:10 | [post] tuple : unit [1] : unit | provenance | | +| test.swift:64:20:64:51 | TupleExpr : unit [0] : unit | test.swift:64:5:64:16 | TupleExpr : unit [0] : unit | provenance | | +| test.swift:64:21:64:35 | CallExpr : unit | test.swift:64:20:64:51 | TupleExpr : unit [0] : unit | provenance | | +| test.swift:66:10:66:14 | tuple : unit [1] : unit | test.swift:66:10:66:16 | MemberAccessExpr | provenance | | +| test.swift:74:5:74:9 | [post] tuple : unit [0] : unit | test.swift:75:10:75:14 | tuple : unit [0] : unit | provenance | | +| test.swift:74:5:74:11 | MemberAccessExpr : unit | test.swift:74:5:74:9 | [post] tuple : unit [0] : unit | provenance | | +| test.swift:74:15:74:29 | CallExpr : unit | test.swift:74:5:74:11 | MemberAccessExpr : unit | provenance | | +| test.swift:75:10:75:14 | tuple : unit [0] : unit | test.swift:75:10:75:16 | MemberAccessExpr | provenance | | +| test.swift:86:9:86:9 | x : unit | test.swift:90:10:90:10 | x | provenance | | +| test.swift:86:9:86:9 | x : unit | test.swift:99:14:99:14 | x | provenance | | +| test.swift:86:13:86:27 | CallExpr : unit | test.swift:86:9:86:9 | x : unit | provenance | | +| test.swift:94:9:94:9 | y : unit | test.swift:96:10:96:10 | y | provenance | | +| test.swift:94:9:94:9 | y : unit | test.swift:100:14:100:14 | y | provenance | | +| test.swift:94:13:94:27 | CallExpr : unit | test.swift:94:9:94:9 | y : unit | provenance | | +| test.swift:107:9:107:13 | [post] tuple : unit [0] : unit | test.swift:112:10:112:14 | tuple : unit [0] : unit | provenance | | +| test.swift:107:9:107:15 | MemberAccessExpr : unit | test.swift:107:9:107:13 | [post] tuple : unit [0] : unit | provenance | | +| test.swift:107:19:107:33 | CallExpr : unit | test.swift:107:9:107:15 | MemberAccessExpr : unit | provenance | | +| test.swift:112:10:112:14 | tuple : unit [0] : unit | test.swift:112:10:112:16 | MemberAccessExpr | provenance | | +| test.swift:117:9:117:13 | tuple : unit [0] : unit | test.swift:120:17:120:21 | tuple : unit [0] : unit | provenance | | +| test.swift:117:9:117:13 | tuple : unit [1] : unit | test.swift:120:17:120:21 | tuple : unit [1] : unit | provenance | | +| test.swift:117:17:117:50 | TupleExpr : unit [0] : unit | test.swift:117:9:117:13 | tuple : unit [0] : unit | provenance | | +| test.swift:117:17:117:50 | TupleExpr : unit [1] : unit | test.swift:117:9:117:13 | tuple : unit [1] : unit | provenance | | +| test.swift:117:18:117:33 | CallExpr : unit | test.swift:117:17:117:50 | TupleExpr : unit [0] : unit | provenance | | +| test.swift:117:35:117:49 | CallExpr : unit | test.swift:117:17:117:50 | TupleExpr : unit [1] : unit | provenance | | +| test.swift:120:9:120:13 | TupleExpr : unit [0] : unit | test.swift:120:10:120:10 | a : unit | provenance | | +| test.swift:120:9:120:13 | TupleExpr : unit [1] : unit | test.swift:120:12:120:12 | b : unit | provenance | | +| test.swift:120:10:120:10 | a : unit | test.swift:125:10:125:10 | a | provenance | | +| test.swift:120:12:120:12 | b : unit | test.swift:126:10:126:10 | b | provenance | | +| test.swift:120:17:120:21 | tuple : unit [0] : unit | test.swift:120:9:120:13 | TupleExpr : unit [0] : unit | provenance | | +| test.swift:120:17:120:21 | tuple : unit [1] : unit | test.swift:120:9:120:13 | TupleExpr : unit [1] : unit | provenance | | +| test.swift:131:5:131:5 | a : unit | test.swift:131:14:131:14 | a | provenance | | +| test.swift:131:5:131:5 | a : unit | test.swift:132:10:132:10 | a | provenance | | +| test.swift:131:19:131:33 | CallExpr : unit | test.swift:131:5:131:5 | a : unit | provenance | | +nodes +| implicit-self.swift:11:9:11:12 | [post] self : unit [x] : unit | semmle.label | [post] self : unit [x] : unit | +| implicit-self.swift:11:9:11:14 | MemberAccessExpr : unit | semmle.label | MemberAccessExpr : unit | +| implicit-self.swift:11:18:11:31 | CallExpr : unit | semmle.label | CallExpr : unit | +| implicit-self.swift:12:14:12:17 | self : unit [x] : unit | semmle.label | self : unit [x] : unit | +| implicit-self.swift:12:14:12:19 | MemberAccessExpr | semmle.label | MemberAccessExpr | +| implicit-self.swift:17:9:17:9 | x : unit | semmle.label | x : unit | +| implicit-self.swift:17:13:17:26 | CallExpr : unit | semmle.label | CallExpr : unit | +| implicit-self.swift:18:14:18:14 | x | semmle.label | x | +| implicit-self.swift:23:9:23:9 | x : unit | semmle.label | x : unit | +| implicit-self.swift:23:13:23:26 | CallExpr : unit | semmle.label | CallExpr : unit | +| implicit-self.swift:24:14:24:17 | self : unit [x] : unit | semmle.label | self : unit [x] : unit | +| implicit-self.swift:24:14:24:19 | MemberAccessExpr | semmle.label | MemberAccessExpr | +| implicit-self.swift:29:9:29:12 | [post] self : unit [x] : unit | semmle.label | [post] self : unit [x] : unit | +| implicit-self.swift:29:9:29:14 | MemberAccessExpr : unit | semmle.label | MemberAccessExpr : unit | +| implicit-self.swift:29:18:29:31 | CallExpr : unit | semmle.label | CallExpr : unit | +| implicit-self.swift:30:14:30:14 | x | semmle.label | x | +| implicit-self.swift:35:9:35:12 | [post] self : unit [box, x] : unit | semmle.label | [post] self : unit [box, x] : unit | +| implicit-self.swift:35:9:35:16 | [post] MemberAccessExpr : unit [x] : unit | semmle.label | [post] MemberAccessExpr : unit [x] : unit | +| implicit-self.swift:35:9:35:18 | MemberAccessExpr : unit | semmle.label | MemberAccessExpr : unit | +| implicit-self.swift:35:22:35:35 | CallExpr : unit | semmle.label | CallExpr : unit | +| implicit-self.swift:36:14:36:17 | self : unit [box, x] : unit | semmle.label | self : unit [box, x] : unit | +| implicit-self.swift:36:14:36:21 | MemberAccessExpr : unit [x] : unit | semmle.label | MemberAccessExpr : unit [x] : unit | +| implicit-self.swift:36:14:36:23 | MemberAccessExpr | semmle.label | MemberAccessExpr | +| implicit-self.swift:41:9:41:11 | [post] box : unit [x] : unit | semmle.label | [post] box : unit [x] : unit | +| implicit-self.swift:41:9:41:13 | MemberAccessExpr : unit | semmle.label | MemberAccessExpr : unit | +| implicit-self.swift:41:17:41:30 | CallExpr : unit | semmle.label | CallExpr : unit | +| implicit-self.swift:42:14:42:16 | box : unit [x] : unit | semmle.label | box : unit [x] : unit | +| implicit-self.swift:42:14:42:18 | MemberAccessExpr | semmle.label | MemberAccessExpr | +| implicit-self.swift:47:9:47:11 | [post] box : unit [x] : unit | semmle.label | [post] box : unit [x] : unit | +| implicit-self.swift:47:9:47:13 | MemberAccessExpr : unit | semmle.label | MemberAccessExpr : unit | +| implicit-self.swift:47:17:47:30 | CallExpr : unit | semmle.label | CallExpr : unit | +| implicit-self.swift:48:14:48:17 | self : unit [box, x] : unit | semmle.label | self : unit [box, x] : unit | +| implicit-self.swift:48:14:48:21 | MemberAccessExpr : unit [x] : unit | semmle.label | MemberAccessExpr : unit [x] : unit | +| implicit-self.swift:48:14:48:23 | MemberAccessExpr | semmle.label | MemberAccessExpr | +| implicit-self.swift:53:9:53:12 | [post] self : unit [box, x] : unit | semmle.label | [post] self : unit [box, x] : unit | +| implicit-self.swift:53:9:53:16 | [post] MemberAccessExpr : unit [x] : unit | semmle.label | [post] MemberAccessExpr : unit [x] : unit | +| implicit-self.swift:53:9:53:18 | MemberAccessExpr : unit | semmle.label | MemberAccessExpr : unit | +| implicit-self.swift:53:22:53:35 | CallExpr : unit | semmle.label | CallExpr : unit | +| implicit-self.swift:54:14:54:16 | box : unit [x] : unit | semmle.label | box : unit [x] : unit | +| implicit-self.swift:54:14:54:18 | MemberAccessExpr | semmle.label | MemberAccessExpr | +| test.swift:2:10:2:21 | CallExpr | semmle.label | CallExpr | +| test.swift:6:10:6:23 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:6:10:6:32 | BinaryExpr | semmle.label | BinaryExpr | +| test.swift:7:10:7:32 | BinaryExpr | semmle.label | BinaryExpr | +| test.swift:7:19:7:32 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:9:10:9:33 | StringInterpolationExpr | semmle.label | StringInterpolationExpr | +| test.swift:9:13:9:26 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:10:10:10:33 | StringInterpolationExpr | semmle.label | StringInterpolationExpr | +| test.swift:10:18:10:31 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:11:10:11:38 | StringInterpolationExpr | semmle.label | StringInterpolationExpr | +| test.swift:11:18:11:31 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:16:10:16:33 | TupleExpr : unit [0] : unit | semmle.label | TupleExpr : unit [0] : unit | +| test.swift:16:10:16:35 | MemberAccessExpr | semmle.label | MemberAccessExpr | +| test.swift:16:11:16:25 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:19:10:19:33 | TupleExpr : unit [1] : unit | semmle.label | TupleExpr : unit [1] : unit | +| test.swift:19:10:19:35 | MemberAccessExpr | semmle.label | MemberAccessExpr | +| test.swift:19:19:19:32 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:23:9:23:9 | a : unit | semmle.label | a : unit | +| test.swift:23:13:23:26 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:24:10:24:10 | a | semmle.label | a | +| test.swift:28:9:28:14 | TupleExpr : unit [0] : unit | semmle.label | TupleExpr : unit [0] : unit | +| test.swift:28:10:28:10 | a : unit | semmle.label | a : unit | +| test.swift:28:18:28:41 | TupleExpr : unit [0] : unit | semmle.label | TupleExpr : unit [0] : unit | +| test.swift:28:19:28:33 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:29:10:29:10 | a | semmle.label | a | +| test.swift:32:9:32:14 | TupleExpr : unit [1] : unit | semmle.label | TupleExpr : unit [1] : unit | +| test.swift:32:13:32:13 | d : unit | semmle.label | d : unit | +| test.swift:32:18:32:41 | TupleExpr : unit [1] : unit | semmle.label | TupleExpr : unit [1] : unit | +| test.swift:32:27:32:40 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:34:10:34:10 | d | semmle.label | d | +| test.swift:38:9:38:9 | a : unit | semmle.label | a : unit | +| test.swift:38:13:38:26 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:39:10:39:10 | a | semmle.label | a | +| test.swift:46:5:46:9 | [post] tuple : unit [0] : unit | semmle.label | [post] tuple : unit [0] : unit | +| test.swift:46:5:46:11 | MemberAccessExpr : unit | semmle.label | MemberAccessExpr : unit | +| test.swift:46:15:46:28 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:47:10:47:14 | tuple : unit [0] : unit | semmle.label | tuple : unit [0] : unit | +| test.swift:47:10:47:16 | MemberAccessExpr | semmle.label | MemberAccessExpr | +| test.swift:53:5:53:14 | [post] deep_tuple : unit [1, 0] : unit | semmle.label | [post] deep_tuple : unit [1, 0] : unit | +| test.swift:53:5:53:16 | [post] MemberAccessExpr : unit [0] : unit | semmle.label | [post] MemberAccessExpr : unit [0] : unit | +| test.swift:53:5:53:18 | MemberAccessExpr : unit | semmle.label | MemberAccessExpr : unit | +| test.swift:53:22:53:35 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:58:10:58:19 | deep_tuple : unit [1, 0] : unit | semmle.label | deep_tuple : unit [1, 0] : unit | +| test.swift:58:10:58:21 | MemberAccessExpr : unit [0] : unit | semmle.label | MemberAccessExpr : unit [0] : unit | +| test.swift:58:10:58:23 | MemberAccessExpr | semmle.label | MemberAccessExpr | +| test.swift:64:5:64:16 | TupleExpr : unit [0] : unit | semmle.label | TupleExpr : unit [0] : unit | +| test.swift:64:6:64:10 | [post] tuple : unit [1] : unit | semmle.label | [post] tuple : unit [1] : unit | +| test.swift:64:6:64:12 | MemberAccessExpr : unit | semmle.label | MemberAccessExpr : unit | +| test.swift:64:20:64:51 | TupleExpr : unit [0] : unit | semmle.label | TupleExpr : unit [0] : unit | +| test.swift:64:21:64:35 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:66:10:66:14 | tuple : unit [1] : unit | semmle.label | tuple : unit [1] : unit | +| test.swift:66:10:66:16 | MemberAccessExpr | semmle.label | MemberAccessExpr | +| test.swift:74:5:74:9 | [post] tuple : unit [0] : unit | semmle.label | [post] tuple : unit [0] : unit | +| test.swift:74:5:74:11 | MemberAccessExpr : unit | semmle.label | MemberAccessExpr : unit | +| test.swift:74:15:74:29 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:75:10:75:14 | tuple : unit [0] : unit | semmle.label | tuple : unit [0] : unit | +| test.swift:75:10:75:16 | MemberAccessExpr | semmle.label | MemberAccessExpr | +| test.swift:86:9:86:9 | x : unit | semmle.label | x : unit | +| test.swift:86:13:86:27 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:90:10:90:10 | x | semmle.label | x | +| test.swift:94:9:94:9 | y : unit | semmle.label | y : unit | +| test.swift:94:13:94:27 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:96:10:96:10 | y | semmle.label | y | +| test.swift:99:14:99:14 | x | semmle.label | x | +| test.swift:100:14:100:14 | y | semmle.label | y | +| test.swift:107:9:107:13 | [post] tuple : unit [0] : unit | semmle.label | [post] tuple : unit [0] : unit | +| test.swift:107:9:107:15 | MemberAccessExpr : unit | semmle.label | MemberAccessExpr : unit | +| test.swift:107:19:107:33 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:112:10:112:14 | tuple : unit [0] : unit | semmle.label | tuple : unit [0] : unit | +| test.swift:112:10:112:16 | MemberAccessExpr | semmle.label | MemberAccessExpr | +| test.swift:117:9:117:13 | tuple : unit [0] : unit | semmle.label | tuple : unit [0] : unit | +| test.swift:117:9:117:13 | tuple : unit [1] : unit | semmle.label | tuple : unit [1] : unit | +| test.swift:117:17:117:50 | TupleExpr : unit [0] : unit | semmle.label | TupleExpr : unit [0] : unit | +| test.swift:117:17:117:50 | TupleExpr : unit [1] : unit | semmle.label | TupleExpr : unit [1] : unit | +| test.swift:117:18:117:33 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:117:35:117:49 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:120:9:120:13 | TupleExpr : unit [0] : unit | semmle.label | TupleExpr : unit [0] : unit | +| test.swift:120:9:120:13 | TupleExpr : unit [1] : unit | semmle.label | TupleExpr : unit [1] : unit | +| test.swift:120:10:120:10 | a : unit | semmle.label | a : unit | +| test.swift:120:12:120:12 | b : unit | semmle.label | b : unit | +| test.swift:120:17:120:21 | tuple : unit [0] : unit | semmle.label | tuple : unit [0] : unit | +| test.swift:120:17:120:21 | tuple : unit [1] : unit | semmle.label | tuple : unit [1] : unit | +| test.swift:125:10:125:10 | a | semmle.label | a | +| test.swift:126:10:126:10 | b | semmle.label | b | +| test.swift:131:5:131:5 | a : unit | semmle.label | a : unit | +| test.swift:131:14:131:14 | a | semmle.label | a | +| test.swift:131:19:131:33 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:132:10:132:10 | a | semmle.label | a | +subpaths +testFailures +#select +| implicit-self.swift:12:14:12:19 | MemberAccessExpr | implicit-self.swift:11:18:11:31 | CallExpr : unit | implicit-self.swift:12:14:12:19 | MemberAccessExpr | $@ | implicit-self.swift:11:18:11:31 | CallExpr : unit | CallExpr : unit | +| implicit-self.swift:18:14:18:14 | x | implicit-self.swift:17:13:17:26 | CallExpr : unit | implicit-self.swift:18:14:18:14 | x | $@ | implicit-self.swift:17:13:17:26 | CallExpr : unit | CallExpr : unit | +| implicit-self.swift:24:14:24:19 | MemberAccessExpr | implicit-self.swift:23:13:23:26 | CallExpr : unit | implicit-self.swift:24:14:24:19 | MemberAccessExpr | $@ | implicit-self.swift:23:13:23:26 | CallExpr : unit | CallExpr : unit | +| implicit-self.swift:30:14:30:14 | x | implicit-self.swift:29:18:29:31 | CallExpr : unit | implicit-self.swift:30:14:30:14 | x | $@ | implicit-self.swift:29:18:29:31 | CallExpr : unit | CallExpr : unit | +| implicit-self.swift:36:14:36:23 | MemberAccessExpr | implicit-self.swift:35:22:35:35 | CallExpr : unit | implicit-self.swift:36:14:36:23 | MemberAccessExpr | $@ | implicit-self.swift:35:22:35:35 | CallExpr : unit | CallExpr : unit | +| implicit-self.swift:42:14:42:18 | MemberAccessExpr | implicit-self.swift:41:17:41:30 | CallExpr : unit | implicit-self.swift:42:14:42:18 | MemberAccessExpr | $@ | implicit-self.swift:41:17:41:30 | CallExpr : unit | CallExpr : unit | +| implicit-self.swift:48:14:48:23 | MemberAccessExpr | implicit-self.swift:47:17:47:30 | CallExpr : unit | implicit-self.swift:48:14:48:23 | MemberAccessExpr | $@ | implicit-self.swift:47:17:47:30 | CallExpr : unit | CallExpr : unit | +| implicit-self.swift:54:14:54:18 | MemberAccessExpr | implicit-self.swift:53:22:53:35 | CallExpr : unit | implicit-self.swift:54:14:54:18 | MemberAccessExpr | $@ | implicit-self.swift:53:22:53:35 | CallExpr : unit | CallExpr : unit | +| test.swift:2:10:2:21 | CallExpr | test.swift:2:10:2:21 | CallExpr | test.swift:2:10:2:21 | CallExpr | $@ | test.swift:2:10:2:21 | CallExpr | CallExpr | +| test.swift:6:10:6:32 | BinaryExpr | test.swift:6:10:6:23 | CallExpr : unit | test.swift:6:10:6:32 | BinaryExpr | $@ | test.swift:6:10:6:23 | CallExpr : unit | CallExpr : unit | +| test.swift:7:10:7:32 | BinaryExpr | test.swift:7:19:7:32 | CallExpr : unit | test.swift:7:10:7:32 | BinaryExpr | $@ | test.swift:7:19:7:32 | CallExpr : unit | CallExpr : unit | +| test.swift:9:10:9:33 | StringInterpolationExpr | test.swift:9:13:9:26 | CallExpr : unit | test.swift:9:10:9:33 | StringInterpolationExpr | $@ | test.swift:9:13:9:26 | CallExpr : unit | CallExpr : unit | +| test.swift:10:10:10:33 | StringInterpolationExpr | test.swift:10:18:10:31 | CallExpr : unit | test.swift:10:10:10:33 | StringInterpolationExpr | $@ | test.swift:10:18:10:31 | CallExpr : unit | CallExpr : unit | +| test.swift:11:10:11:38 | StringInterpolationExpr | test.swift:11:18:11:31 | CallExpr : unit | test.swift:11:10:11:38 | StringInterpolationExpr | $@ | test.swift:11:18:11:31 | CallExpr : unit | CallExpr : unit | +| test.swift:16:10:16:35 | MemberAccessExpr | test.swift:16:11:16:25 | CallExpr : unit | test.swift:16:10:16:35 | MemberAccessExpr | $@ | test.swift:16:11:16:25 | CallExpr : unit | CallExpr : unit | +| test.swift:19:10:19:35 | MemberAccessExpr | test.swift:19:19:19:32 | CallExpr : unit | test.swift:19:10:19:35 | MemberAccessExpr | $@ | test.swift:19:19:19:32 | CallExpr : unit | CallExpr : unit | +| test.swift:24:10:24:10 | a | test.swift:23:13:23:26 | CallExpr : unit | test.swift:24:10:24:10 | a | $@ | test.swift:23:13:23:26 | CallExpr : unit | CallExpr : unit | +| test.swift:29:10:29:10 | a | test.swift:28:19:28:33 | CallExpr : unit | test.swift:29:10:29:10 | a | $@ | test.swift:28:19:28:33 | CallExpr : unit | CallExpr : unit | +| test.swift:34:10:34:10 | d | test.swift:32:27:32:40 | CallExpr : unit | test.swift:34:10:34:10 | d | $@ | test.swift:32:27:32:40 | CallExpr : unit | CallExpr : unit | +| test.swift:39:10:39:10 | a | test.swift:38:13:38:26 | CallExpr : unit | test.swift:39:10:39:10 | a | $@ | test.swift:38:13:38:26 | CallExpr : unit | CallExpr : unit | +| test.swift:47:10:47:16 | MemberAccessExpr | test.swift:46:15:46:28 | CallExpr : unit | test.swift:47:10:47:16 | MemberAccessExpr | $@ | test.swift:46:15:46:28 | CallExpr : unit | CallExpr : unit | +| test.swift:58:10:58:23 | MemberAccessExpr | test.swift:53:22:53:35 | CallExpr : unit | test.swift:58:10:58:23 | MemberAccessExpr | $@ | test.swift:53:22:53:35 | CallExpr : unit | CallExpr : unit | +| test.swift:66:10:66:16 | MemberAccessExpr | test.swift:64:21:64:35 | CallExpr : unit | test.swift:66:10:66:16 | MemberAccessExpr | $@ | test.swift:64:21:64:35 | CallExpr : unit | CallExpr : unit | +| test.swift:75:10:75:16 | MemberAccessExpr | test.swift:74:15:74:29 | CallExpr : unit | test.swift:75:10:75:16 | MemberAccessExpr | $@ | test.swift:74:15:74:29 | CallExpr : unit | CallExpr : unit | +| test.swift:90:10:90:10 | x | test.swift:86:13:86:27 | CallExpr : unit | test.swift:90:10:90:10 | x | $@ | test.swift:86:13:86:27 | CallExpr : unit | CallExpr : unit | +| test.swift:96:10:96:10 | y | test.swift:94:13:94:27 | CallExpr : unit | test.swift:96:10:96:10 | y | $@ | test.swift:94:13:94:27 | CallExpr : unit | CallExpr : unit | +| test.swift:99:14:99:14 | x | test.swift:86:13:86:27 | CallExpr : unit | test.swift:99:14:99:14 | x | $@ | test.swift:86:13:86:27 | CallExpr : unit | CallExpr : unit | +| test.swift:100:14:100:14 | y | test.swift:94:13:94:27 | CallExpr : unit | test.swift:100:14:100:14 | y | $@ | test.swift:94:13:94:27 | CallExpr : unit | CallExpr : unit | +| test.swift:112:10:112:16 | MemberAccessExpr | test.swift:107:19:107:33 | CallExpr : unit | test.swift:112:10:112:16 | MemberAccessExpr | $@ | test.swift:107:19:107:33 | CallExpr : unit | CallExpr : unit | +| test.swift:125:10:125:10 | a | test.swift:117:18:117:33 | CallExpr : unit | test.swift:125:10:125:10 | a | $@ | test.swift:117:18:117:33 | CallExpr : unit | CallExpr : unit | +| test.swift:126:10:126:10 | b | test.swift:117:35:117:49 | CallExpr : unit | test.swift:126:10:126:10 | b | $@ | test.swift:117:35:117:49 | CallExpr : unit | CallExpr : unit | +| test.swift:131:14:131:14 | a | test.swift:131:19:131:33 | CallExpr : unit | test.swift:131:14:131:14 | a | $@ | test.swift:131:19:131:33 | CallExpr : unit | CallExpr : unit | +| test.swift:132:10:132:10 | a | test.swift:131:19:131:33 | CallExpr : unit | test.swift:132:10:132:10 | a | $@ | test.swift:131:19:131:33 | CallExpr : unit | CallExpr : unit | diff --git a/unified/ql/test/library-tests/dataflow/test.ql b/unified/ql/test/library-tests/dataflow/test.ql index ee97b8e524e9..eccf3ccfd79d 100644 --- a/unified/ql/test/library-tests/dataflow/test.ql +++ b/unified/ql/test/library-tests/dataflow/test.ql @@ -1,3 +1,15 @@ +/** + * @kind path-problem + * @id unified/test/library-tests/dataflow + * @severity info + * @precision low + */ + private import unified private import utils.test.InlineFlowTest import DefaultFlowTest +import TaintFlow::PathGraph + +from TaintFlow::PathNode source, TaintFlow::PathNode sink +where TaintFlow::flowPath(source, sink) +select sink, source, sink, "$@", source, source.toString() From 11bcf844b00b4d7ab668ca3b7415a4c275374001 Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 15 Sep 2026 13:52:31 +0200 Subject: [PATCH 29/30] unified: Omit "unit" type from path steps --- .../unified/internal/dataflow/DataFlowInstantiation.qll | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll index d4b03b4b59c4..da5602cddd29 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll @@ -95,7 +95,10 @@ module DataFlowInput implements InputSig { // // Types // - class DataFlowType = Unit; // TODO: track types + class DataFlowType extends Unit { + // TODO: track proper types + string toString() { result = "" } // do not include "unit" type in path steps + } class CastNode extends Node { CastNode() { none() } // TODO From 343459c713bd14881a5b227076a082eee8029f3c Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 15 Sep 2026 14:21:29 +0200 Subject: [PATCH 30/30] unified: Update expected output --- .../test/library-tests/dataflow/test.expected | 450 +++++++++--------- .../CleartextLogging.expected | 6 +- 2 files changed, 228 insertions(+), 228 deletions(-) diff --git a/unified/ql/test/library-tests/dataflow/test.expected b/unified/ql/test/library-tests/dataflow/test.expected index 20a3141892fb..f6df03922f1f 100644 --- a/unified/ql/test/library-tests/dataflow/test.expected +++ b/unified/ql/test/library-tests/dataflow/test.expected @@ -1,265 +1,265 @@ models edges -| implicit-self.swift:11:9:11:12 | [post] self : unit [x] : unit | implicit-self.swift:12:14:12:17 | self : unit [x] : unit | provenance | | -| implicit-self.swift:11:9:11:14 | MemberAccessExpr : unit | implicit-self.swift:11:9:11:12 | [post] self : unit [x] : unit | provenance | | -| implicit-self.swift:11:18:11:31 | CallExpr : unit | implicit-self.swift:11:9:11:14 | MemberAccessExpr : unit | provenance | | -| implicit-self.swift:12:14:12:17 | self : unit [x] : unit | implicit-self.swift:12:14:12:19 | MemberAccessExpr | provenance | | -| implicit-self.swift:17:9:17:9 | x : unit | implicit-self.swift:18:14:18:14 | x | provenance | | -| implicit-self.swift:17:13:17:26 | CallExpr : unit | implicit-self.swift:17:9:17:9 | x : unit | provenance | | -| implicit-self.swift:23:9:23:9 | x : unit | implicit-self.swift:24:14:24:17 | self : unit [x] : unit | provenance | | -| implicit-self.swift:23:13:23:26 | CallExpr : unit | implicit-self.swift:23:9:23:9 | x : unit | provenance | | -| implicit-self.swift:24:14:24:17 | self : unit [x] : unit | implicit-self.swift:24:14:24:19 | MemberAccessExpr | provenance | | -| implicit-self.swift:29:9:29:12 | [post] self : unit [x] : unit | implicit-self.swift:30:14:30:14 | x | provenance | | -| implicit-self.swift:29:9:29:14 | MemberAccessExpr : unit | implicit-self.swift:29:9:29:12 | [post] self : unit [x] : unit | provenance | | -| implicit-self.swift:29:18:29:31 | CallExpr : unit | implicit-self.swift:29:9:29:14 | MemberAccessExpr : unit | provenance | | -| implicit-self.swift:35:9:35:12 | [post] self : unit [box, x] : unit | implicit-self.swift:36:14:36:17 | self : unit [box, x] : unit | provenance | | -| implicit-self.swift:35:9:35:16 | [post] MemberAccessExpr : unit [x] : unit | implicit-self.swift:35:9:35:12 | [post] self : unit [box, x] : unit | provenance | | -| implicit-self.swift:35:9:35:18 | MemberAccessExpr : unit | implicit-self.swift:35:9:35:16 | [post] MemberAccessExpr : unit [x] : unit | provenance | | -| implicit-self.swift:35:22:35:35 | CallExpr : unit | implicit-self.swift:35:9:35:18 | MemberAccessExpr : unit | provenance | | -| implicit-self.swift:36:14:36:17 | self : unit [box, x] : unit | implicit-self.swift:36:14:36:21 | MemberAccessExpr : unit [x] : unit | provenance | | -| implicit-self.swift:36:14:36:21 | MemberAccessExpr : unit [x] : unit | implicit-self.swift:36:14:36:23 | MemberAccessExpr | provenance | | -| implicit-self.swift:41:9:41:11 | [post] box : unit [x] : unit | implicit-self.swift:42:14:42:16 | box : unit [x] : unit | provenance | | -| implicit-self.swift:41:9:41:13 | MemberAccessExpr : unit | implicit-self.swift:41:9:41:11 | [post] box : unit [x] : unit | provenance | | -| implicit-self.swift:41:17:41:30 | CallExpr : unit | implicit-self.swift:41:9:41:13 | MemberAccessExpr : unit | provenance | | -| implicit-self.swift:42:14:42:16 | box : unit [x] : unit | implicit-self.swift:42:14:42:18 | MemberAccessExpr | provenance | | -| implicit-self.swift:47:9:47:11 | [post] box : unit [x] : unit | implicit-self.swift:48:14:48:17 | self : unit [box, x] : unit | provenance | | -| implicit-self.swift:47:9:47:13 | MemberAccessExpr : unit | implicit-self.swift:47:9:47:11 | [post] box : unit [x] : unit | provenance | | -| implicit-self.swift:47:17:47:30 | CallExpr : unit | implicit-self.swift:47:9:47:13 | MemberAccessExpr : unit | provenance | | -| implicit-self.swift:48:14:48:17 | self : unit [box, x] : unit | implicit-self.swift:48:14:48:21 | MemberAccessExpr : unit [x] : unit | provenance | | -| implicit-self.swift:48:14:48:21 | MemberAccessExpr : unit [x] : unit | implicit-self.swift:48:14:48:23 | MemberAccessExpr | provenance | | -| implicit-self.swift:53:9:53:12 | [post] self : unit [box, x] : unit | implicit-self.swift:54:14:54:16 | box : unit [x] : unit | provenance | | -| implicit-self.swift:53:9:53:16 | [post] MemberAccessExpr : unit [x] : unit | implicit-self.swift:53:9:53:12 | [post] self : unit [box, x] : unit | provenance | | -| implicit-self.swift:53:9:53:18 | MemberAccessExpr : unit | implicit-self.swift:53:9:53:16 | [post] MemberAccessExpr : unit [x] : unit | provenance | | -| implicit-self.swift:53:22:53:35 | CallExpr : unit | implicit-self.swift:53:9:53:18 | MemberAccessExpr : unit | provenance | | -| implicit-self.swift:54:14:54:16 | box : unit [x] : unit | implicit-self.swift:54:14:54:18 | MemberAccessExpr | provenance | | -| test.swift:6:10:6:23 | CallExpr : unit | test.swift:6:10:6:32 | BinaryExpr | provenance | | -| test.swift:7:19:7:32 | CallExpr : unit | test.swift:7:10:7:32 | BinaryExpr | provenance | | -| test.swift:9:13:9:26 | CallExpr : unit | test.swift:9:10:9:33 | StringInterpolationExpr | provenance | | -| test.swift:10:18:10:31 | CallExpr : unit | test.swift:10:10:10:33 | StringInterpolationExpr | provenance | | -| test.swift:11:18:11:31 | CallExpr : unit | test.swift:11:10:11:38 | StringInterpolationExpr | provenance | | -| test.swift:16:10:16:33 | TupleExpr : unit [0] : unit | test.swift:16:10:16:35 | MemberAccessExpr | provenance | | -| test.swift:16:11:16:25 | CallExpr : unit | test.swift:16:10:16:33 | TupleExpr : unit [0] : unit | provenance | | -| test.swift:19:10:19:33 | TupleExpr : unit [1] : unit | test.swift:19:10:19:35 | MemberAccessExpr | provenance | | -| test.swift:19:19:19:32 | CallExpr : unit | test.swift:19:10:19:33 | TupleExpr : unit [1] : unit | provenance | | -| test.swift:23:9:23:9 | a : unit | test.swift:24:10:24:10 | a | provenance | | -| test.swift:23:13:23:26 | CallExpr : unit | test.swift:23:9:23:9 | a : unit | provenance | | -| test.swift:28:9:28:14 | TupleExpr : unit [0] : unit | test.swift:28:10:28:10 | a : unit | provenance | | -| test.swift:28:10:28:10 | a : unit | test.swift:29:10:29:10 | a | provenance | | -| test.swift:28:18:28:41 | TupleExpr : unit [0] : unit | test.swift:28:9:28:14 | TupleExpr : unit [0] : unit | provenance | | -| test.swift:28:19:28:33 | CallExpr : unit | test.swift:28:18:28:41 | TupleExpr : unit [0] : unit | provenance | | -| test.swift:32:9:32:14 | TupleExpr : unit [1] : unit | test.swift:32:13:32:13 | d : unit | provenance | | -| test.swift:32:13:32:13 | d : unit | test.swift:34:10:34:10 | d | provenance | | -| test.swift:32:18:32:41 | TupleExpr : unit [1] : unit | test.swift:32:9:32:14 | TupleExpr : unit [1] : unit | provenance | | -| test.swift:32:27:32:40 | CallExpr : unit | test.swift:32:18:32:41 | TupleExpr : unit [1] : unit | provenance | | -| test.swift:38:9:38:9 | a : unit | test.swift:39:10:39:10 | a | provenance | | -| test.swift:38:13:38:26 | CallExpr : unit | test.swift:38:9:38:9 | a : unit | provenance | | -| test.swift:46:5:46:9 | [post] tuple : unit [0] : unit | test.swift:47:10:47:14 | tuple : unit [0] : unit | provenance | | -| test.swift:46:5:46:11 | MemberAccessExpr : unit | test.swift:46:5:46:9 | [post] tuple : unit [0] : unit | provenance | | -| test.swift:46:15:46:28 | CallExpr : unit | test.swift:46:5:46:11 | MemberAccessExpr : unit | provenance | | -| test.swift:47:10:47:14 | tuple : unit [0] : unit | test.swift:47:10:47:16 | MemberAccessExpr | provenance | | -| test.swift:53:5:53:14 | [post] deep_tuple : unit [1, 0] : unit | test.swift:58:10:58:19 | deep_tuple : unit [1, 0] : unit | provenance | | -| test.swift:53:5:53:16 | [post] MemberAccessExpr : unit [0] : unit | test.swift:53:5:53:14 | [post] deep_tuple : unit [1, 0] : unit | provenance | | -| test.swift:53:5:53:18 | MemberAccessExpr : unit | test.swift:53:5:53:16 | [post] MemberAccessExpr : unit [0] : unit | provenance | | -| test.swift:53:22:53:35 | CallExpr : unit | test.swift:53:5:53:18 | MemberAccessExpr : unit | provenance | | -| test.swift:58:10:58:19 | deep_tuple : unit [1, 0] : unit | test.swift:58:10:58:21 | MemberAccessExpr : unit [0] : unit | provenance | | -| test.swift:58:10:58:21 | MemberAccessExpr : unit [0] : unit | test.swift:58:10:58:23 | MemberAccessExpr | provenance | | -| test.swift:64:5:64:16 | TupleExpr : unit [0] : unit | test.swift:64:6:64:12 | MemberAccessExpr : unit | provenance | | -| test.swift:64:6:64:10 | [post] tuple : unit [1] : unit | test.swift:66:10:66:14 | tuple : unit [1] : unit | provenance | | -| test.swift:64:6:64:12 | MemberAccessExpr : unit | test.swift:64:6:64:10 | [post] tuple : unit [1] : unit | provenance | | -| test.swift:64:20:64:51 | TupleExpr : unit [0] : unit | test.swift:64:5:64:16 | TupleExpr : unit [0] : unit | provenance | | -| test.swift:64:21:64:35 | CallExpr : unit | test.swift:64:20:64:51 | TupleExpr : unit [0] : unit | provenance | | -| test.swift:66:10:66:14 | tuple : unit [1] : unit | test.swift:66:10:66:16 | MemberAccessExpr | provenance | | -| test.swift:74:5:74:9 | [post] tuple : unit [0] : unit | test.swift:75:10:75:14 | tuple : unit [0] : unit | provenance | | -| test.swift:74:5:74:11 | MemberAccessExpr : unit | test.swift:74:5:74:9 | [post] tuple : unit [0] : unit | provenance | | -| test.swift:74:15:74:29 | CallExpr : unit | test.swift:74:5:74:11 | MemberAccessExpr : unit | provenance | | -| test.swift:75:10:75:14 | tuple : unit [0] : unit | test.swift:75:10:75:16 | MemberAccessExpr | provenance | | -| test.swift:86:9:86:9 | x : unit | test.swift:90:10:90:10 | x | provenance | | -| test.swift:86:9:86:9 | x : unit | test.swift:99:14:99:14 | x | provenance | | -| test.swift:86:13:86:27 | CallExpr : unit | test.swift:86:9:86:9 | x : unit | provenance | | -| test.swift:94:9:94:9 | y : unit | test.swift:96:10:96:10 | y | provenance | | -| test.swift:94:9:94:9 | y : unit | test.swift:100:14:100:14 | y | provenance | | -| test.swift:94:13:94:27 | CallExpr : unit | test.swift:94:9:94:9 | y : unit | provenance | | -| test.swift:107:9:107:13 | [post] tuple : unit [0] : unit | test.swift:112:10:112:14 | tuple : unit [0] : unit | provenance | | -| test.swift:107:9:107:15 | MemberAccessExpr : unit | test.swift:107:9:107:13 | [post] tuple : unit [0] : unit | provenance | | -| test.swift:107:19:107:33 | CallExpr : unit | test.swift:107:9:107:15 | MemberAccessExpr : unit | provenance | | -| test.swift:112:10:112:14 | tuple : unit [0] : unit | test.swift:112:10:112:16 | MemberAccessExpr | provenance | | -| test.swift:117:9:117:13 | tuple : unit [0] : unit | test.swift:120:17:120:21 | tuple : unit [0] : unit | provenance | | -| test.swift:117:9:117:13 | tuple : unit [1] : unit | test.swift:120:17:120:21 | tuple : unit [1] : unit | provenance | | -| test.swift:117:17:117:50 | TupleExpr : unit [0] : unit | test.swift:117:9:117:13 | tuple : unit [0] : unit | provenance | | -| test.swift:117:17:117:50 | TupleExpr : unit [1] : unit | test.swift:117:9:117:13 | tuple : unit [1] : unit | provenance | | -| test.swift:117:18:117:33 | CallExpr : unit | test.swift:117:17:117:50 | TupleExpr : unit [0] : unit | provenance | | -| test.swift:117:35:117:49 | CallExpr : unit | test.swift:117:17:117:50 | TupleExpr : unit [1] : unit | provenance | | -| test.swift:120:9:120:13 | TupleExpr : unit [0] : unit | test.swift:120:10:120:10 | a : unit | provenance | | -| test.swift:120:9:120:13 | TupleExpr : unit [1] : unit | test.swift:120:12:120:12 | b : unit | provenance | | -| test.swift:120:10:120:10 | a : unit | test.swift:125:10:125:10 | a | provenance | | -| test.swift:120:12:120:12 | b : unit | test.swift:126:10:126:10 | b | provenance | | -| test.swift:120:17:120:21 | tuple : unit [0] : unit | test.swift:120:9:120:13 | TupleExpr : unit [0] : unit | provenance | | -| test.swift:120:17:120:21 | tuple : unit [1] : unit | test.swift:120:9:120:13 | TupleExpr : unit [1] : unit | provenance | | -| test.swift:131:5:131:5 | a : unit | test.swift:131:14:131:14 | a | provenance | | -| test.swift:131:5:131:5 | a : unit | test.swift:132:10:132:10 | a | provenance | | -| test.swift:131:19:131:33 | CallExpr : unit | test.swift:131:5:131:5 | a : unit | provenance | | +| implicit-self.swift:11:9:11:12 | [post] self [x] | implicit-self.swift:12:14:12:17 | self [x] | provenance | | +| implicit-self.swift:11:9:11:14 | MemberAccessExpr | implicit-self.swift:11:9:11:12 | [post] self [x] | provenance | | +| implicit-self.swift:11:18:11:31 | CallExpr | implicit-self.swift:11:9:11:14 | MemberAccessExpr | provenance | | +| implicit-self.swift:12:14:12:17 | self [x] | implicit-self.swift:12:14:12:19 | MemberAccessExpr | provenance | | +| implicit-self.swift:17:9:17:9 | x | implicit-self.swift:18:14:18:14 | x | provenance | | +| implicit-self.swift:17:13:17:26 | CallExpr | implicit-self.swift:17:9:17:9 | x | provenance | | +| implicit-self.swift:23:9:23:9 | x | implicit-self.swift:24:14:24:17 | self [x] | provenance | | +| implicit-self.swift:23:13:23:26 | CallExpr | implicit-self.swift:23:9:23:9 | x | provenance | | +| implicit-self.swift:24:14:24:17 | self [x] | implicit-self.swift:24:14:24:19 | MemberAccessExpr | provenance | | +| implicit-self.swift:29:9:29:12 | [post] self [x] | implicit-self.swift:30:14:30:14 | x | provenance | | +| implicit-self.swift:29:9:29:14 | MemberAccessExpr | implicit-self.swift:29:9:29:12 | [post] self [x] | provenance | | +| implicit-self.swift:29:18:29:31 | CallExpr | implicit-self.swift:29:9:29:14 | MemberAccessExpr | provenance | | +| implicit-self.swift:35:9:35:12 | [post] self [box, x] | implicit-self.swift:36:14:36:17 | self [box, x] | provenance | | +| implicit-self.swift:35:9:35:16 | [post] MemberAccessExpr [x] | implicit-self.swift:35:9:35:12 | [post] self [box, x] | provenance | | +| implicit-self.swift:35:9:35:18 | MemberAccessExpr | implicit-self.swift:35:9:35:16 | [post] MemberAccessExpr [x] | provenance | | +| implicit-self.swift:35:22:35:35 | CallExpr | implicit-self.swift:35:9:35:18 | MemberAccessExpr | provenance | | +| implicit-self.swift:36:14:36:17 | self [box, x] | implicit-self.swift:36:14:36:21 | MemberAccessExpr [x] | provenance | | +| implicit-self.swift:36:14:36:21 | MemberAccessExpr [x] | implicit-self.swift:36:14:36:23 | MemberAccessExpr | provenance | | +| implicit-self.swift:41:9:41:11 | [post] box [x] | implicit-self.swift:42:14:42:16 | box [x] | provenance | | +| implicit-self.swift:41:9:41:13 | MemberAccessExpr | implicit-self.swift:41:9:41:11 | [post] box [x] | provenance | | +| implicit-self.swift:41:17:41:30 | CallExpr | implicit-self.swift:41:9:41:13 | MemberAccessExpr | provenance | | +| implicit-self.swift:42:14:42:16 | box [x] | implicit-self.swift:42:14:42:18 | MemberAccessExpr | provenance | | +| implicit-self.swift:47:9:47:11 | [post] box [x] | implicit-self.swift:48:14:48:17 | self [box, x] | provenance | | +| implicit-self.swift:47:9:47:13 | MemberAccessExpr | implicit-self.swift:47:9:47:11 | [post] box [x] | provenance | | +| implicit-self.swift:47:17:47:30 | CallExpr | implicit-self.swift:47:9:47:13 | MemberAccessExpr | provenance | | +| implicit-self.swift:48:14:48:17 | self [box, x] | implicit-self.swift:48:14:48:21 | MemberAccessExpr [x] | provenance | | +| implicit-self.swift:48:14:48:21 | MemberAccessExpr [x] | implicit-self.swift:48:14:48:23 | MemberAccessExpr | provenance | | +| implicit-self.swift:53:9:53:12 | [post] self [box, x] | implicit-self.swift:54:14:54:16 | box [x] | provenance | | +| implicit-self.swift:53:9:53:16 | [post] MemberAccessExpr [x] | implicit-self.swift:53:9:53:12 | [post] self [box, x] | provenance | | +| implicit-self.swift:53:9:53:18 | MemberAccessExpr | implicit-self.swift:53:9:53:16 | [post] MemberAccessExpr [x] | provenance | | +| implicit-self.swift:53:22:53:35 | CallExpr | implicit-self.swift:53:9:53:18 | MemberAccessExpr | provenance | | +| implicit-self.swift:54:14:54:16 | box [x] | implicit-self.swift:54:14:54:18 | MemberAccessExpr | provenance | | +| test.swift:6:10:6:23 | CallExpr | test.swift:6:10:6:32 | BinaryExpr | provenance | | +| test.swift:7:19:7:32 | CallExpr | test.swift:7:10:7:32 | BinaryExpr | provenance | | +| test.swift:9:13:9:26 | CallExpr | test.swift:9:10:9:33 | StringInterpolationExpr | provenance | | +| test.swift:10:18:10:31 | CallExpr | test.swift:10:10:10:33 | StringInterpolationExpr | provenance | | +| test.swift:11:18:11:31 | CallExpr | test.swift:11:10:11:38 | StringInterpolationExpr | provenance | | +| test.swift:16:10:16:33 | TupleExpr [0] | test.swift:16:10:16:35 | MemberAccessExpr | provenance | | +| test.swift:16:11:16:25 | CallExpr | test.swift:16:10:16:33 | TupleExpr [0] | provenance | | +| test.swift:19:10:19:33 | TupleExpr [1] | test.swift:19:10:19:35 | MemberAccessExpr | provenance | | +| test.swift:19:19:19:32 | CallExpr | test.swift:19:10:19:33 | TupleExpr [1] | provenance | | +| test.swift:23:9:23:9 | a | test.swift:24:10:24:10 | a | provenance | | +| test.swift:23:13:23:26 | CallExpr | test.swift:23:9:23:9 | a | provenance | | +| test.swift:28:9:28:14 | TupleExpr [0] | test.swift:28:10:28:10 | a | provenance | | +| test.swift:28:10:28:10 | a | test.swift:29:10:29:10 | a | provenance | | +| test.swift:28:18:28:41 | TupleExpr [0] | test.swift:28:9:28:14 | TupleExpr [0] | provenance | | +| test.swift:28:19:28:33 | CallExpr | test.swift:28:18:28:41 | TupleExpr [0] | provenance | | +| test.swift:32:9:32:14 | TupleExpr [1] | test.swift:32:13:32:13 | d | provenance | | +| test.swift:32:13:32:13 | d | test.swift:34:10:34:10 | d | provenance | | +| test.swift:32:18:32:41 | TupleExpr [1] | test.swift:32:9:32:14 | TupleExpr [1] | provenance | | +| test.swift:32:27:32:40 | CallExpr | test.swift:32:18:32:41 | TupleExpr [1] | provenance | | +| test.swift:38:9:38:9 | a | test.swift:39:10:39:10 | a | provenance | | +| test.swift:38:13:38:26 | CallExpr | test.swift:38:9:38:9 | a | provenance | | +| test.swift:46:5:46:9 | [post] tuple [0] | test.swift:47:10:47:14 | tuple [0] | provenance | | +| test.swift:46:5:46:11 | MemberAccessExpr | test.swift:46:5:46:9 | [post] tuple [0] | provenance | | +| test.swift:46:15:46:28 | CallExpr | test.swift:46:5:46:11 | MemberAccessExpr | provenance | | +| test.swift:47:10:47:14 | tuple [0] | test.swift:47:10:47:16 | MemberAccessExpr | provenance | | +| test.swift:53:5:53:14 | [post] deep_tuple [1, 0] | test.swift:58:10:58:19 | deep_tuple [1, 0] | provenance | | +| test.swift:53:5:53:16 | [post] MemberAccessExpr [0] | test.swift:53:5:53:14 | [post] deep_tuple [1, 0] | provenance | | +| test.swift:53:5:53:18 | MemberAccessExpr | test.swift:53:5:53:16 | [post] MemberAccessExpr [0] | provenance | | +| test.swift:53:22:53:35 | CallExpr | test.swift:53:5:53:18 | MemberAccessExpr | provenance | | +| test.swift:58:10:58:19 | deep_tuple [1, 0] | test.swift:58:10:58:21 | MemberAccessExpr [0] | provenance | | +| test.swift:58:10:58:21 | MemberAccessExpr [0] | test.swift:58:10:58:23 | MemberAccessExpr | provenance | | +| test.swift:64:5:64:16 | TupleExpr [0] | test.swift:64:6:64:12 | MemberAccessExpr | provenance | | +| test.swift:64:6:64:10 | [post] tuple [1] | test.swift:66:10:66:14 | tuple [1] | provenance | | +| test.swift:64:6:64:12 | MemberAccessExpr | test.swift:64:6:64:10 | [post] tuple [1] | provenance | | +| test.swift:64:20:64:51 | TupleExpr [0] | test.swift:64:5:64:16 | TupleExpr [0] | provenance | | +| test.swift:64:21:64:35 | CallExpr | test.swift:64:20:64:51 | TupleExpr [0] | provenance | | +| test.swift:66:10:66:14 | tuple [1] | test.swift:66:10:66:16 | MemberAccessExpr | provenance | | +| test.swift:74:5:74:9 | [post] tuple [0] | test.swift:75:10:75:14 | tuple [0] | provenance | | +| test.swift:74:5:74:11 | MemberAccessExpr | test.swift:74:5:74:9 | [post] tuple [0] | provenance | | +| test.swift:74:15:74:29 | CallExpr | test.swift:74:5:74:11 | MemberAccessExpr | provenance | | +| test.swift:75:10:75:14 | tuple [0] | test.swift:75:10:75:16 | MemberAccessExpr | provenance | | +| test.swift:86:9:86:9 | x | test.swift:90:10:90:10 | x | provenance | | +| test.swift:86:9:86:9 | x | test.swift:99:14:99:14 | x | provenance | | +| test.swift:86:13:86:27 | CallExpr | test.swift:86:9:86:9 | x | provenance | | +| test.swift:94:9:94:9 | y | test.swift:96:10:96:10 | y | provenance | | +| test.swift:94:9:94:9 | y | test.swift:100:14:100:14 | y | provenance | | +| test.swift:94:13:94:27 | CallExpr | test.swift:94:9:94:9 | y | provenance | | +| test.swift:107:9:107:13 | [post] tuple [0] | test.swift:112:10:112:14 | tuple [0] | provenance | | +| test.swift:107:9:107:15 | MemberAccessExpr | test.swift:107:9:107:13 | [post] tuple [0] | provenance | | +| test.swift:107:19:107:33 | CallExpr | test.swift:107:9:107:15 | MemberAccessExpr | provenance | | +| test.swift:112:10:112:14 | tuple [0] | test.swift:112:10:112:16 | MemberAccessExpr | provenance | | +| test.swift:117:9:117:13 | tuple [0] | test.swift:120:17:120:21 | tuple [0] | provenance | | +| test.swift:117:9:117:13 | tuple [1] | test.swift:120:17:120:21 | tuple [1] | provenance | | +| test.swift:117:17:117:50 | TupleExpr [0] | test.swift:117:9:117:13 | tuple [0] | provenance | | +| test.swift:117:17:117:50 | TupleExpr [1] | test.swift:117:9:117:13 | tuple [1] | provenance | | +| test.swift:117:18:117:33 | CallExpr | test.swift:117:17:117:50 | TupleExpr [0] | provenance | | +| test.swift:117:35:117:49 | CallExpr | test.swift:117:17:117:50 | TupleExpr [1] | provenance | | +| test.swift:120:9:120:13 | TupleExpr [0] | test.swift:120:10:120:10 | a | provenance | | +| test.swift:120:9:120:13 | TupleExpr [1] | test.swift:120:12:120:12 | b | provenance | | +| test.swift:120:10:120:10 | a | test.swift:125:10:125:10 | a | provenance | | +| test.swift:120:12:120:12 | b | test.swift:126:10:126:10 | b | provenance | | +| test.swift:120:17:120:21 | tuple [0] | test.swift:120:9:120:13 | TupleExpr [0] | provenance | | +| test.swift:120:17:120:21 | tuple [1] | test.swift:120:9:120:13 | TupleExpr [1] | provenance | | +| test.swift:131:5:131:5 | a | test.swift:131:14:131:14 | a | provenance | | +| test.swift:131:5:131:5 | a | test.swift:132:10:132:10 | a | provenance | | +| test.swift:131:19:131:33 | CallExpr | test.swift:131:5:131:5 | a | provenance | | nodes -| implicit-self.swift:11:9:11:12 | [post] self : unit [x] : unit | semmle.label | [post] self : unit [x] : unit | -| implicit-self.swift:11:9:11:14 | MemberAccessExpr : unit | semmle.label | MemberAccessExpr : unit | -| implicit-self.swift:11:18:11:31 | CallExpr : unit | semmle.label | CallExpr : unit | -| implicit-self.swift:12:14:12:17 | self : unit [x] : unit | semmle.label | self : unit [x] : unit | +| implicit-self.swift:11:9:11:12 | [post] self [x] | semmle.label | [post] self [x] | +| implicit-self.swift:11:9:11:14 | MemberAccessExpr | semmle.label | MemberAccessExpr | +| implicit-self.swift:11:18:11:31 | CallExpr | semmle.label | CallExpr | +| implicit-self.swift:12:14:12:17 | self [x] | semmle.label | self [x] | | implicit-self.swift:12:14:12:19 | MemberAccessExpr | semmle.label | MemberAccessExpr | -| implicit-self.swift:17:9:17:9 | x : unit | semmle.label | x : unit | -| implicit-self.swift:17:13:17:26 | CallExpr : unit | semmle.label | CallExpr : unit | +| implicit-self.swift:17:9:17:9 | x | semmle.label | x | +| implicit-self.swift:17:13:17:26 | CallExpr | semmle.label | CallExpr | | implicit-self.swift:18:14:18:14 | x | semmle.label | x | -| implicit-self.swift:23:9:23:9 | x : unit | semmle.label | x : unit | -| implicit-self.swift:23:13:23:26 | CallExpr : unit | semmle.label | CallExpr : unit | -| implicit-self.swift:24:14:24:17 | self : unit [x] : unit | semmle.label | self : unit [x] : unit | +| implicit-self.swift:23:9:23:9 | x | semmle.label | x | +| implicit-self.swift:23:13:23:26 | CallExpr | semmle.label | CallExpr | +| implicit-self.swift:24:14:24:17 | self [x] | semmle.label | self [x] | | implicit-self.swift:24:14:24:19 | MemberAccessExpr | semmle.label | MemberAccessExpr | -| implicit-self.swift:29:9:29:12 | [post] self : unit [x] : unit | semmle.label | [post] self : unit [x] : unit | -| implicit-self.swift:29:9:29:14 | MemberAccessExpr : unit | semmle.label | MemberAccessExpr : unit | -| implicit-self.swift:29:18:29:31 | CallExpr : unit | semmle.label | CallExpr : unit | +| implicit-self.swift:29:9:29:12 | [post] self [x] | semmle.label | [post] self [x] | +| implicit-self.swift:29:9:29:14 | MemberAccessExpr | semmle.label | MemberAccessExpr | +| implicit-self.swift:29:18:29:31 | CallExpr | semmle.label | CallExpr | | implicit-self.swift:30:14:30:14 | x | semmle.label | x | -| implicit-self.swift:35:9:35:12 | [post] self : unit [box, x] : unit | semmle.label | [post] self : unit [box, x] : unit | -| implicit-self.swift:35:9:35:16 | [post] MemberAccessExpr : unit [x] : unit | semmle.label | [post] MemberAccessExpr : unit [x] : unit | -| implicit-self.swift:35:9:35:18 | MemberAccessExpr : unit | semmle.label | MemberAccessExpr : unit | -| implicit-self.swift:35:22:35:35 | CallExpr : unit | semmle.label | CallExpr : unit | -| implicit-self.swift:36:14:36:17 | self : unit [box, x] : unit | semmle.label | self : unit [box, x] : unit | -| implicit-self.swift:36:14:36:21 | MemberAccessExpr : unit [x] : unit | semmle.label | MemberAccessExpr : unit [x] : unit | +| implicit-self.swift:35:9:35:12 | [post] self [box, x] | semmle.label | [post] self [box, x] | +| implicit-self.swift:35:9:35:16 | [post] MemberAccessExpr [x] | semmle.label | [post] MemberAccessExpr [x] | +| implicit-self.swift:35:9:35:18 | MemberAccessExpr | semmle.label | MemberAccessExpr | +| implicit-self.swift:35:22:35:35 | CallExpr | semmle.label | CallExpr | +| implicit-self.swift:36:14:36:17 | self [box, x] | semmle.label | self [box, x] | +| implicit-self.swift:36:14:36:21 | MemberAccessExpr [x] | semmle.label | MemberAccessExpr [x] | | implicit-self.swift:36:14:36:23 | MemberAccessExpr | semmle.label | MemberAccessExpr | -| implicit-self.swift:41:9:41:11 | [post] box : unit [x] : unit | semmle.label | [post] box : unit [x] : unit | -| implicit-self.swift:41:9:41:13 | MemberAccessExpr : unit | semmle.label | MemberAccessExpr : unit | -| implicit-self.swift:41:17:41:30 | CallExpr : unit | semmle.label | CallExpr : unit | -| implicit-self.swift:42:14:42:16 | box : unit [x] : unit | semmle.label | box : unit [x] : unit | +| implicit-self.swift:41:9:41:11 | [post] box [x] | semmle.label | [post] box [x] | +| implicit-self.swift:41:9:41:13 | MemberAccessExpr | semmle.label | MemberAccessExpr | +| implicit-self.swift:41:17:41:30 | CallExpr | semmle.label | CallExpr | +| implicit-self.swift:42:14:42:16 | box [x] | semmle.label | box [x] | | implicit-self.swift:42:14:42:18 | MemberAccessExpr | semmle.label | MemberAccessExpr | -| implicit-self.swift:47:9:47:11 | [post] box : unit [x] : unit | semmle.label | [post] box : unit [x] : unit | -| implicit-self.swift:47:9:47:13 | MemberAccessExpr : unit | semmle.label | MemberAccessExpr : unit | -| implicit-self.swift:47:17:47:30 | CallExpr : unit | semmle.label | CallExpr : unit | -| implicit-self.swift:48:14:48:17 | self : unit [box, x] : unit | semmle.label | self : unit [box, x] : unit | -| implicit-self.swift:48:14:48:21 | MemberAccessExpr : unit [x] : unit | semmle.label | MemberAccessExpr : unit [x] : unit | +| implicit-self.swift:47:9:47:11 | [post] box [x] | semmle.label | [post] box [x] | +| implicit-self.swift:47:9:47:13 | MemberAccessExpr | semmle.label | MemberAccessExpr | +| implicit-self.swift:47:17:47:30 | CallExpr | semmle.label | CallExpr | +| implicit-self.swift:48:14:48:17 | self [box, x] | semmle.label | self [box, x] | +| implicit-self.swift:48:14:48:21 | MemberAccessExpr [x] | semmle.label | MemberAccessExpr [x] | | implicit-self.swift:48:14:48:23 | MemberAccessExpr | semmle.label | MemberAccessExpr | -| implicit-self.swift:53:9:53:12 | [post] self : unit [box, x] : unit | semmle.label | [post] self : unit [box, x] : unit | -| implicit-self.swift:53:9:53:16 | [post] MemberAccessExpr : unit [x] : unit | semmle.label | [post] MemberAccessExpr : unit [x] : unit | -| implicit-self.swift:53:9:53:18 | MemberAccessExpr : unit | semmle.label | MemberAccessExpr : unit | -| implicit-self.swift:53:22:53:35 | CallExpr : unit | semmle.label | CallExpr : unit | -| implicit-self.swift:54:14:54:16 | box : unit [x] : unit | semmle.label | box : unit [x] : unit | +| implicit-self.swift:53:9:53:12 | [post] self [box, x] | semmle.label | [post] self [box, x] | +| implicit-self.swift:53:9:53:16 | [post] MemberAccessExpr [x] | semmle.label | [post] MemberAccessExpr [x] | +| implicit-self.swift:53:9:53:18 | MemberAccessExpr | semmle.label | MemberAccessExpr | +| implicit-self.swift:53:22:53:35 | CallExpr | semmle.label | CallExpr | +| implicit-self.swift:54:14:54:16 | box [x] | semmle.label | box [x] | | implicit-self.swift:54:14:54:18 | MemberAccessExpr | semmle.label | MemberAccessExpr | | test.swift:2:10:2:21 | CallExpr | semmle.label | CallExpr | -| test.swift:6:10:6:23 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:6:10:6:23 | CallExpr | semmle.label | CallExpr | | test.swift:6:10:6:32 | BinaryExpr | semmle.label | BinaryExpr | | test.swift:7:10:7:32 | BinaryExpr | semmle.label | BinaryExpr | -| test.swift:7:19:7:32 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:7:19:7:32 | CallExpr | semmle.label | CallExpr | | test.swift:9:10:9:33 | StringInterpolationExpr | semmle.label | StringInterpolationExpr | -| test.swift:9:13:9:26 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:9:13:9:26 | CallExpr | semmle.label | CallExpr | | test.swift:10:10:10:33 | StringInterpolationExpr | semmle.label | StringInterpolationExpr | -| test.swift:10:18:10:31 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:10:18:10:31 | CallExpr | semmle.label | CallExpr | | test.swift:11:10:11:38 | StringInterpolationExpr | semmle.label | StringInterpolationExpr | -| test.swift:11:18:11:31 | CallExpr : unit | semmle.label | CallExpr : unit | -| test.swift:16:10:16:33 | TupleExpr : unit [0] : unit | semmle.label | TupleExpr : unit [0] : unit | +| test.swift:11:18:11:31 | CallExpr | semmle.label | CallExpr | +| test.swift:16:10:16:33 | TupleExpr [0] | semmle.label | TupleExpr [0] | | test.swift:16:10:16:35 | MemberAccessExpr | semmle.label | MemberAccessExpr | -| test.swift:16:11:16:25 | CallExpr : unit | semmle.label | CallExpr : unit | -| test.swift:19:10:19:33 | TupleExpr : unit [1] : unit | semmle.label | TupleExpr : unit [1] : unit | +| test.swift:16:11:16:25 | CallExpr | semmle.label | CallExpr | +| test.swift:19:10:19:33 | TupleExpr [1] | semmle.label | TupleExpr [1] | | test.swift:19:10:19:35 | MemberAccessExpr | semmle.label | MemberAccessExpr | -| test.swift:19:19:19:32 | CallExpr : unit | semmle.label | CallExpr : unit | -| test.swift:23:9:23:9 | a : unit | semmle.label | a : unit | -| test.swift:23:13:23:26 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:19:19:19:32 | CallExpr | semmle.label | CallExpr | +| test.swift:23:9:23:9 | a | semmle.label | a | +| test.swift:23:13:23:26 | CallExpr | semmle.label | CallExpr | | test.swift:24:10:24:10 | a | semmle.label | a | -| test.swift:28:9:28:14 | TupleExpr : unit [0] : unit | semmle.label | TupleExpr : unit [0] : unit | -| test.swift:28:10:28:10 | a : unit | semmle.label | a : unit | -| test.swift:28:18:28:41 | TupleExpr : unit [0] : unit | semmle.label | TupleExpr : unit [0] : unit | -| test.swift:28:19:28:33 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:28:9:28:14 | TupleExpr [0] | semmle.label | TupleExpr [0] | +| test.swift:28:10:28:10 | a | semmle.label | a | +| test.swift:28:18:28:41 | TupleExpr [0] | semmle.label | TupleExpr [0] | +| test.swift:28:19:28:33 | CallExpr | semmle.label | CallExpr | | test.swift:29:10:29:10 | a | semmle.label | a | -| test.swift:32:9:32:14 | TupleExpr : unit [1] : unit | semmle.label | TupleExpr : unit [1] : unit | -| test.swift:32:13:32:13 | d : unit | semmle.label | d : unit | -| test.swift:32:18:32:41 | TupleExpr : unit [1] : unit | semmle.label | TupleExpr : unit [1] : unit | -| test.swift:32:27:32:40 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:32:9:32:14 | TupleExpr [1] | semmle.label | TupleExpr [1] | +| test.swift:32:13:32:13 | d | semmle.label | d | +| test.swift:32:18:32:41 | TupleExpr [1] | semmle.label | TupleExpr [1] | +| test.swift:32:27:32:40 | CallExpr | semmle.label | CallExpr | | test.swift:34:10:34:10 | d | semmle.label | d | -| test.swift:38:9:38:9 | a : unit | semmle.label | a : unit | -| test.swift:38:13:38:26 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:38:9:38:9 | a | semmle.label | a | +| test.swift:38:13:38:26 | CallExpr | semmle.label | CallExpr | | test.swift:39:10:39:10 | a | semmle.label | a | -| test.swift:46:5:46:9 | [post] tuple : unit [0] : unit | semmle.label | [post] tuple : unit [0] : unit | -| test.swift:46:5:46:11 | MemberAccessExpr : unit | semmle.label | MemberAccessExpr : unit | -| test.swift:46:15:46:28 | CallExpr : unit | semmle.label | CallExpr : unit | -| test.swift:47:10:47:14 | tuple : unit [0] : unit | semmle.label | tuple : unit [0] : unit | +| test.swift:46:5:46:9 | [post] tuple [0] | semmle.label | [post] tuple [0] | +| test.swift:46:5:46:11 | MemberAccessExpr | semmle.label | MemberAccessExpr | +| test.swift:46:15:46:28 | CallExpr | semmle.label | CallExpr | +| test.swift:47:10:47:14 | tuple [0] | semmle.label | tuple [0] | | test.swift:47:10:47:16 | MemberAccessExpr | semmle.label | MemberAccessExpr | -| test.swift:53:5:53:14 | [post] deep_tuple : unit [1, 0] : unit | semmle.label | [post] deep_tuple : unit [1, 0] : unit | -| test.swift:53:5:53:16 | [post] MemberAccessExpr : unit [0] : unit | semmle.label | [post] MemberAccessExpr : unit [0] : unit | -| test.swift:53:5:53:18 | MemberAccessExpr : unit | semmle.label | MemberAccessExpr : unit | -| test.swift:53:22:53:35 | CallExpr : unit | semmle.label | CallExpr : unit | -| test.swift:58:10:58:19 | deep_tuple : unit [1, 0] : unit | semmle.label | deep_tuple : unit [1, 0] : unit | -| test.swift:58:10:58:21 | MemberAccessExpr : unit [0] : unit | semmle.label | MemberAccessExpr : unit [0] : unit | +| test.swift:53:5:53:14 | [post] deep_tuple [1, 0] | semmle.label | [post] deep_tuple [1, 0] | +| test.swift:53:5:53:16 | [post] MemberAccessExpr [0] | semmle.label | [post] MemberAccessExpr [0] | +| test.swift:53:5:53:18 | MemberAccessExpr | semmle.label | MemberAccessExpr | +| test.swift:53:22:53:35 | CallExpr | semmle.label | CallExpr | +| test.swift:58:10:58:19 | deep_tuple [1, 0] | semmle.label | deep_tuple [1, 0] | +| test.swift:58:10:58:21 | MemberAccessExpr [0] | semmle.label | MemberAccessExpr [0] | | test.swift:58:10:58:23 | MemberAccessExpr | semmle.label | MemberAccessExpr | -| test.swift:64:5:64:16 | TupleExpr : unit [0] : unit | semmle.label | TupleExpr : unit [0] : unit | -| test.swift:64:6:64:10 | [post] tuple : unit [1] : unit | semmle.label | [post] tuple : unit [1] : unit | -| test.swift:64:6:64:12 | MemberAccessExpr : unit | semmle.label | MemberAccessExpr : unit | -| test.swift:64:20:64:51 | TupleExpr : unit [0] : unit | semmle.label | TupleExpr : unit [0] : unit | -| test.swift:64:21:64:35 | CallExpr : unit | semmle.label | CallExpr : unit | -| test.swift:66:10:66:14 | tuple : unit [1] : unit | semmle.label | tuple : unit [1] : unit | +| test.swift:64:5:64:16 | TupleExpr [0] | semmle.label | TupleExpr [0] | +| test.swift:64:6:64:10 | [post] tuple [1] | semmle.label | [post] tuple [1] | +| test.swift:64:6:64:12 | MemberAccessExpr | semmle.label | MemberAccessExpr | +| test.swift:64:20:64:51 | TupleExpr [0] | semmle.label | TupleExpr [0] | +| test.swift:64:21:64:35 | CallExpr | semmle.label | CallExpr | +| test.swift:66:10:66:14 | tuple [1] | semmle.label | tuple [1] | | test.swift:66:10:66:16 | MemberAccessExpr | semmle.label | MemberAccessExpr | -| test.swift:74:5:74:9 | [post] tuple : unit [0] : unit | semmle.label | [post] tuple : unit [0] : unit | -| test.swift:74:5:74:11 | MemberAccessExpr : unit | semmle.label | MemberAccessExpr : unit | -| test.swift:74:15:74:29 | CallExpr : unit | semmle.label | CallExpr : unit | -| test.swift:75:10:75:14 | tuple : unit [0] : unit | semmle.label | tuple : unit [0] : unit | +| test.swift:74:5:74:9 | [post] tuple [0] | semmle.label | [post] tuple [0] | +| test.swift:74:5:74:11 | MemberAccessExpr | semmle.label | MemberAccessExpr | +| test.swift:74:15:74:29 | CallExpr | semmle.label | CallExpr | +| test.swift:75:10:75:14 | tuple [0] | semmle.label | tuple [0] | | test.swift:75:10:75:16 | MemberAccessExpr | semmle.label | MemberAccessExpr | -| test.swift:86:9:86:9 | x : unit | semmle.label | x : unit | -| test.swift:86:13:86:27 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:86:9:86:9 | x | semmle.label | x | +| test.swift:86:13:86:27 | CallExpr | semmle.label | CallExpr | | test.swift:90:10:90:10 | x | semmle.label | x | -| test.swift:94:9:94:9 | y : unit | semmle.label | y : unit | -| test.swift:94:13:94:27 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:94:9:94:9 | y | semmle.label | y | +| test.swift:94:13:94:27 | CallExpr | semmle.label | CallExpr | | test.swift:96:10:96:10 | y | semmle.label | y | | test.swift:99:14:99:14 | x | semmle.label | x | | test.swift:100:14:100:14 | y | semmle.label | y | -| test.swift:107:9:107:13 | [post] tuple : unit [0] : unit | semmle.label | [post] tuple : unit [0] : unit | -| test.swift:107:9:107:15 | MemberAccessExpr : unit | semmle.label | MemberAccessExpr : unit | -| test.swift:107:19:107:33 | CallExpr : unit | semmle.label | CallExpr : unit | -| test.swift:112:10:112:14 | tuple : unit [0] : unit | semmle.label | tuple : unit [0] : unit | +| test.swift:107:9:107:13 | [post] tuple [0] | semmle.label | [post] tuple [0] | +| test.swift:107:9:107:15 | MemberAccessExpr | semmle.label | MemberAccessExpr | +| test.swift:107:19:107:33 | CallExpr | semmle.label | CallExpr | +| test.swift:112:10:112:14 | tuple [0] | semmle.label | tuple [0] | | test.swift:112:10:112:16 | MemberAccessExpr | semmle.label | MemberAccessExpr | -| test.swift:117:9:117:13 | tuple : unit [0] : unit | semmle.label | tuple : unit [0] : unit | -| test.swift:117:9:117:13 | tuple : unit [1] : unit | semmle.label | tuple : unit [1] : unit | -| test.swift:117:17:117:50 | TupleExpr : unit [0] : unit | semmle.label | TupleExpr : unit [0] : unit | -| test.swift:117:17:117:50 | TupleExpr : unit [1] : unit | semmle.label | TupleExpr : unit [1] : unit | -| test.swift:117:18:117:33 | CallExpr : unit | semmle.label | CallExpr : unit | -| test.swift:117:35:117:49 | CallExpr : unit | semmle.label | CallExpr : unit | -| test.swift:120:9:120:13 | TupleExpr : unit [0] : unit | semmle.label | TupleExpr : unit [0] : unit | -| test.swift:120:9:120:13 | TupleExpr : unit [1] : unit | semmle.label | TupleExpr : unit [1] : unit | -| test.swift:120:10:120:10 | a : unit | semmle.label | a : unit | -| test.swift:120:12:120:12 | b : unit | semmle.label | b : unit | -| test.swift:120:17:120:21 | tuple : unit [0] : unit | semmle.label | tuple : unit [0] : unit | -| test.swift:120:17:120:21 | tuple : unit [1] : unit | semmle.label | tuple : unit [1] : unit | +| test.swift:117:9:117:13 | tuple [0] | semmle.label | tuple [0] | +| test.swift:117:9:117:13 | tuple [1] | semmle.label | tuple [1] | +| test.swift:117:17:117:50 | TupleExpr [0] | semmle.label | TupleExpr [0] | +| test.swift:117:17:117:50 | TupleExpr [1] | semmle.label | TupleExpr [1] | +| test.swift:117:18:117:33 | CallExpr | semmle.label | CallExpr | +| test.swift:117:35:117:49 | CallExpr | semmle.label | CallExpr | +| test.swift:120:9:120:13 | TupleExpr [0] | semmle.label | TupleExpr [0] | +| test.swift:120:9:120:13 | TupleExpr [1] | semmle.label | TupleExpr [1] | +| test.swift:120:10:120:10 | a | semmle.label | a | +| test.swift:120:12:120:12 | b | semmle.label | b | +| test.swift:120:17:120:21 | tuple [0] | semmle.label | tuple [0] | +| test.swift:120:17:120:21 | tuple [1] | semmle.label | tuple [1] | | test.swift:125:10:125:10 | a | semmle.label | a | | test.swift:126:10:126:10 | b | semmle.label | b | -| test.swift:131:5:131:5 | a : unit | semmle.label | a : unit | +| test.swift:131:5:131:5 | a | semmle.label | a | | test.swift:131:14:131:14 | a | semmle.label | a | -| test.swift:131:19:131:33 | CallExpr : unit | semmle.label | CallExpr : unit | +| test.swift:131:19:131:33 | CallExpr | semmle.label | CallExpr | | test.swift:132:10:132:10 | a | semmle.label | a | subpaths testFailures #select -| implicit-self.swift:12:14:12:19 | MemberAccessExpr | implicit-self.swift:11:18:11:31 | CallExpr : unit | implicit-self.swift:12:14:12:19 | MemberAccessExpr | $@ | implicit-self.swift:11:18:11:31 | CallExpr : unit | CallExpr : unit | -| implicit-self.swift:18:14:18:14 | x | implicit-self.swift:17:13:17:26 | CallExpr : unit | implicit-self.swift:18:14:18:14 | x | $@ | implicit-self.swift:17:13:17:26 | CallExpr : unit | CallExpr : unit | -| implicit-self.swift:24:14:24:19 | MemberAccessExpr | implicit-self.swift:23:13:23:26 | CallExpr : unit | implicit-self.swift:24:14:24:19 | MemberAccessExpr | $@ | implicit-self.swift:23:13:23:26 | CallExpr : unit | CallExpr : unit | -| implicit-self.swift:30:14:30:14 | x | implicit-self.swift:29:18:29:31 | CallExpr : unit | implicit-self.swift:30:14:30:14 | x | $@ | implicit-self.swift:29:18:29:31 | CallExpr : unit | CallExpr : unit | -| implicit-self.swift:36:14:36:23 | MemberAccessExpr | implicit-self.swift:35:22:35:35 | CallExpr : unit | implicit-self.swift:36:14:36:23 | MemberAccessExpr | $@ | implicit-self.swift:35:22:35:35 | CallExpr : unit | CallExpr : unit | -| implicit-self.swift:42:14:42:18 | MemberAccessExpr | implicit-self.swift:41:17:41:30 | CallExpr : unit | implicit-self.swift:42:14:42:18 | MemberAccessExpr | $@ | implicit-self.swift:41:17:41:30 | CallExpr : unit | CallExpr : unit | -| implicit-self.swift:48:14:48:23 | MemberAccessExpr | implicit-self.swift:47:17:47:30 | CallExpr : unit | implicit-self.swift:48:14:48:23 | MemberAccessExpr | $@ | implicit-self.swift:47:17:47:30 | CallExpr : unit | CallExpr : unit | -| implicit-self.swift:54:14:54:18 | MemberAccessExpr | implicit-self.swift:53:22:53:35 | CallExpr : unit | implicit-self.swift:54:14:54:18 | MemberAccessExpr | $@ | implicit-self.swift:53:22:53:35 | CallExpr : unit | CallExpr : unit | +| implicit-self.swift:12:14:12:19 | MemberAccessExpr | implicit-self.swift:11:18:11:31 | CallExpr | implicit-self.swift:12:14:12:19 | MemberAccessExpr | $@ | implicit-self.swift:11:18:11:31 | CallExpr | CallExpr | +| implicit-self.swift:18:14:18:14 | x | implicit-self.swift:17:13:17:26 | CallExpr | implicit-self.swift:18:14:18:14 | x | $@ | implicit-self.swift:17:13:17:26 | CallExpr | CallExpr | +| implicit-self.swift:24:14:24:19 | MemberAccessExpr | implicit-self.swift:23:13:23:26 | CallExpr | implicit-self.swift:24:14:24:19 | MemberAccessExpr | $@ | implicit-self.swift:23:13:23:26 | CallExpr | CallExpr | +| implicit-self.swift:30:14:30:14 | x | implicit-self.swift:29:18:29:31 | CallExpr | implicit-self.swift:30:14:30:14 | x | $@ | implicit-self.swift:29:18:29:31 | CallExpr | CallExpr | +| implicit-self.swift:36:14:36:23 | MemberAccessExpr | implicit-self.swift:35:22:35:35 | CallExpr | implicit-self.swift:36:14:36:23 | MemberAccessExpr | $@ | implicit-self.swift:35:22:35:35 | CallExpr | CallExpr | +| implicit-self.swift:42:14:42:18 | MemberAccessExpr | implicit-self.swift:41:17:41:30 | CallExpr | implicit-self.swift:42:14:42:18 | MemberAccessExpr | $@ | implicit-self.swift:41:17:41:30 | CallExpr | CallExpr | +| implicit-self.swift:48:14:48:23 | MemberAccessExpr | implicit-self.swift:47:17:47:30 | CallExpr | implicit-self.swift:48:14:48:23 | MemberAccessExpr | $@ | implicit-self.swift:47:17:47:30 | CallExpr | CallExpr | +| implicit-self.swift:54:14:54:18 | MemberAccessExpr | implicit-self.swift:53:22:53:35 | CallExpr | implicit-self.swift:54:14:54:18 | MemberAccessExpr | $@ | implicit-self.swift:53:22:53:35 | CallExpr | CallExpr | | test.swift:2:10:2:21 | CallExpr | test.swift:2:10:2:21 | CallExpr | test.swift:2:10:2:21 | CallExpr | $@ | test.swift:2:10:2:21 | CallExpr | CallExpr | -| test.swift:6:10:6:32 | BinaryExpr | test.swift:6:10:6:23 | CallExpr : unit | test.swift:6:10:6:32 | BinaryExpr | $@ | test.swift:6:10:6:23 | CallExpr : unit | CallExpr : unit | -| test.swift:7:10:7:32 | BinaryExpr | test.swift:7:19:7:32 | CallExpr : unit | test.swift:7:10:7:32 | BinaryExpr | $@ | test.swift:7:19:7:32 | CallExpr : unit | CallExpr : unit | -| test.swift:9:10:9:33 | StringInterpolationExpr | test.swift:9:13:9:26 | CallExpr : unit | test.swift:9:10:9:33 | StringInterpolationExpr | $@ | test.swift:9:13:9:26 | CallExpr : unit | CallExpr : unit | -| test.swift:10:10:10:33 | StringInterpolationExpr | test.swift:10:18:10:31 | CallExpr : unit | test.swift:10:10:10:33 | StringInterpolationExpr | $@ | test.swift:10:18:10:31 | CallExpr : unit | CallExpr : unit | -| test.swift:11:10:11:38 | StringInterpolationExpr | test.swift:11:18:11:31 | CallExpr : unit | test.swift:11:10:11:38 | StringInterpolationExpr | $@ | test.swift:11:18:11:31 | CallExpr : unit | CallExpr : unit | -| test.swift:16:10:16:35 | MemberAccessExpr | test.swift:16:11:16:25 | CallExpr : unit | test.swift:16:10:16:35 | MemberAccessExpr | $@ | test.swift:16:11:16:25 | CallExpr : unit | CallExpr : unit | -| test.swift:19:10:19:35 | MemberAccessExpr | test.swift:19:19:19:32 | CallExpr : unit | test.swift:19:10:19:35 | MemberAccessExpr | $@ | test.swift:19:19:19:32 | CallExpr : unit | CallExpr : unit | -| test.swift:24:10:24:10 | a | test.swift:23:13:23:26 | CallExpr : unit | test.swift:24:10:24:10 | a | $@ | test.swift:23:13:23:26 | CallExpr : unit | CallExpr : unit | -| test.swift:29:10:29:10 | a | test.swift:28:19:28:33 | CallExpr : unit | test.swift:29:10:29:10 | a | $@ | test.swift:28:19:28:33 | CallExpr : unit | CallExpr : unit | -| test.swift:34:10:34:10 | d | test.swift:32:27:32:40 | CallExpr : unit | test.swift:34:10:34:10 | d | $@ | test.swift:32:27:32:40 | CallExpr : unit | CallExpr : unit | -| test.swift:39:10:39:10 | a | test.swift:38:13:38:26 | CallExpr : unit | test.swift:39:10:39:10 | a | $@ | test.swift:38:13:38:26 | CallExpr : unit | CallExpr : unit | -| test.swift:47:10:47:16 | MemberAccessExpr | test.swift:46:15:46:28 | CallExpr : unit | test.swift:47:10:47:16 | MemberAccessExpr | $@ | test.swift:46:15:46:28 | CallExpr : unit | CallExpr : unit | -| test.swift:58:10:58:23 | MemberAccessExpr | test.swift:53:22:53:35 | CallExpr : unit | test.swift:58:10:58:23 | MemberAccessExpr | $@ | test.swift:53:22:53:35 | CallExpr : unit | CallExpr : unit | -| test.swift:66:10:66:16 | MemberAccessExpr | test.swift:64:21:64:35 | CallExpr : unit | test.swift:66:10:66:16 | MemberAccessExpr | $@ | test.swift:64:21:64:35 | CallExpr : unit | CallExpr : unit | -| test.swift:75:10:75:16 | MemberAccessExpr | test.swift:74:15:74:29 | CallExpr : unit | test.swift:75:10:75:16 | MemberAccessExpr | $@ | test.swift:74:15:74:29 | CallExpr : unit | CallExpr : unit | -| test.swift:90:10:90:10 | x | test.swift:86:13:86:27 | CallExpr : unit | test.swift:90:10:90:10 | x | $@ | test.swift:86:13:86:27 | CallExpr : unit | CallExpr : unit | -| test.swift:96:10:96:10 | y | test.swift:94:13:94:27 | CallExpr : unit | test.swift:96:10:96:10 | y | $@ | test.swift:94:13:94:27 | CallExpr : unit | CallExpr : unit | -| test.swift:99:14:99:14 | x | test.swift:86:13:86:27 | CallExpr : unit | test.swift:99:14:99:14 | x | $@ | test.swift:86:13:86:27 | CallExpr : unit | CallExpr : unit | -| test.swift:100:14:100:14 | y | test.swift:94:13:94:27 | CallExpr : unit | test.swift:100:14:100:14 | y | $@ | test.swift:94:13:94:27 | CallExpr : unit | CallExpr : unit | -| test.swift:112:10:112:16 | MemberAccessExpr | test.swift:107:19:107:33 | CallExpr : unit | test.swift:112:10:112:16 | MemberAccessExpr | $@ | test.swift:107:19:107:33 | CallExpr : unit | CallExpr : unit | -| test.swift:125:10:125:10 | a | test.swift:117:18:117:33 | CallExpr : unit | test.swift:125:10:125:10 | a | $@ | test.swift:117:18:117:33 | CallExpr : unit | CallExpr : unit | -| test.swift:126:10:126:10 | b | test.swift:117:35:117:49 | CallExpr : unit | test.swift:126:10:126:10 | b | $@ | test.swift:117:35:117:49 | CallExpr : unit | CallExpr : unit | -| test.swift:131:14:131:14 | a | test.swift:131:19:131:33 | CallExpr : unit | test.swift:131:14:131:14 | a | $@ | test.swift:131:19:131:33 | CallExpr : unit | CallExpr : unit | -| test.swift:132:10:132:10 | a | test.swift:131:19:131:33 | CallExpr : unit | test.swift:132:10:132:10 | a | $@ | test.swift:131:19:131:33 | CallExpr : unit | CallExpr : unit | +| test.swift:6:10:6:32 | BinaryExpr | test.swift:6:10:6:23 | CallExpr | test.swift:6:10:6:32 | BinaryExpr | $@ | test.swift:6:10:6:23 | CallExpr | CallExpr | +| test.swift:7:10:7:32 | BinaryExpr | test.swift:7:19:7:32 | CallExpr | test.swift:7:10:7:32 | BinaryExpr | $@ | test.swift:7:19:7:32 | CallExpr | CallExpr | +| test.swift:9:10:9:33 | StringInterpolationExpr | test.swift:9:13:9:26 | CallExpr | test.swift:9:10:9:33 | StringInterpolationExpr | $@ | test.swift:9:13:9:26 | CallExpr | CallExpr | +| test.swift:10:10:10:33 | StringInterpolationExpr | test.swift:10:18:10:31 | CallExpr | test.swift:10:10:10:33 | StringInterpolationExpr | $@ | test.swift:10:18:10:31 | CallExpr | CallExpr | +| test.swift:11:10:11:38 | StringInterpolationExpr | test.swift:11:18:11:31 | CallExpr | test.swift:11:10:11:38 | StringInterpolationExpr | $@ | test.swift:11:18:11:31 | CallExpr | CallExpr | +| test.swift:16:10:16:35 | MemberAccessExpr | test.swift:16:11:16:25 | CallExpr | test.swift:16:10:16:35 | MemberAccessExpr | $@ | test.swift:16:11:16:25 | CallExpr | CallExpr | +| test.swift:19:10:19:35 | MemberAccessExpr | test.swift:19:19:19:32 | CallExpr | test.swift:19:10:19:35 | MemberAccessExpr | $@ | test.swift:19:19:19:32 | CallExpr | CallExpr | +| test.swift:24:10:24:10 | a | test.swift:23:13:23:26 | CallExpr | test.swift:24:10:24:10 | a | $@ | test.swift:23:13:23:26 | CallExpr | CallExpr | +| test.swift:29:10:29:10 | a | test.swift:28:19:28:33 | CallExpr | test.swift:29:10:29:10 | a | $@ | test.swift:28:19:28:33 | CallExpr | CallExpr | +| test.swift:34:10:34:10 | d | test.swift:32:27:32:40 | CallExpr | test.swift:34:10:34:10 | d | $@ | test.swift:32:27:32:40 | CallExpr | CallExpr | +| test.swift:39:10:39:10 | a | test.swift:38:13:38:26 | CallExpr | test.swift:39:10:39:10 | a | $@ | test.swift:38:13:38:26 | CallExpr | CallExpr | +| test.swift:47:10:47:16 | MemberAccessExpr | test.swift:46:15:46:28 | CallExpr | test.swift:47:10:47:16 | MemberAccessExpr | $@ | test.swift:46:15:46:28 | CallExpr | CallExpr | +| test.swift:58:10:58:23 | MemberAccessExpr | test.swift:53:22:53:35 | CallExpr | test.swift:58:10:58:23 | MemberAccessExpr | $@ | test.swift:53:22:53:35 | CallExpr | CallExpr | +| test.swift:66:10:66:16 | MemberAccessExpr | test.swift:64:21:64:35 | CallExpr | test.swift:66:10:66:16 | MemberAccessExpr | $@ | test.swift:64:21:64:35 | CallExpr | CallExpr | +| test.swift:75:10:75:16 | MemberAccessExpr | test.swift:74:15:74:29 | CallExpr | test.swift:75:10:75:16 | MemberAccessExpr | $@ | test.swift:74:15:74:29 | CallExpr | CallExpr | +| test.swift:90:10:90:10 | x | test.swift:86:13:86:27 | CallExpr | test.swift:90:10:90:10 | x | $@ | test.swift:86:13:86:27 | CallExpr | CallExpr | +| test.swift:96:10:96:10 | y | test.swift:94:13:94:27 | CallExpr | test.swift:96:10:96:10 | y | $@ | test.swift:94:13:94:27 | CallExpr | CallExpr | +| test.swift:99:14:99:14 | x | test.swift:86:13:86:27 | CallExpr | test.swift:99:14:99:14 | x | $@ | test.swift:86:13:86:27 | CallExpr | CallExpr | +| test.swift:100:14:100:14 | y | test.swift:94:13:94:27 | CallExpr | test.swift:100:14:100:14 | y | $@ | test.swift:94:13:94:27 | CallExpr | CallExpr | +| test.swift:112:10:112:16 | MemberAccessExpr | test.swift:107:19:107:33 | CallExpr | test.swift:112:10:112:16 | MemberAccessExpr | $@ | test.swift:107:19:107:33 | CallExpr | CallExpr | +| test.swift:125:10:125:10 | a | test.swift:117:18:117:33 | CallExpr | test.swift:125:10:125:10 | a | $@ | test.swift:117:18:117:33 | CallExpr | CallExpr | +| test.swift:126:10:126:10 | b | test.swift:117:35:117:49 | CallExpr | test.swift:126:10:126:10 | b | $@ | test.swift:117:35:117:49 | CallExpr | CallExpr | +| test.swift:131:14:131:14 | a | test.swift:131:19:131:33 | CallExpr | test.swift:131:14:131:14 | a | $@ | test.swift:131:19:131:33 | CallExpr | CallExpr | +| test.swift:132:10:132:10 | a | test.swift:131:19:131:33 | CallExpr | test.swift:132:10:132:10 | a | $@ | test.swift:131:19:131:33 | CallExpr | CallExpr | diff --git a/unified/ql/test/query-tests/security/CWE-312/CleartextLogging/CleartextLogging.expected b/unified/ql/test/query-tests/security/CWE-312/CleartextLogging/CleartextLogging.expected index cf60bed522d7..cb3f1cb61f2f 100644 --- a/unified/ql/test/query-tests/security/CWE-312/CleartextLogging/CleartextLogging.expected +++ b/unified/ql/test/query-tests/security/CWE-312/CleartextLogging/CleartextLogging.expected @@ -1,8 +1,8 @@ #select -| CleartextLoggingBad.swift:2:7:2:44 | StringInterpolationExpr | CleartextLoggingBad.swift:2:35:2:42 | password : unit | CleartextLoggingBad.swift:2:7:2:44 | StringInterpolationExpr | Logging of $@ | CleartextLoggingBad.swift:2:35:2:42 | password | sensitive data | +| CleartextLoggingBad.swift:2:7:2:44 | StringInterpolationExpr | CleartextLoggingBad.swift:2:35:2:42 | password | CleartextLoggingBad.swift:2:7:2:44 | StringInterpolationExpr | Logging of $@ | CleartextLoggingBad.swift:2:35:2:42 | password | sensitive data | edges -| CleartextLoggingBad.swift:2:35:2:42 | password : unit | CleartextLoggingBad.swift:2:7:2:44 | StringInterpolationExpr | provenance | | +| CleartextLoggingBad.swift:2:35:2:42 | password | CleartextLoggingBad.swift:2:7:2:44 | StringInterpolationExpr | provenance | | nodes | CleartextLoggingBad.swift:2:7:2:44 | StringInterpolationExpr | semmle.label | StringInterpolationExpr | -| CleartextLoggingBad.swift:2:35:2:42 | password : unit | semmle.label | password : unit | +| CleartextLoggingBad.swift:2:35:2:42 | password | semmle.label | password | subpaths