Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f6bd6e8
unified: Add ExprPositions
asgerf Sep 11, 2026
ed87152
unified: Scaffold empty data flow graph with test
asgerf Sep 8, 2026
a35466d
unified: Add taint through +
asgerf Sep 8, 2026
75d215d
unified: Add basic test with tuples
asgerf Sep 8, 2026
a1bca71
unified: Add steps for member access and tuples
asgerf Sep 8, 2026
eadbcd1
unified: Add query for viewing data flow graph
asgerf Sep 8, 2026
e8ee131
unified: Add incoming-value nodes
asgerf Sep 12, 2026
a87414c
unified: CFG-insensitive variable flow
asgerf Sep 9, 2026
dfd1a50
unified: Add post-update nodes
asgerf Sep 9, 2026
707238e
unified: Add variable-ref nodes
asgerf Sep 9, 2026
b5c2ff6
unified: Update debug graph
asgerf Sep 9, 2026
d0e5d1b
unified: Add LocalSsa
asgerf Sep 10, 2026
e5efdbd
unified: Include SSA steps in debug view
asgerf Sep 10, 2026
bf3aae6
unified: Handle implicit 'self' references
asgerf Sep 10, 2026
ff8e042
unified: Add some tests with assignment-timing
asgerf Sep 10, 2026
098e7e0
unified: Instantiate DataFlowConsistency
asgerf Sep 9, 2026
09cfe5d
unified: Add flow through string interpolation
asgerf Sep 14, 2026
a03cd48
unified: Add dumb version of CleartextLogging
asgerf Sep 14, 2026
2d1a99c
unified: Copy query help from old swift
asgerf Sep 14, 2026
02564e9
Add support for AssociatedTypeDeclaration return type
asgerf Sep 14, 2026
3c332a3
Update unified/ql/src/queries/security/CWE-312/CleartextLogging.qhelp
asgerf Sep 14, 2026
0b48cd4
unified: Add plugin
asgerf Sep 15, 2026
2acd6da
unified: Add local SSA consistency-query
asgerf Sep 15, 2026
6fe1db0
unified: Add sink calls without flow
asgerf Sep 15, 2026
dea3219
unified: Explain the "local" in "local SSA"
asgerf Sep 15, 2026
5f2b8e8
unified: Rename to TExprPostUpdateNode for clarity
asgerf Sep 15, 2026
535b215
unified: Fill in nodeIsVisible and neverSkipInPathGraph
asgerf Sep 15, 2026
6f0f14a
unified: Include path problem output tuples
asgerf Sep 15, 2026
11bcf84
unified: Omit "unit" type from path steps
asgerf Sep 15, 2026
343459c
unified: Update expected output
asgerf Sep 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions unified/ql/consistency-queries/DataFlowConsistency.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
private import unified
Comment thread
hvitved marked this conversation as resolved.
private import codeql.unified.internal.dataflow.AllDataFlow
private import codeql.dataflow.internal.DataFlowImplConsistency

module ConsistencyInput implements InputSig<Location, DataFlowInput> { }

module ConsistencyOutput =
MakeConsistency<Location, DataFlowInput, TaintTrackingInput, ConsistencyInput>;

import ConsistencyOutput
3 changes: 3 additions & 0 deletions unified/ql/consistency-queries/LocalSsaConsistency.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
private import unified
private import codeql.unified.internal.dataflow.LocalSsa
import LocalSsaOutput::Consistency
21 changes: 13 additions & 8 deletions unified/ql/lib/codeql/unified/internal/AstExtra.qll
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
}
}
70 changes: 70 additions & 0 deletions unified/ql/lib/codeql/unified/internal/ExprPositions.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
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(FunctionExpr 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 = any(AssociatedTypeDeclaration 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
}
3 changes: 3 additions & 0 deletions unified/ql/lib/codeql/unified/internal/FacadeAst.qll
Original file line number Diff line number Diff line change
Expand Up @@ -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()) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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() }
}
}

Expand Down
11 changes: 11 additions & 0 deletions unified/ql/lib/codeql/unified/internal/dataflow/AllDataFlow.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/** Re-exports all the files in the internal dataflow folder (except DataFlowPublic). */

import Content
import DataFlowGraph
import DataFlowInstantiation
import DataFlowNode
import DataFlowPlugin
import Step
import LocalSsa
import TaintTrackingInstantiation
import VariableRefKind
37 changes: 37 additions & 0 deletions unified/ql/lib/codeql/unified/internal/dataflow/Content.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
private import unified
private import AllDataFlow

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) }

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 }
}
118 changes: 118 additions & 0 deletions unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
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
node2.isIncomingValue(decl.getPattern())
Comment thread
hvitved marked this conversation as resolved.
)
or
exists(AssignExpr assign |
node1.isResultValue(assign.getValue()) and
step.value() and
node2.isIncomingValue(assign.getTarget())
)
or
exists(LocalVariableAccess access |
node1.isLocalVariableRead(access, access.getLocalVariable()) and
step.value() and
node2.isResultValue(access)
or
node1.isIncomingValue(access) and
step.value() and
node2.isLocalVariableWrite(access, access.getLocalVariable())
or
node1.isPostUpdate(access) and
step.value() and
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(StringInterpolationExpr expr |
node1.isResultValue(expr.getAnElement()) and
step.taint() and
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
node1.isIncomingValue(expr) and
step.readName(i.toString()) and
node2.isIncomingValue(expr.getElement(i).getValue())
)
or
exists(MemberAccessExpr expr |
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
}

/** Holds if `node` should be included in the debug view. */
private signature predicate relevantNodeSig(AstNode node);

module DebugGraph<relevantNodeSig/1 relevantNode> {
private Node adjacent(Node n) {
step(n, _, result)
or
step(result, _, n)
or
localSsaStep(n, result, _)
or
localSsaStep(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) {
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
relevantDataFlowNode(node1) and
relevantDataFlowNode(node2) and
(
exists(Step step |
step(node1, step, node2) and
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"
)
}
}
Loading
Loading