mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Swift: Add alert provenance plumbing.
This commit is contained in:
@@ -490,9 +490,9 @@ private module Cached {
|
||||
* model.
|
||||
*/
|
||||
cached
|
||||
predicate sourceNode(Node node, string kind) {
|
||||
predicate sourceNode(Node node, string kind, string model) {
|
||||
exists(SourceSinkInterpretationInput::InterpretNode n |
|
||||
isSourceNode(n, kind) and n.asNode() = node
|
||||
isSourceNode(n, kind, model) and n.asNode() = node
|
||||
)
|
||||
}
|
||||
|
||||
@@ -501,57 +501,76 @@ private module Cached {
|
||||
* model.
|
||||
*/
|
||||
cached
|
||||
predicate sinkNode(Node node, string kind) {
|
||||
predicate sinkNode(Node node, string kind, string model) {
|
||||
exists(SourceSinkInterpretationInput::InterpretNode n |
|
||||
isSinkNode(n, kind) and n.asNode() = node
|
||||
isSinkNode(n, kind, model) and n.asNode() = node
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import Cached
|
||||
|
||||
/**
|
||||
* Holds if `node` is specified as a source with the given kind in a MaD flow
|
||||
* model.
|
||||
*/
|
||||
predicate sourceNode(Node node, string kind) { sourceNode(node, kind, _) }
|
||||
|
||||
/**
|
||||
* Holds if `node` is specified as a sink with the given kind in a MaD flow
|
||||
* model.
|
||||
*/
|
||||
predicate sinkNode(Node node, string kind) { sinkNode(node, kind, _) }
|
||||
|
||||
private predicate interpretSummary(
|
||||
Function f, string input, string output, string kind, string provenance
|
||||
Function f, string input, string output, string kind, string provenance, string model
|
||||
) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind, provenance) and
|
||||
model = "" and // TODO: Insert MaD provenance from summaryModel
|
||||
f = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
)
|
||||
}
|
||||
|
||||
// adapter class for converting Mad summaries to `SummarizedCallable`s
|
||||
private class SummarizedCallableAdapter extends SummarizedCallable {
|
||||
SummarizedCallableAdapter() { interpretSummary(this, _, _, _, _) }
|
||||
SummarizedCallableAdapter() { interpretSummary(this, _, _, _, _, _) }
|
||||
|
||||
private predicate relevantSummaryElementManual(string input, string output, string kind) {
|
||||
private predicate relevantSummaryElementManual(
|
||||
string input, string output, string kind, string model
|
||||
) {
|
||||
exists(Provenance provenance |
|
||||
interpretSummary(this, input, output, kind, provenance) and
|
||||
interpretSummary(this, input, output, kind, provenance, model) and
|
||||
provenance.isManual()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate relevantSummaryElementGenerated(string input, string output, string kind) {
|
||||
private predicate relevantSummaryElementGenerated(
|
||||
string input, string output, string kind, string model
|
||||
) {
|
||||
exists(Provenance provenance |
|
||||
interpretSummary(this, input, output, kind, provenance) and
|
||||
interpretSummary(this, input, output, kind, provenance, model) and
|
||||
provenance.isGenerated()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||
override predicate propagatesFlow(
|
||||
string input, string output, boolean preservesValue, string model
|
||||
) {
|
||||
exists(string kind |
|
||||
this.relevantSummaryElementManual(input, output, kind)
|
||||
this.relevantSummaryElementManual(input, output, kind, model)
|
||||
or
|
||||
not this.relevantSummaryElementManual(_, _, _) and
|
||||
this.relevantSummaryElementGenerated(input, output, kind)
|
||||
not this.relevantSummaryElementManual(_, _, _, _) and
|
||||
this.relevantSummaryElementGenerated(input, output, kind, model)
|
||||
|
|
||||
if kind = "value" then preservesValue = true else preservesValue = false
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasProvenance(Provenance provenance) {
|
||||
interpretSummary(this, _, _, _, provenance)
|
||||
interpretSummary(this, _, _, _, provenance, _)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ private import codeql.swift.dataflow.Ssa
|
||||
private import codeql.swift.controlflow.BasicBlocks
|
||||
private import codeql.swift.dataflow.FlowSummary as FlowSummary
|
||||
private import codeql.swift.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
private import codeql.swift.dataflow.ExternalFlow
|
||||
private import codeql.swift.frameworks.StandardLibrary.PointerTypes
|
||||
private import codeql.swift.frameworks.StandardLibrary.Array
|
||||
private import codeql.swift.frameworks.StandardLibrary.Dictionary
|
||||
@@ -184,127 +185,130 @@ private module Cached {
|
||||
nodeTo = getParameterDefNode(nodeFrom.asParameter())
|
||||
}
|
||||
|
||||
private predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) {
|
||||
exists(Ssa::Definition def |
|
||||
// Step from assignment RHS to def
|
||||
def.(Ssa::WriteDefinition).assigns(nodeFrom.getCfgNode()) and
|
||||
nodeTo.asDefinition() = def
|
||||
or
|
||||
// step from def to first read
|
||||
nodeFrom.asDefinition() = def and
|
||||
nodeTo.getCfgNode() = def.getAFirstRead() and
|
||||
(
|
||||
nodeTo instanceof InoutReturnNodeImpl
|
||||
implies
|
||||
nodeTo.(InoutReturnNodeImpl).getParameter() = def.getSourceVariable().asVarDecl()
|
||||
private predicate localFlowStepCommon(Node nodeFrom, Node nodeTo, string model) {
|
||||
(
|
||||
exists(Ssa::Definition def |
|
||||
// Step from assignment RHS to def
|
||||
def.(Ssa::WriteDefinition).assigns(nodeFrom.getCfgNode()) and
|
||||
nodeTo.asDefinition() = def
|
||||
or
|
||||
// step from def to first read
|
||||
nodeFrom.asDefinition() = def and
|
||||
nodeTo.getCfgNode() = def.getAFirstRead() and
|
||||
(
|
||||
nodeTo instanceof InoutReturnNodeImpl
|
||||
implies
|
||||
nodeTo.(InoutReturnNodeImpl).getParameter() = def.getSourceVariable().asVarDecl()
|
||||
)
|
||||
or
|
||||
// use-use flow
|
||||
localSsaFlowStepUseUse(def, nodeFrom, nodeTo)
|
||||
or
|
||||
localSsaFlowStepUseUse(def, nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo)
|
||||
or
|
||||
// step from previous read to Phi node
|
||||
localFlowSsaInput(nodeFrom, def, nodeTo.asDefinition())
|
||||
)
|
||||
or
|
||||
// use-use flow
|
||||
localSsaFlowStepUseUse(def, nodeFrom, nodeTo)
|
||||
localFlowSsaParamInput(nodeFrom, nodeTo)
|
||||
or
|
||||
localSsaFlowStepUseUse(def, nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo)
|
||||
// flow through `&` (inout argument)
|
||||
nodeFrom.asExpr() = nodeTo.asExpr().(InOutExpr).getSubExpr()
|
||||
or
|
||||
// step from previous read to Phi node
|
||||
localFlowSsaInput(nodeFrom, def, nodeTo.asDefinition())
|
||||
)
|
||||
or
|
||||
localFlowSsaParamInput(nodeFrom, nodeTo)
|
||||
or
|
||||
// flow through `&` (inout argument)
|
||||
nodeFrom.asExpr() = nodeTo.asExpr().(InOutExpr).getSubExpr()
|
||||
or
|
||||
// reverse flow through `&` (inout argument)
|
||||
nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr().(InOutExpr).getSubExpr() =
|
||||
nodeTo.(PostUpdateNode).getPreUpdateNode().asExpr()
|
||||
or
|
||||
// flow through `try!` and similar constructs
|
||||
nodeFrom.asExpr() = nodeTo.asExpr().(AnyTryExpr).getSubExpr()
|
||||
or
|
||||
// flow through `!`
|
||||
// note: there's a case in `readStep` that handles when the source is the
|
||||
// `OptionalSomeContentSet` within the RHS. This case is for when the
|
||||
// `Optional` itself is tainted (which it usually shouldn't be, but
|
||||
// retaining this case increases robustness of flow).
|
||||
nodeFrom.asExpr() = nodeTo.asExpr().(ForceValueExpr).getSubExpr()
|
||||
or
|
||||
// read of an optional .some member via `let x: T = y: T?` pattern matching
|
||||
// note: similar to `ForceValueExpr` this is ideally a content `readStep` but
|
||||
// in practice we sometimes have taint on the optional itself.
|
||||
nodeTo.asPattern() = nodeFrom.asPattern().(OptionalSomePattern).getSubPattern()
|
||||
or
|
||||
// flow through `?` and `?.`
|
||||
nodeFrom.asExpr() = nodeTo.asExpr().(BindOptionalExpr).getSubExpr()
|
||||
or
|
||||
nodeFrom.asExpr() = nodeTo.asExpr().(OptionalEvaluationExpr).getSubExpr()
|
||||
or
|
||||
// flow through unary `+` (which does nothing)
|
||||
nodeFrom.asExpr() = nodeTo.asExpr().(UnaryPlusExpr).getOperand()
|
||||
or
|
||||
// flow through varargs expansions (that wrap an `ArrayExpr` where varargs enter a call)
|
||||
nodeFrom.asExpr() = nodeTo.asExpr().(VarargExpansionExpr).getSubExpr()
|
||||
or
|
||||
// flow through nil-coalescing operator `??`
|
||||
exists(BinaryExpr nco |
|
||||
nco.getOperator().(FreeFunction).getName() = "??(_:_:)" and
|
||||
nodeTo.asExpr() = nco
|
||||
|
|
||||
// value argument
|
||||
nodeFrom.asExpr() = nco.getAnOperand()
|
||||
// reverse flow through `&` (inout argument)
|
||||
nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr().(InOutExpr).getSubExpr() =
|
||||
nodeTo.(PostUpdateNode).getPreUpdateNode().asExpr()
|
||||
or
|
||||
// unpack closure (the second argument is an `AutoClosureExpr` argument)
|
||||
nodeFrom.asExpr() = nco.getAnOperand().(AutoClosureExpr).getExpr()
|
||||
)
|
||||
or
|
||||
// flow through ternary operator `? :`
|
||||
exists(IfExpr ie |
|
||||
nodeTo.asExpr() = ie and
|
||||
nodeFrom.asExpr() = ie.getBranch(_)
|
||||
)
|
||||
or
|
||||
// flow through OpenExistentialExpr (compiler generated expression wrapper)
|
||||
nodeFrom.asExpr() = nodeTo.asExpr().(OpenExistentialExpr).getSubExpr()
|
||||
or
|
||||
// flow from Expr to Pattern
|
||||
exists(Expr e, Pattern p |
|
||||
nodeFrom.asExpr() = e and
|
||||
nodeTo.asPattern() = p and
|
||||
p.getImmediateMatchingExpr() = e
|
||||
)
|
||||
or
|
||||
// flow from Pattern to an identity-preserving sub-Pattern:
|
||||
nodeTo.asPattern() =
|
||||
[
|
||||
nodeFrom.asPattern().(IsPattern).getSubPattern(),
|
||||
nodeFrom.asPattern().(TypedPattern).getSubPattern()
|
||||
]
|
||||
or
|
||||
// Flow from the last component in a key path chain to
|
||||
// the return node for the key path.
|
||||
exists(KeyPathExpr keyPath |
|
||||
nodeFrom.(KeyPathComponentNodeImpl).getComponent() =
|
||||
keyPath.getComponent(keyPath.getNumberOfComponents() - 1) and
|
||||
nodeTo.(KeyPathReturnNodeImpl).getKeyPathExpr() = keyPath
|
||||
)
|
||||
or
|
||||
exists(KeyPathExpr keyPath |
|
||||
nodeTo.(KeyPathComponentPostUpdateNode).getComponent() =
|
||||
keyPath.getComponent(keyPath.getNumberOfComponents() - 1) and
|
||||
nodeFrom.(KeyPathReturnPostUpdateNode).getKeyPathExpr() = keyPath
|
||||
)
|
||||
or
|
||||
// Flow to the result of a keypath assignment
|
||||
exists(KeyPathApplicationExpr apply, AssignExpr assign |
|
||||
apply = assign.getDest() and
|
||||
nodeTo.asExpr() = apply and
|
||||
nodeFrom.asExpr() = assign.getSource()
|
||||
)
|
||||
// flow through `try!` and similar constructs
|
||||
nodeFrom.asExpr() = nodeTo.asExpr().(AnyTryExpr).getSubExpr()
|
||||
or
|
||||
// flow through `!`
|
||||
// note: there's a case in `readStep` that handles when the source is the
|
||||
// `OptionalSomeContentSet` within the RHS. This case is for when the
|
||||
// `Optional` itself is tainted (which it usually shouldn't be, but
|
||||
// retaining this case increases robustness of flow).
|
||||
nodeFrom.asExpr() = nodeTo.asExpr().(ForceValueExpr).getSubExpr()
|
||||
or
|
||||
// read of an optional .some member via `let x: T = y: T?` pattern matching
|
||||
// note: similar to `ForceValueExpr` this is ideally a content `readStep` but
|
||||
// in practice we sometimes have taint on the optional itself.
|
||||
nodeTo.asPattern() = nodeFrom.asPattern().(OptionalSomePattern).getSubPattern()
|
||||
or
|
||||
// flow through `?` and `?.`
|
||||
nodeFrom.asExpr() = nodeTo.asExpr().(BindOptionalExpr).getSubExpr()
|
||||
or
|
||||
nodeFrom.asExpr() = nodeTo.asExpr().(OptionalEvaluationExpr).getSubExpr()
|
||||
or
|
||||
// flow through unary `+` (which does nothing)
|
||||
nodeFrom.asExpr() = nodeTo.asExpr().(UnaryPlusExpr).getOperand()
|
||||
or
|
||||
// flow through varargs expansions (that wrap an `ArrayExpr` where varargs enter a call)
|
||||
nodeFrom.asExpr() = nodeTo.asExpr().(VarargExpansionExpr).getSubExpr()
|
||||
or
|
||||
// flow through nil-coalescing operator `??`
|
||||
exists(BinaryExpr nco |
|
||||
nco.getOperator().(FreeFunction).getName() = "??(_:_:)" and
|
||||
nodeTo.asExpr() = nco
|
||||
|
|
||||
// value argument
|
||||
nodeFrom.asExpr() = nco.getAnOperand()
|
||||
or
|
||||
// unpack closure (the second argument is an `AutoClosureExpr` argument)
|
||||
nodeFrom.asExpr() = nco.getAnOperand().(AutoClosureExpr).getExpr()
|
||||
)
|
||||
or
|
||||
// flow through ternary operator `? :`
|
||||
exists(IfExpr ie |
|
||||
nodeTo.asExpr() = ie and
|
||||
nodeFrom.asExpr() = ie.getBranch(_)
|
||||
)
|
||||
or
|
||||
// flow through OpenExistentialExpr (compiler generated expression wrapper)
|
||||
nodeFrom.asExpr() = nodeTo.asExpr().(OpenExistentialExpr).getSubExpr()
|
||||
or
|
||||
// flow from Expr to Pattern
|
||||
exists(Expr e, Pattern p |
|
||||
nodeFrom.asExpr() = e and
|
||||
nodeTo.asPattern() = p and
|
||||
p.getImmediateMatchingExpr() = e
|
||||
)
|
||||
or
|
||||
// flow from Pattern to an identity-preserving sub-Pattern:
|
||||
nodeTo.asPattern() =
|
||||
[
|
||||
nodeFrom.asPattern().(IsPattern).getSubPattern(),
|
||||
nodeFrom.asPattern().(TypedPattern).getSubPattern()
|
||||
]
|
||||
or
|
||||
// Flow from the last component in a key path chain to
|
||||
// the return node for the key path.
|
||||
exists(KeyPathExpr keyPath |
|
||||
nodeFrom.(KeyPathComponentNodeImpl).getComponent() =
|
||||
keyPath.getComponent(keyPath.getNumberOfComponents() - 1) and
|
||||
nodeTo.(KeyPathReturnNodeImpl).getKeyPathExpr() = keyPath
|
||||
)
|
||||
or
|
||||
exists(KeyPathExpr keyPath |
|
||||
nodeTo.(KeyPathComponentPostUpdateNode).getComponent() =
|
||||
keyPath.getComponent(keyPath.getNumberOfComponents() - 1) and
|
||||
nodeFrom.(KeyPathReturnPostUpdateNode).getKeyPathExpr() = keyPath
|
||||
)
|
||||
or
|
||||
// Flow to the result of a keypath assignment
|
||||
exists(KeyPathApplicationExpr apply, AssignExpr assign |
|
||||
apply = assign.getDest() and
|
||||
nodeTo.asExpr() = apply and
|
||||
nodeFrom.asExpr() = assign.getSource()
|
||||
)
|
||||
or
|
||||
// flow step according to the closure capture library
|
||||
captureValueStep(nodeFrom, nodeTo)
|
||||
) and
|
||||
model = ""
|
||||
or
|
||||
// flow through a flow summary (extension of `SummaryModelCsv`)
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode(), true)
|
||||
or
|
||||
// flow step according to the closure capture library
|
||||
captureValueStep(nodeFrom, nodeTo)
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode(), true, model)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -312,14 +316,14 @@ private module Cached {
|
||||
* data flow.
|
||||
*/
|
||||
cached
|
||||
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
localFlowStepCommon(nodeFrom, nodeTo)
|
||||
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo, string model) {
|
||||
localFlowStepCommon(nodeFrom, nodeTo, model)
|
||||
}
|
||||
|
||||
/** This is the local flow predicate that is exposed. */
|
||||
cached
|
||||
predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) {
|
||||
localFlowStepCommon(nodeFrom, nodeTo) or
|
||||
localFlowStepCommon(nodeFrom, nodeTo, _) or
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(nodeFrom, nodeTo, _)
|
||||
}
|
||||
|
||||
@@ -1396,6 +1400,10 @@ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
|
||||
/** Extra data-flow steps needed for lambda flow analysis. */
|
||||
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
|
||||
|
||||
predicate knownSourceModel(Node source, string model) { sourceNode(source, _, model) }
|
||||
|
||||
predicate knownSinkModel(Node sink, string model) { sinkNode(sink, _, model) }
|
||||
|
||||
/**
|
||||
* Holds if flow is allowed to pass from parameter `p` and back to itself as a
|
||||
* side-effect, resulting in a summary from `p` to itself.
|
||||
|
||||
@@ -120,12 +120,13 @@ module SourceSinkInterpretationInput implements
|
||||
* `output`, kind `kind`, and provenance `provenance`.
|
||||
*/
|
||||
predicate sourceElement(
|
||||
SourceOrSinkElement e, string output, string kind, Public::Provenance provenance
|
||||
SourceOrSinkElement e, string output, string kind, Public::Provenance provenance, string model
|
||||
) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance) and
|
||||
model = "" and // TODO: Insert MaD provenance from sourceModel
|
||||
e = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
)
|
||||
}
|
||||
@@ -135,12 +136,13 @@ module SourceSinkInterpretationInput implements
|
||||
* `input`, kind `kind` and provenance `provenance`.
|
||||
*/
|
||||
predicate sinkElement(
|
||||
SourceOrSinkElement e, string input, string kind, Public::Provenance provenance
|
||||
SourceOrSinkElement e, string input, string kind, Public::Provenance provenance, string model
|
||||
) {
|
||||
exists(
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
sinkModel(package, type, subtypes, name, signature, ext, input, kind, provenance) and
|
||||
model = "" and // TODO: Insert MaD provenance from sinkModel
|
||||
e = interpretElement(package, type, subtypes, name, signature, ext)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -20,63 +20,66 @@ private module Cached {
|
||||
* in all global taint flow configurations.
|
||||
*/
|
||||
cached
|
||||
predicate defaultAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// Flow through one argument of `appendLiteral` and `appendInterpolation` and to the second argument.
|
||||
// This is needed for string interpolation generated by the compiler. An interpolated string
|
||||
// like `"I am \(n) years old."` is represented as
|
||||
// ```
|
||||
// $interpolated = ""
|
||||
// appendLiteral(&$interpolated, "I am ")
|
||||
// appendInterpolation(&$interpolated, n)
|
||||
// appendLiteral(&$interpolated, " years old.")
|
||||
// ```
|
||||
exists(ApplyExpr apply, ExprCfgNode e |
|
||||
nodeFrom.asExpr() = [apply.getAnArgument().getExpr(), apply.getQualifier()] and
|
||||
apply.getStaticTarget().getName() = ["appendLiteral(_:)", "appendInterpolation(_:)"] and
|
||||
e.getExpr() = apply.getQualifier() and
|
||||
nodeTo.(PostUpdateNodeImpl).getPreUpdateNode().getCfgNode() = e
|
||||
)
|
||||
or
|
||||
// Flow from the computation of the interpolated string literal to the result of the interpolation.
|
||||
exists(InterpolatedStringLiteralExpr interpolated |
|
||||
nodeTo.asExpr() = interpolated and
|
||||
nodeFrom.asExpr() = interpolated.getAppendingExpr()
|
||||
)
|
||||
or
|
||||
// allow flow through arithmetic (this case includes string concatenation)
|
||||
nodeTo.asExpr().(ArithmeticOperation).getAnOperand() = nodeFrom.asExpr()
|
||||
or
|
||||
// allow flow through bitwise operations
|
||||
nodeTo.asExpr().(BitwiseOperation).getAnOperand() = nodeFrom.asExpr()
|
||||
or
|
||||
// allow flow through assignment operations (e.g. `+=`)
|
||||
exists(AssignOperation op |
|
||||
nodeFrom.asExpr() = op.getSource() and
|
||||
nodeTo.asExpr() = op.getDest()
|
||||
)
|
||||
or
|
||||
// flow through a subscript access
|
||||
exists(SubscriptExpr se |
|
||||
se.getBase() = nodeFrom.asExpr() and
|
||||
se = nodeTo.asExpr()
|
||||
)
|
||||
or
|
||||
// flow through autoclosure expressions (which turn value arguments into closure arguments);
|
||||
// if the value is tainted, it's helpful to consider the autoclosure itself to be tainted as
|
||||
// well for the purposes of matching sink models.
|
||||
nodeFrom.asExpr() = nodeTo.asExpr().(AutoClosureExpr).getExpr()
|
||||
or
|
||||
// flow through the read of a content that inherits taint
|
||||
exists(DataFlow::ContentSet f |
|
||||
readStep(nodeFrom, f, nodeTo) and
|
||||
f.getAReadContent() instanceof TaintInheritingContent
|
||||
)
|
||||
predicate defaultAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, string model) {
|
||||
(
|
||||
// Flow through one argument of `appendLiteral` and `appendInterpolation` and to the second argument.
|
||||
// This is needed for string interpolation generated by the compiler. An interpolated string
|
||||
// like `"I am \(n) years old."` is represented as
|
||||
// ```
|
||||
// $interpolated = ""
|
||||
// appendLiteral(&$interpolated, "I am ")
|
||||
// appendInterpolation(&$interpolated, n)
|
||||
// appendLiteral(&$interpolated, " years old.")
|
||||
// ```
|
||||
exists(ApplyExpr apply, ExprCfgNode e |
|
||||
nodeFrom.asExpr() = [apply.getAnArgument().getExpr(), apply.getQualifier()] and
|
||||
apply.getStaticTarget().getName() = ["appendLiteral(_:)", "appendInterpolation(_:)"] and
|
||||
e.getExpr() = apply.getQualifier() and
|
||||
nodeTo.(PostUpdateNodeImpl).getPreUpdateNode().getCfgNode() = e
|
||||
)
|
||||
or
|
||||
// Flow from the computation of the interpolated string literal to the result of the interpolation.
|
||||
exists(InterpolatedStringLiteralExpr interpolated |
|
||||
nodeTo.asExpr() = interpolated and
|
||||
nodeFrom.asExpr() = interpolated.getAppendingExpr()
|
||||
)
|
||||
or
|
||||
// allow flow through arithmetic (this case includes string concatenation)
|
||||
nodeTo.asExpr().(ArithmeticOperation).getAnOperand() = nodeFrom.asExpr()
|
||||
or
|
||||
// allow flow through bitwise operations
|
||||
nodeTo.asExpr().(BitwiseOperation).getAnOperand() = nodeFrom.asExpr()
|
||||
or
|
||||
// allow flow through assignment operations (e.g. `+=`)
|
||||
exists(AssignOperation op |
|
||||
nodeFrom.asExpr() = op.getSource() and
|
||||
nodeTo.asExpr() = op.getDest()
|
||||
)
|
||||
or
|
||||
// flow through a subscript access
|
||||
exists(SubscriptExpr se |
|
||||
se.getBase() = nodeFrom.asExpr() and
|
||||
se = nodeTo.asExpr()
|
||||
)
|
||||
or
|
||||
// flow through autoclosure expressions (which turn value arguments into closure arguments);
|
||||
// if the value is tainted, it's helpful to consider the autoclosure itself to be tainted as
|
||||
// well for the purposes of matching sink models.
|
||||
nodeFrom.asExpr() = nodeTo.asExpr().(AutoClosureExpr).getExpr()
|
||||
or
|
||||
// flow through the read of a content that inherits taint
|
||||
exists(DataFlow::ContentSet f |
|
||||
readStep(nodeFrom, f, nodeTo) and
|
||||
f.getAReadContent() instanceof TaintInheritingContent
|
||||
)
|
||||
) and
|
||||
model = ""
|
||||
or
|
||||
// flow through a flow summary (extension of `SummaryModelCsv`)
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode(), false)
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode(), false, model)
|
||||
or
|
||||
any(AdditionalTaintStep a).step(nodeFrom, nodeTo)
|
||||
any(AdditionalTaintStep a).step(nodeFrom, nodeTo) and model = "AdditionalTaintStep"
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,7 +90,7 @@ private module Cached {
|
||||
predicate localTaintStepCached(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
DataFlow::localFlowStep(nodeFrom, nodeTo)
|
||||
or
|
||||
defaultAdditionalTaintStep(nodeFrom, nodeTo)
|
||||
defaultAdditionalTaintStep(nodeFrom, nodeTo, _)
|
||||
or
|
||||
// Simple flow through library code is included in the exposed local
|
||||
// step relation, even though flow is technically inter-procedural
|
||||
|
||||
Reference in New Issue
Block a user