Merge branch 'main' into felicitymay-9916-update-links

This commit is contained in:
Felicity Chapman
2023-05-03 11:05:10 +01:00
committed by GitHub
296 changed files with 19394 additions and 8695 deletions

View File

@@ -40,7 +40,6 @@
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll",
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl1.qll",
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl2.qll",
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImplForStringsNewReplacer.qll",

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The new dataflow (`semmle.code.cpp.dataflow.new.DataFlow`) and taint-tracking libraries (`semmle.code.cpp.dataflow.new.TaintTracking`) now support tracking flow through static local variables.

File diff suppressed because it is too large Load Diff

View File

@@ -815,24 +815,20 @@ private module Cached {
)
}
private predicate store(
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
exists(ContentSet cs |
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
)
}
/**
* Holds if data can flow from `node1` to `node2` via a direct assignment to
* `f`.
* `c`.
*
* This includes reverse steps through reads when the result of the read has
* been stored into, in order to handle cases like `x.f1.f2 = y`.
*/
cached
predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) {
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
predicate store(
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
exists(ContentSet cs |
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
)
}
/**
@@ -932,36 +928,15 @@ private module Cached {
TReturnCtxNoFlowThrough() or
TReturnCtxMaybeFlowThrough(ReturnPosition pos)
cached
newtype TTypedContentApprox =
MkTypedContentApprox(ContentApprox c, DataFlowType t) {
exists(Content cont |
c = getContentApprox(cont) and
store(_, cont, _, _, t)
)
}
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
cached
TypedContent getATypedContent(TypedContentApprox c) {
exists(ContentApprox cls, DataFlowType t, Content cont |
c = MkTypedContentApprox(cls, pragma[only_bind_into](t)) and
result = MkTypedContent(cont, pragma[only_bind_into](t)) and
cls = getContentApprox(cont)
)
}
cached
newtype TAccessPathFront =
TFrontNil(DataFlowType t) or
TFrontHead(TypedContent tc)
TFrontNil() or
TFrontHead(Content c)
cached
newtype TApproxAccessPathFront =
TApproxFrontNil(DataFlowType t) or
TApproxFrontHead(TypedContentApprox tc)
TApproxFrontNil() or
TApproxFrontHead(ContentApprox c)
cached
newtype TAccessPathFrontOption =
@@ -986,8 +961,16 @@ predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
/**
* A `Node` at which a cast can occur such that the type should be checked.
*/
class CastingNode extends Node {
class CastingNode instanceof Node {
CastingNode() { castingNode(this) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
private predicate readStepWithTypes(
@@ -1135,9 +1118,17 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable)
* The value of a parameter at function entry, viewed as a node in a data
* flow graph.
*/
class ParamNode extends Node {
class ParamNode instanceof Node {
ParamNode() { parameterNode(this, _, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/**
* Holds if this node is the parameter of callable `c` at the specified
* position.
@@ -1146,9 +1137,17 @@ class ParamNode extends Node {
}
/** A data-flow node that represents a call argument. */
class ArgNode extends Node {
class ArgNode instanceof Node {
ArgNode() { argumentNode(this, _, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Holds if this argument occurs at the given position in the given call. */
final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
argumentNode(this, call, pos)
@@ -1159,9 +1158,17 @@ class ArgNode extends Node {
* A node from which flow can return to the caller. This is either a regular
* `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter.
*/
class ReturnNodeExt extends Node {
class ReturnNodeExt instanceof Node {
ReturnNodeExt() { returnNodeExt(this, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the kind of this returned value. */
ReturnKindExt getKind() { returnNodeExt(this, result) }
}
@@ -1170,8 +1177,16 @@ class ReturnNodeExt extends Node {
* A node to which data can flow from a call. Either an ordinary out node
* or a post-update node associated with a call argument.
*/
class OutNodeExt extends Node {
class OutNodeExt instanceof Node {
OutNodeExt() { outNodeExt(this) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
/**
@@ -1387,67 +1402,37 @@ class ReturnCtx extends TReturnCtx {
}
}
/** An approximated `Content` tagged with the type of a containing object. */
class TypedContentApprox extends MkTypedContentApprox {
private ContentApprox c;
private DataFlowType t;
TypedContentApprox() { this = MkTypedContentApprox(c, t) }
/** Gets a typed content approximated by this value. */
TypedContent getATypedContent() { result = getATypedContent(this) }
/** Gets the content. */
ContentApprox getContent() { result = c }
/** Gets the container type. */
DataFlowType getContainerType() { result = t }
/** Gets a textual representation of this approximated content. */
string toString() { result = c.toString() }
}
/**
* The front of an approximated access path. This is either a head or a nil.
*/
abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
abstract string toString();
abstract DataFlowType getType();
abstract boolean toBoolNonEmpty();
TypedContentApprox getHead() { this = TApproxFrontHead(result) }
ContentApprox getHead() { this = TApproxFrontHead(result) }
pragma[nomagic]
TypedContent getAHead() {
exists(TypedContentApprox cont |
Content getAHead() {
exists(ContentApprox cont |
this = TApproxFrontHead(cont) and
result = cont.getATypedContent()
cont = getContentApprox(result)
)
}
}
class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil {
private DataFlowType t;
ApproxAccessPathFrontNil() { this = TApproxFrontNil(t) }
override string toString() { result = ppReprType(t) }
override DataFlowType getType() { result = t }
override string toString() { result = "nil" }
override boolean toBoolNonEmpty() { result = false }
}
class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead {
private TypedContentApprox tc;
private ContentApprox c;
ApproxAccessPathFrontHead() { this = TApproxFrontHead(tc) }
ApproxAccessPathFrontHead() { this = TApproxFrontHead(c) }
override string toString() { result = tc.toString() }
override DataFlowType getType() { result = tc.getContainerType() }
override string toString() { result = c.toString() }
override boolean toBoolNonEmpty() { result = true }
}
@@ -1461,65 +1446,31 @@ class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption {
}
}
/** A `Content` tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;
private DataFlowType t;
TypedContent() { this = MkTypedContent(c, t) }
/** Gets the content. */
Content getContent() { result = c }
/** Gets the container type. */
DataFlowType getContainerType() { result = t }
/** Gets a textual representation of this content. */
string toString() { result = c.toString() }
/**
* Holds if access paths with this `TypedContent` at their head always should
* be tracked at high precision. This disables adaptive access path precision
* for such access paths.
*/
predicate forceHighPrecision() { forceHighPrecision(c) }
}
/**
* The front of an access path. This is either a head or a nil.
*/
abstract class AccessPathFront extends TAccessPathFront {
abstract string toString();
abstract DataFlowType getType();
abstract ApproxAccessPathFront toApprox();
TypedContent getHead() { this = TFrontHead(result) }
Content getHead() { this = TFrontHead(result) }
}
class AccessPathFrontNil extends AccessPathFront, TFrontNil {
private DataFlowType t;
override string toString() { result = "nil" }
AccessPathFrontNil() { this = TFrontNil(t) }
override string toString() { result = ppReprType(t) }
override DataFlowType getType() { result = t }
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil(t) }
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil() }
}
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
private TypedContent tc;
private Content c;
AccessPathFrontHead() { this = TFrontHead(tc) }
AccessPathFrontHead() { this = TFrontHead(c) }
override string toString() { result = tc.toString() }
override string toString() { result = c.toString() }
override DataFlowType getType() { result = tc.getContainerType() }
override ApproxAccessPathFront toApprox() { result.getAHead() = tc }
override ApproxAccessPathFront toApprox() { result.getAHead() = c }
}
/** An optional access path front. */

View File

@@ -815,24 +815,20 @@ private module Cached {
)
}
private predicate store(
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
exists(ContentSet cs |
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
)
}
/**
* Holds if data can flow from `node1` to `node2` via a direct assignment to
* `f`.
* `c`.
*
* This includes reverse steps through reads when the result of the read has
* been stored into, in order to handle cases like `x.f1.f2 = y`.
*/
cached
predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) {
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
predicate store(
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
exists(ContentSet cs |
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
)
}
/**
@@ -932,36 +928,15 @@ private module Cached {
TReturnCtxNoFlowThrough() or
TReturnCtxMaybeFlowThrough(ReturnPosition pos)
cached
newtype TTypedContentApprox =
MkTypedContentApprox(ContentApprox c, DataFlowType t) {
exists(Content cont |
c = getContentApprox(cont) and
store(_, cont, _, _, t)
)
}
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
cached
TypedContent getATypedContent(TypedContentApprox c) {
exists(ContentApprox cls, DataFlowType t, Content cont |
c = MkTypedContentApprox(cls, pragma[only_bind_into](t)) and
result = MkTypedContent(cont, pragma[only_bind_into](t)) and
cls = getContentApprox(cont)
)
}
cached
newtype TAccessPathFront =
TFrontNil(DataFlowType t) or
TFrontHead(TypedContent tc)
TFrontNil() or
TFrontHead(Content c)
cached
newtype TApproxAccessPathFront =
TApproxFrontNil(DataFlowType t) or
TApproxFrontHead(TypedContentApprox tc)
TApproxFrontNil() or
TApproxFrontHead(ContentApprox c)
cached
newtype TAccessPathFrontOption =
@@ -986,8 +961,16 @@ predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
/**
* A `Node` at which a cast can occur such that the type should be checked.
*/
class CastingNode extends Node {
class CastingNode instanceof Node {
CastingNode() { castingNode(this) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
private predicate readStepWithTypes(
@@ -1135,9 +1118,17 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable)
* The value of a parameter at function entry, viewed as a node in a data
* flow graph.
*/
class ParamNode extends Node {
class ParamNode instanceof Node {
ParamNode() { parameterNode(this, _, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/**
* Holds if this node is the parameter of callable `c` at the specified
* position.
@@ -1146,9 +1137,17 @@ class ParamNode extends Node {
}
/** A data-flow node that represents a call argument. */
class ArgNode extends Node {
class ArgNode instanceof Node {
ArgNode() { argumentNode(this, _, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Holds if this argument occurs at the given position in the given call. */
final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
argumentNode(this, call, pos)
@@ -1159,9 +1158,17 @@ class ArgNode extends Node {
* A node from which flow can return to the caller. This is either a regular
* `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter.
*/
class ReturnNodeExt extends Node {
class ReturnNodeExt instanceof Node {
ReturnNodeExt() { returnNodeExt(this, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the kind of this returned value. */
ReturnKindExt getKind() { returnNodeExt(this, result) }
}
@@ -1170,8 +1177,16 @@ class ReturnNodeExt extends Node {
* A node to which data can flow from a call. Either an ordinary out node
* or a post-update node associated with a call argument.
*/
class OutNodeExt extends Node {
class OutNodeExt instanceof Node {
OutNodeExt() { outNodeExt(this) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
/**
@@ -1387,67 +1402,37 @@ class ReturnCtx extends TReturnCtx {
}
}
/** An approximated `Content` tagged with the type of a containing object. */
class TypedContentApprox extends MkTypedContentApprox {
private ContentApprox c;
private DataFlowType t;
TypedContentApprox() { this = MkTypedContentApprox(c, t) }
/** Gets a typed content approximated by this value. */
TypedContent getATypedContent() { result = getATypedContent(this) }
/** Gets the content. */
ContentApprox getContent() { result = c }
/** Gets the container type. */
DataFlowType getContainerType() { result = t }
/** Gets a textual representation of this approximated content. */
string toString() { result = c.toString() }
}
/**
* The front of an approximated access path. This is either a head or a nil.
*/
abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
abstract string toString();
abstract DataFlowType getType();
abstract boolean toBoolNonEmpty();
TypedContentApprox getHead() { this = TApproxFrontHead(result) }
ContentApprox getHead() { this = TApproxFrontHead(result) }
pragma[nomagic]
TypedContent getAHead() {
exists(TypedContentApprox cont |
Content getAHead() {
exists(ContentApprox cont |
this = TApproxFrontHead(cont) and
result = cont.getATypedContent()
cont = getContentApprox(result)
)
}
}
class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil {
private DataFlowType t;
ApproxAccessPathFrontNil() { this = TApproxFrontNil(t) }
override string toString() { result = ppReprType(t) }
override DataFlowType getType() { result = t }
override string toString() { result = "nil" }
override boolean toBoolNonEmpty() { result = false }
}
class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead {
private TypedContentApprox tc;
private ContentApprox c;
ApproxAccessPathFrontHead() { this = TApproxFrontHead(tc) }
ApproxAccessPathFrontHead() { this = TApproxFrontHead(c) }
override string toString() { result = tc.toString() }
override DataFlowType getType() { result = tc.getContainerType() }
override string toString() { result = c.toString() }
override boolean toBoolNonEmpty() { result = true }
}
@@ -1461,65 +1446,31 @@ class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption {
}
}
/** A `Content` tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;
private DataFlowType t;
TypedContent() { this = MkTypedContent(c, t) }
/** Gets the content. */
Content getContent() { result = c }
/** Gets the container type. */
DataFlowType getContainerType() { result = t }
/** Gets a textual representation of this content. */
string toString() { result = c.toString() }
/**
* Holds if access paths with this `TypedContent` at their head always should
* be tracked at high precision. This disables adaptive access path precision
* for such access paths.
*/
predicate forceHighPrecision() { forceHighPrecision(c) }
}
/**
* The front of an access path. This is either a head or a nil.
*/
abstract class AccessPathFront extends TAccessPathFront {
abstract string toString();
abstract DataFlowType getType();
abstract ApproxAccessPathFront toApprox();
TypedContent getHead() { this = TFrontHead(result) }
Content getHead() { this = TFrontHead(result) }
}
class AccessPathFrontNil extends AccessPathFront, TFrontNil {
private DataFlowType t;
override string toString() { result = "nil" }
AccessPathFrontNil() { this = TFrontNil(t) }
override string toString() { result = ppReprType(t) }
override DataFlowType getType() { result = t }
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil(t) }
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil() }
}
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
private TypedContent tc;
private Content c;
AccessPathFrontHead() { this = TFrontHead(tc) }
AccessPathFrontHead() { this = TFrontHead(c) }
override string toString() { result = tc.toString() }
override string toString() { result = c.toString() }
override DataFlowType getType() { result = tc.getContainerType() }
override ApproxAccessPathFront toApprox() { result.getAHead() = tc }
override ApproxAccessPathFront toApprox() { result.getAHead() = c }
}
/** An optional access path front. */

View File

@@ -607,13 +607,21 @@ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
result.getReturnKind() = kind
}
/** A variable that behaves like a global variable. */
class GlobalLikeVariable extends Variable {
GlobalLikeVariable() {
this instanceof Cpp::GlobalOrNamespaceVariable or
this instanceof Cpp::StaticLocalVariable
}
}
/**
* Holds if data can flow from `node1` to `node2` in a way that loses the
* calling context. For example, this would happen with flow through a
* global or static variable.
*/
predicate jumpStep(Node n1, Node n2) {
exists(Cpp::GlobalOrNamespaceVariable v |
exists(GlobalLikeVariable v |
exists(Ssa::GlobalUse globalUse |
v = globalUse.getVariable() and
n1.(FinalGlobalValue).getGlobalUse() = globalUse

View File

@@ -145,14 +145,14 @@ private newtype TDefOrUseImpl =
or
// Since the pruning stage doesn't know about global variables we can't use the above check to
// rule out dead assignments to globals.
base.(VariableAddressInstruction).getAstVariable() instanceof Cpp::GlobalOrNamespaceVariable
base.(VariableAddressInstruction).getAstVariable() instanceof GlobalLikeVariable
)
} or
TUseImpl(Operand operand, int indirectionIndex) {
isUse(_, operand, _, _, indirectionIndex) and
not isDef(_, _, operand, _, _, _)
} or
TGlobalUse(Cpp::GlobalOrNamespaceVariable v, IRFunction f, int indirectionIndex) {
TGlobalUse(GlobalLikeVariable v, IRFunction f, int indirectionIndex) {
// Represents a final "use" of a global variable to ensure that
// the assignment to a global variable isn't ruled out as dead.
exists(VariableAddressInstruction vai, int defIndex |
@@ -162,7 +162,7 @@ private newtype TDefOrUseImpl =
indirectionIndex = [0 .. defIndex] + 1
)
} or
TGlobalDefImpl(Cpp::GlobalOrNamespaceVariable v, IRFunction f, int indirectionIndex) {
TGlobalDefImpl(GlobalLikeVariable v, IRFunction f, int indirectionIndex) {
// Represents the initial "definition" of a global variable when entering
// a function body.
exists(VariableAddressInstruction vai |
@@ -458,7 +458,7 @@ class FinalParameterUse extends UseImpl, TFinalParameterUse {
}
class GlobalUse extends UseImpl, TGlobalUse {
Cpp::GlobalOrNamespaceVariable global;
GlobalLikeVariable global;
IRFunction f;
GlobalUse() { this = TGlobalUse(global, f, ind) }
@@ -468,7 +468,7 @@ class GlobalUse extends UseImpl, TGlobalUse {
override int getIndirection() { result = ind + 1 }
/** Gets the global variable associated with this use. */
Cpp::GlobalOrNamespaceVariable getVariable() { result = global }
GlobalLikeVariable getVariable() { result = global }
/** Gets the `IRFunction` whose body is exited from after this use. */
IRFunction getIRFunction() { result = f }
@@ -496,14 +496,14 @@ class GlobalUse extends UseImpl, TGlobalUse {
}
class GlobalDefImpl extends DefOrUseImpl, TGlobalDefImpl {
Cpp::GlobalOrNamespaceVariable global;
GlobalLikeVariable global;
IRFunction f;
int indirectionIndex;
GlobalDefImpl() { this = TGlobalDefImpl(global, f, indirectionIndex) }
/** Gets the global variable associated with this definition. */
Cpp::GlobalOrNamespaceVariable getVariable() { result = global }
GlobalLikeVariable getVariable() { result = global }
/** Gets the `IRFunction` whose body is evaluated after this definition. */
IRFunction getIRFunction() { result = f }
@@ -760,13 +760,14 @@ private predicate variableWriteCand(IRBlock bb, int i, SourceVariable v) {
}
private predicate sourceVariableIsGlobal(
SourceVariable sv, Cpp::GlobalOrNamespaceVariable global, IRFunction func, int indirectionIndex
SourceVariable sv, GlobalLikeVariable global, IRFunction func, int indirectionIndex
) {
exists(IRVariable irVar, BaseIRVariable base |
sourceVariableHasBaseAndIndex(sv, base, indirectionIndex) and
irVar = base.getIRVariable() and
irVar.getEnclosingIRFunction() = func and
global = irVar.getAst()
global = irVar.getAst() and
not irVar instanceof IRDynamicInitializationFlag
)
}
@@ -919,7 +920,7 @@ class GlobalDef extends TGlobalDef, SsaDefOrUse {
IRFunction getIRFunction() { result = global.getIRFunction() }
/** Gets the global variable associated with this definition. */
Cpp::GlobalOrNamespaceVariable getVariable() { result = global.getVariable() }
GlobalLikeVariable getVariable() { result = global.getVariable() }
}
class Phi extends TPhi, SsaDefOrUse {

View File

@@ -821,7 +821,7 @@ abstract class TranslatedElement extends TTranslatedElement {
abstract Locatable getAst();
/** DEPRECATED: Alias for getAst */
deprecated Locatable getAST() { result = getAst() }
deprecated Locatable getAST() { result = this.getAst() }
/**
* Get the first instruction to be executed in the evaluation of this element.
@@ -831,7 +831,7 @@ abstract class TranslatedElement extends TTranslatedElement {
/**
* Get the immediate child elements of this element.
*/
final TranslatedElement getAChild() { result = getChild(_) }
final TranslatedElement getAChild() { result = this.getChild(_) }
/**
* Gets the immediate child element of this element. The `id` is unique
@@ -844,25 +844,29 @@ abstract class TranslatedElement extends TTranslatedElement {
* Gets the an identifier string for the element. This id is unique within
* the scope of the element's function.
*/
final int getId() { result = getUniqueId() }
final int getId() { result = this.getUniqueId() }
private TranslatedElement getChildByRank(int rankIndex) {
result =
rank[rankIndex + 1](TranslatedElement child, int id | child = getChild(id) | child order by id)
rank[rankIndex + 1](TranslatedElement child, int id |
child = this.getChild(id)
|
child order by id
)
}
language[monotonicAggregates]
private int getDescendantCount() {
result =
1 + sum(TranslatedElement child | child = getChildByRank(_) | child.getDescendantCount())
1 + sum(TranslatedElement child | child = this.getChildByRank(_) | child.getDescendantCount())
}
private int getUniqueId() {
if not exists(getParent())
if not exists(this.getParent())
then result = 0
else
exists(TranslatedElement parent |
parent = getParent() and
parent = this.getParent() and
if this = parent.getChildByRank(0)
then result = 1 + parent.getUniqueId()
else
@@ -908,7 +912,7 @@ abstract class TranslatedElement extends TTranslatedElement {
* there is no enclosing `try`.
*/
Instruction getExceptionSuccessorInstruction() {
result = getParent().getExceptionSuccessorInstruction()
result = this.getParent().getExceptionSuccessorInstruction()
}
/**
@@ -1022,14 +1026,14 @@ abstract class TranslatedElement extends TTranslatedElement {
exists(Locatable ast |
result.getAst() = ast and
result.getTag() = tag and
hasTempVariableAndAst(tag, ast)
this.hasTempVariableAndAst(tag, ast)
)
}
pragma[noinline]
private predicate hasTempVariableAndAst(TempVariableTag tag, Locatable ast) {
hasTempVariable(tag, _) and
ast = getAst()
this.hasTempVariable(tag, _) and
ast = this.getAst()
}
/**

View File

@@ -35,64 +35,64 @@ abstract class InitializationContext extends TranslatedElement {
* declarations, `return` statements, and `throw` expressions.
*/
abstract class TranslatedVariableInitialization extends TranslatedElement, InitializationContext {
final override TranslatedElement getChild(int id) { id = 0 and result = getInitialization() }
final override TranslatedElement getChild(int id) { id = 0 and result = this.getInitialization() }
final override Instruction getFirstInstruction() {
result = getInstruction(InitializerVariableAddressTag())
result = this.getInstruction(InitializerVariableAddressTag())
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = InitializerVariableAddressTag() and
opcode instanceof Opcode::VariableAddress and
resultType = getTypeForGLValue(getTargetType())
resultType = getTypeForGLValue(this.getTargetType())
or
hasUninitializedInstruction() and
this.hasUninitializedInstruction() and
tag = InitializerStoreTag() and
opcode instanceof Opcode::Uninitialized and
resultType = getTypeForPRValue(getTargetType())
resultType = getTypeForPRValue(this.getTargetType())
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
(
tag = InitializerVariableAddressTag() and
kind instanceof GotoEdge and
if hasUninitializedInstruction()
then result = getInstruction(InitializerStoreTag())
else result = getInitialization().getFirstInstruction()
if this.hasUninitializedInstruction()
then result = this.getInstruction(InitializerStoreTag())
else result = this.getInitialization().getFirstInstruction()
)
or
hasUninitializedInstruction() and
this.hasUninitializedInstruction() and
kind instanceof GotoEdge and
tag = InitializerStoreTag() and
(
result = getInitialization().getFirstInstruction()
result = this.getInitialization().getFirstInstruction()
or
not exists(getInitialization()) and result = getInitializationSuccessor()
not exists(this.getInitialization()) and result = this.getInitializationSuccessor()
)
}
final override Instruction getChildSuccessor(TranslatedElement child) {
child = getInitialization() and result = getInitializationSuccessor()
child = this.getInitialization() and result = this.getInitializationSuccessor()
}
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
hasUninitializedInstruction() and
this.hasUninitializedInstruction() and
tag = InitializerStoreTag() and
operandTag instanceof AddressOperandTag and
result = getInstruction(InitializerVariableAddressTag())
result = this.getInstruction(InitializerVariableAddressTag())
}
final override IRVariable getInstructionVariable(InstructionTag tag) {
(
tag = InitializerVariableAddressTag()
or
hasUninitializedInstruction() and tag = InitializerStoreTag()
this.hasUninitializedInstruction() and tag = InitializerStoreTag()
) and
result = getIRVariable()
result = this.getIRVariable()
}
final override Instruction getTargetAddress() {
result = getInstruction(InitializerVariableAddressTag())
result = this.getInstruction(InitializerVariableAddressTag())
}
/**
@@ -116,13 +116,13 @@ abstract class TranslatedVariableInitialization extends TranslatedElement, Initi
*/
final predicate hasUninitializedInstruction() {
(
not exists(getInitialization()) or
getInitialization() instanceof TranslatedListInitialization or
getInitialization() instanceof TranslatedConstructorInitialization or
getInitialization().(TranslatedStringLiteralInitialization).zeroInitRange(_, _)
not exists(this.getInitialization()) or
this.getInitialization() instanceof TranslatedListInitialization or
this.getInitialization() instanceof TranslatedConstructorInitialization or
this.getInitialization().(TranslatedStringLiteralInitialization).zeroInitRange(_, _)
) and
// Variables with static or thread-local storage duration are zero-initialized at program startup.
getIRVariable() instanceof IRAutomaticVariable
this.getIRVariable() instanceof IRAutomaticVariable
}
}
@@ -146,7 +146,7 @@ abstract class TranslatedInitialization extends TranslatedElement, TTranslatedIn
final override Locatable getAst() { result = expr }
/** DEPRECATED: Alias for getAst */
deprecated override Locatable getAST() { result = getAst() }
deprecated override Locatable getAST() { result = this.getAst() }
/**
* Gets the expression that is doing the initialization.
@@ -157,7 +157,7 @@ abstract class TranslatedInitialization extends TranslatedElement, TTranslatedIn
* Gets the initialization context that describes the location being
* initialized.
*/
final InitializationContext getContext() { result = getParent() }
final InitializationContext getContext() { result = this.getParent() }
final TranslatedFunction getEnclosingFunction() {
result = getTranslatedFunction(this.getFunction())
@@ -169,17 +169,17 @@ abstract class TranslatedInitialization extends TranslatedElement, TTranslatedIn
*/
abstract class TranslatedListInitialization extends TranslatedInitialization, InitializationContext {
override Instruction getFirstInstruction() {
result = getChild(0).getFirstInstruction()
result = this.getChild(0).getFirstInstruction()
or
not exists(getChild(0)) and result = getParent().getChildSuccessor(this)
not exists(this.getChild(0)) and result = this.getParent().getChildSuccessor(this)
}
override Instruction getChildSuccessor(TranslatedElement child) {
exists(int index |
child = getChild(index) and
if exists(getChild(index + 1))
then result = getChild(index + 1).getFirstInstruction()
else result = getParent().getChildSuccessor(this)
child = this.getChild(index) and
if exists(this.getChild(index + 1))
then result = this.getChild(index + 1).getFirstInstruction()
else result = this.getParent().getChildSuccessor(this)
)
}
@@ -189,9 +189,9 @@ abstract class TranslatedListInitialization extends TranslatedInitialization, In
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
override Instruction getTargetAddress() { result = getContext().getTargetAddress() }
override Instruction getTargetAddress() { result = this.getContext().getTargetAddress() }
override Type getTargetType() { result = getContext().getTargetType() }
override Type getTargetType() { result = this.getContext().getTargetType() }
}
/**
@@ -237,9 +237,11 @@ class TranslatedArrayListInitialization extends TranslatedListInitialization {
abstract class TranslatedDirectInitialization extends TranslatedInitialization {
TranslatedDirectInitialization() { not expr instanceof AggregateLiteral }
override TranslatedElement getChild(int id) { id = 0 and result = getInitializer() }
override TranslatedElement getChild(int id) { id = 0 and result = this.getInitializer() }
override Instruction getFirstInstruction() { result = getInitializer().getFirstInstruction() }
override Instruction getFirstInstruction() {
result = this.getInitializer().getFirstInstruction()
}
final TranslatedExpr getInitializer() { result = getTranslatedExpr(expr) }
}
@@ -258,27 +260,27 @@ class TranslatedSimpleDirectInitialization extends TranslatedDirectInitializatio
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = InitializerStoreTag() and
opcode instanceof Opcode::Store and
resultType = getTypeForPRValue(getContext().getTargetType())
resultType = getTypeForPRValue(this.getContext().getTargetType())
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = InitializerStoreTag() and
result = getParent().getChildSuccessor(this) and
result = this.getParent().getChildSuccessor(this) and
kind instanceof GotoEdge
}
override Instruction getChildSuccessor(TranslatedElement child) {
child = getInitializer() and result = getInstruction(InitializerStoreTag())
child = this.getInitializer() and result = this.getInstruction(InitializerStoreTag())
}
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = InitializerStoreTag() and
(
operandTag instanceof AddressOperandTag and
result = getContext().getTargetAddress()
result = this.getContext().getTargetAddress()
or
operandTag instanceof StoreValueOperandTag and
result = getInitializer().getResult()
result = this.getInitializer().getResult()
)
}
}
@@ -305,13 +307,13 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati
// If the initializer string isn't large enough to fill the target, then
// we have to generate another instruction sequence to store a constant
// zero into the remainder of the array.
zeroInitRange(_, elementCount) and
this.zeroInitRange(_, elementCount) and
(
// Create a constant zero whose size is the size of the remaining
// space in the target array.
tag = ZeroPadStringConstantTag() and
opcode instanceof Opcode::Constant and
resultType = getUnknownOpaqueType(elementCount * getElementType().getSize())
resultType = getUnknownOpaqueType(elementCount * this.getElementType().getSize())
or
// The index of the first element to be zero initialized.
tag = ZeroPadStringElementIndexTag() and
@@ -321,12 +323,12 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati
// Compute the address of the first element to be zero initialized.
tag = ZeroPadStringElementAddressTag() and
opcode instanceof Opcode::PointerAdd and
resultType = getTypeForGLValue(getElementType())
resultType = getTypeForGLValue(this.getElementType())
or
// Store the constant zero into the remainder of the string.
tag = ZeroPadStringStoreTag() and
opcode instanceof Opcode::Store and
resultType = getUnknownOpaqueType(elementCount * getElementType().getSize())
resultType = getUnknownOpaqueType(elementCount * this.getElementType().getSize())
)
)
}
@@ -335,78 +337,78 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati
kind instanceof GotoEdge and
(
tag = InitializerLoadStringTag() and
result = getInstruction(InitializerStoreTag())
result = this.getInstruction(InitializerStoreTag())
or
if zeroInitRange(_, _)
if this.zeroInitRange(_, _)
then (
tag = InitializerStoreTag() and
result = getInstruction(ZeroPadStringConstantTag())
result = this.getInstruction(ZeroPadStringConstantTag())
or
tag = ZeroPadStringConstantTag() and
result = getInstruction(ZeroPadStringElementIndexTag())
result = this.getInstruction(ZeroPadStringElementIndexTag())
or
tag = ZeroPadStringElementIndexTag() and
result = getInstruction(ZeroPadStringElementAddressTag())
result = this.getInstruction(ZeroPadStringElementAddressTag())
or
tag = ZeroPadStringElementAddressTag() and
result = getInstruction(ZeroPadStringStoreTag())
result = this.getInstruction(ZeroPadStringStoreTag())
or
tag = ZeroPadStringStoreTag() and
result = getParent().getChildSuccessor(this)
result = this.getParent().getChildSuccessor(this)
) else (
tag = InitializerStoreTag() and
result = getParent().getChildSuccessor(this)
result = this.getParent().getChildSuccessor(this)
)
)
}
override Instruction getChildSuccessor(TranslatedElement child) {
child = getInitializer() and result = getInstruction(InitializerLoadStringTag())
child = this.getInitializer() and result = this.getInstruction(InitializerLoadStringTag())
}
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = InitializerLoadStringTag() and
(
operandTag instanceof AddressOperandTag and
result = getInitializer().getResult()
result = this.getInitializer().getResult()
)
or
tag = InitializerStoreTag() and
(
operandTag instanceof AddressOperandTag and
result = getContext().getTargetAddress()
result = this.getContext().getTargetAddress()
or
operandTag instanceof StoreValueOperandTag and
result = getInstruction(InitializerLoadStringTag())
result = this.getInstruction(InitializerLoadStringTag())
)
or
tag = ZeroPadStringElementAddressTag() and
(
operandTag instanceof LeftOperandTag and
result = getContext().getTargetAddress()
result = this.getContext().getTargetAddress()
or
operandTag instanceof RightOperandTag and
result = getInstruction(ZeroPadStringElementIndexTag())
result = this.getInstruction(ZeroPadStringElementIndexTag())
)
or
tag = ZeroPadStringStoreTag() and
(
operandTag instanceof AddressOperandTag and
result = getInstruction(ZeroPadStringElementAddressTag())
result = this.getInstruction(ZeroPadStringElementAddressTag())
or
operandTag instanceof StoreValueOperandTag and
result = getInstruction(ZeroPadStringConstantTag())
result = this.getInstruction(ZeroPadStringConstantTag())
)
}
override int getInstructionElementSize(InstructionTag tag) {
tag = ZeroPadStringElementAddressTag() and
result = max(getElementType().getSize())
result = max(this.getElementType().getSize())
}
override string getInstructionConstantValue(InstructionTag tag) {
exists(int startIndex |
zeroInitRange(startIndex, _) and
this.zeroInitRange(startIndex, _) and
(
tag = ZeroPadStringConstantTag() and
result = "0"
@@ -419,13 +421,13 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati
override predicate needsUnknownOpaqueType(int byteSize) {
exists(int elementCount |
zeroInitRange(_, elementCount) and
byteSize = elementCount * getElementType().getSize()
this.zeroInitRange(_, elementCount) and
byteSize = elementCount * this.getElementType().getSize()
)
}
private Type getElementType() {
result = getContext().getTargetType().getUnspecifiedType().(ArrayType).getBaseType()
result = this.getContext().getTargetType().getUnspecifiedType().(ArrayType).getBaseType()
}
/**
@@ -435,7 +437,8 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati
predicate zeroInitRange(int startIndex, int elementCount) {
exists(int targetCount |
startIndex = expr.getUnspecifiedType().(ArrayType).getArraySize() and
targetCount = getContext().getTargetType().getUnspecifiedType().(ArrayType).getArraySize() and
targetCount =
this.getContext().getTargetType().getUnspecifiedType().(ArrayType).getArraySize() and
elementCount = targetCount - startIndex and
elementCount > 0
)
@@ -454,14 +457,14 @@ class TranslatedConstructorInitialization extends TranslatedDirectInitialization
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
override Instruction getChildSuccessor(TranslatedElement child) {
child = getInitializer() and result = getParent().getChildSuccessor(this)
child = this.getInitializer() and result = this.getParent().getChildSuccessor(this)
}
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
none()
}
override Instruction getReceiver() { result = getContext().getTargetAddress() }
override Instruction getReceiver() { result = this.getContext().getTargetAddress() }
}
/**
@@ -491,7 +494,7 @@ abstract class TranslatedFieldInitialization extends TranslatedElement {
final override Locatable getAst() { result = ast }
/** DEPRECATED: Alias for getAst */
deprecated override Locatable getAST() { result = getAst() }
deprecated override Locatable getAST() { result = this.getAst() }
final override Declaration getFunction() {
result = getEnclosingFunction(ast) or
@@ -499,7 +502,9 @@ abstract class TranslatedFieldInitialization extends TranslatedElement {
result = getEnclosingVariable(ast).(StaticInitializedStaticLocalVariable)
}
final override Instruction getFirstInstruction() { result = getInstruction(getFieldAddressTag()) }
final override Instruction getFirstInstruction() {
result = this.getInstruction(this.getFieldAddressTag())
}
/**
* Gets the zero-based index describing the order in which this field is to be
@@ -508,19 +513,19 @@ abstract class TranslatedFieldInitialization extends TranslatedElement {
final int getOrder() { result = field.getInitializationOrder() }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = getFieldAddressTag() and
tag = this.getFieldAddressTag() and
opcode instanceof Opcode::FieldAddress and
resultType = getTypeForGLValue(field.getType())
}
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = getFieldAddressTag() and
tag = this.getFieldAddressTag() and
operandTag instanceof UnaryOperandTag and
result = getParent().(InitializationContext).getTargetAddress()
result = this.getParent().(InitializationContext).getTargetAddress()
}
override Field getInstructionField(InstructionTag tag) {
tag = getFieldAddressTag() and result = field
tag = this.getFieldAddressTag() and result = field
}
final InstructionTag getFieldAddressTag() { result = InitializerFieldAddressTag() }
@@ -545,21 +550,23 @@ class TranslatedExplicitFieldInitialization extends TranslatedFieldInitializatio
this = TTranslatedExplicitFieldInitialization(ast, field, expr, position)
}
override Instruction getTargetAddress() { result = getInstruction(getFieldAddressTag()) }
override Instruction getTargetAddress() {
result = this.getInstruction(this.getFieldAddressTag())
}
override Type getTargetType() { result = field.getUnspecifiedType() }
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = getFieldAddressTag() and
result = getInitialization().getFirstInstruction() and
tag = this.getFieldAddressTag() and
result = this.getInitialization().getFirstInstruction() and
kind instanceof GotoEdge
}
override Instruction getChildSuccessor(TranslatedElement child) {
child = getInitialization() and result = getParent().getChildSuccessor(this)
child = this.getInitialization() and result = this.getParent().getChildSuccessor(this)
}
override TranslatedElement getChild(int id) { id = 0 and result = getInitialization() }
override TranslatedElement getChild(int id) { id = 0 and result = this.getInitialization() }
private TranslatedInitialization getInitialization() {
result = getTranslatedInitialization(expr)
@@ -584,11 +591,11 @@ class TranslatedFieldValueInitialization extends TranslatedFieldInitialization,
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
TranslatedFieldInitialization.super.hasInstruction(opcode, tag, resultType)
or
tag = getFieldDefaultValueTag() and
tag = this.getFieldDefaultValueTag() and
opcode instanceof Opcode::Constant and
resultType = getTypeForPRValue(field.getType())
or
tag = getFieldDefaultValueStoreTag() and
tag = this.getFieldDefaultValueStoreTag() and
opcode instanceof Opcode::Store and
resultType = getTypeForPRValue(field.getUnspecifiedType())
}
@@ -596,32 +603,32 @@ class TranslatedFieldValueInitialization extends TranslatedFieldInitialization,
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
kind instanceof GotoEdge and
(
tag = getFieldAddressTag() and
result = getInstruction(getFieldDefaultValueTag())
tag = this.getFieldAddressTag() and
result = this.getInstruction(this.getFieldDefaultValueTag())
or
tag = getFieldDefaultValueTag() and
result = getInstruction(getFieldDefaultValueStoreTag())
tag = this.getFieldDefaultValueTag() and
result = this.getInstruction(this.getFieldDefaultValueStoreTag())
or
tag = getFieldDefaultValueStoreTag() and
result = getParent().getChildSuccessor(this)
tag = this.getFieldDefaultValueStoreTag() and
result = this.getParent().getChildSuccessor(this)
)
}
override string getInstructionConstantValue(InstructionTag tag) {
tag = getFieldDefaultValueTag() and
tag = this.getFieldDefaultValueTag() and
result = getZeroValue(field.getUnspecifiedType())
}
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
result = TranslatedFieldInitialization.super.getInstructionRegisterOperand(tag, operandTag)
or
tag = getFieldDefaultValueStoreTag() and
tag = this.getFieldDefaultValueStoreTag() and
(
operandTag instanceof AddressOperandTag and
result = getInstruction(getFieldAddressTag())
result = this.getInstruction(this.getFieldAddressTag())
or
operandTag instanceof StoreValueOperandTag and
result = getInstruction(getFieldDefaultValueTag())
result = this.getInstruction(this.getFieldDefaultValueTag())
)
}
@@ -644,13 +651,13 @@ abstract class TranslatedElementInitialization extends TranslatedElement {
ArrayOrVectorAggregateLiteral initList;
final override string toString() {
result = initList.toString() + "[" + getElementIndex().toString() + "]"
result = initList.toString() + "[" + this.getElementIndex().toString() + "]"
}
final override Locatable getAst() { result = initList }
/** DEPRECATED: Alias for getAst */
deprecated override Locatable getAST() { result = getAst() }
deprecated override Locatable getAST() { result = this.getAst() }
final override Declaration getFunction() {
result = getEnclosingFunction(initList)
@@ -660,43 +667,45 @@ abstract class TranslatedElementInitialization extends TranslatedElement {
result = getEnclosingVariable(initList).(StaticInitializedStaticLocalVariable)
}
final override Instruction getFirstInstruction() { result = getInstruction(getElementIndexTag()) }
final override Instruction getFirstInstruction() {
result = this.getInstruction(this.getElementIndexTag())
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = getElementIndexTag() and
tag = this.getElementIndexTag() and
opcode instanceof Opcode::Constant and
resultType = getIntType()
or
tag = getElementAddressTag() and
tag = this.getElementAddressTag() and
opcode instanceof Opcode::PointerAdd and
resultType = getTypeForGLValue(getElementType())
resultType = getTypeForGLValue(this.getElementType())
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = getElementIndexTag() and
result = getInstruction(getElementAddressTag()) and
tag = this.getElementIndexTag() and
result = this.getInstruction(this.getElementAddressTag()) and
kind instanceof GotoEdge
}
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = getElementAddressTag() and
tag = this.getElementAddressTag() and
(
operandTag instanceof LeftOperandTag and
result = getParent().(InitializationContext).getTargetAddress()
result = this.getParent().(InitializationContext).getTargetAddress()
or
operandTag instanceof RightOperandTag and
result = getInstruction(getElementIndexTag())
result = this.getInstruction(this.getElementIndexTag())
)
}
override int getInstructionElementSize(InstructionTag tag) {
tag = getElementAddressTag() and
result = max(getElementType().getSize())
tag = this.getElementAddressTag() and
result = max(this.getElementType().getSize())
}
override string getInstructionConstantValue(InstructionTag tag) {
tag = getElementIndexTag() and
result = getElementIndex().toString()
tag = this.getElementIndexTag() and
result = this.getElementIndex().toString()
}
abstract int getElementIndex();
@@ -726,23 +735,25 @@ class TranslatedExplicitElementInitialization extends TranslatedElementInitializ
this = TTranslatedExplicitElementInitialization(initList, elementIndex, position)
}
override Instruction getTargetAddress() { result = getInstruction(getElementAddressTag()) }
override Instruction getTargetAddress() {
result = this.getInstruction(this.getElementAddressTag())
}
override Type getTargetType() { result = getElementType() }
override Type getTargetType() { result = this.getElementType() }
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
result = TranslatedElementInitialization.super.getInstructionSuccessor(tag, kind)
or
tag = getElementAddressTag() and
result = getInitialization().getFirstInstruction() and
tag = this.getElementAddressTag() and
result = this.getInitialization().getFirstInstruction() and
kind instanceof GotoEdge
}
override Instruction getChildSuccessor(TranslatedElement child) {
child = getInitialization() and result = getParent().getChildSuccessor(this)
child = this.getInitialization() and result = this.getParent().getChildSuccessor(this)
}
override TranslatedElement getChild(int id) { id = 0 and result = getInitialization() }
override TranslatedElement getChild(int id) { id = 0 and result = this.getInitialization() }
override int getElementIndex() { result = elementIndex }
@@ -773,13 +784,13 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
TranslatedElementInitialization.super.hasInstruction(opcode, tag, resultType)
or
tag = getElementDefaultValueTag() and
tag = this.getElementDefaultValueTag() and
opcode instanceof Opcode::Constant and
resultType = getDefaultValueType()
resultType = this.getDefaultValueType()
or
tag = getElementDefaultValueStoreTag() and
tag = this.getElementDefaultValueStoreTag() and
opcode instanceof Opcode::Store and
resultType = getDefaultValueType()
resultType = this.getDefaultValueType()
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -787,34 +798,34 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati
or
kind instanceof GotoEdge and
(
tag = getElementAddressTag() and
result = getInstruction(getElementDefaultValueTag())
tag = this.getElementAddressTag() and
result = this.getInstruction(this.getElementDefaultValueTag())
or
tag = getElementDefaultValueTag() and
result = getInstruction(getElementDefaultValueStoreTag())
tag = this.getElementDefaultValueTag() and
result = this.getInstruction(this.getElementDefaultValueStoreTag())
or
tag = getElementDefaultValueStoreTag() and
result = getParent().getChildSuccessor(this)
tag = this.getElementDefaultValueStoreTag() and
result = this.getParent().getChildSuccessor(this)
)
}
override string getInstructionConstantValue(InstructionTag tag) {
result = TranslatedElementInitialization.super.getInstructionConstantValue(tag)
or
tag = getElementDefaultValueTag() and
result = getZeroValue(getElementType())
tag = this.getElementDefaultValueTag() and
result = getZeroValue(this.getElementType())
}
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
result = TranslatedElementInitialization.super.getInstructionRegisterOperand(tag, operandTag)
or
tag = getElementDefaultValueStoreTag() and
tag = this.getElementDefaultValueStoreTag() and
(
operandTag instanceof AddressOperandTag and
result = getInstruction(getElementAddressTag())
result = this.getInstruction(this.getElementAddressTag())
or
operandTag instanceof StoreValueOperandTag and
result = getInstruction(getElementDefaultValueTag())
result = this.getInstruction(this.getElementDefaultValueTag())
)
}
@@ -825,7 +836,7 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati
override int getElementIndex() { result = elementIndex }
override predicate needsUnknownOpaqueType(int byteSize) {
elementCount != 0 and byteSize = elementCount * getElementType().getSize()
elementCount != 0 and byteSize = elementCount * this.getElementType().getSize()
}
private InstructionTag getElementDefaultValueTag() {
@@ -838,8 +849,8 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati
private CppType getDefaultValueType() {
if elementCount = 1
then result = getTypeForPRValue(getElementType())
else result = getUnknownOpaqueType(elementCount * getElementType().getSize())
then result = getTypeForPRValue(this.getElementType())
else result = getUnknownOpaqueType(elementCount * this.getElementType().getSize())
}
}
@@ -849,18 +860,18 @@ abstract class TranslatedStructorCallFromStructor extends TranslatedElement, Str
final override Locatable getAst() { result = call }
/** DEPRECATED: Alias for getAst */
deprecated override Locatable getAST() { result = getAst() }
deprecated override Locatable getAST() { result = this.getAst() }
final override TranslatedElement getChild(int id) {
id = 0 and
result = getStructorCall()
result = this.getStructorCall()
}
final override Function getFunction() { result = getEnclosingFunction(call) }
final override Instruction getChildSuccessor(TranslatedElement child) {
child = getStructorCall() and
result = getParent().getChildSuccessor(this)
child = this.getStructorCall() and
result = this.getParent().getChildSuccessor(this)
}
final TranslatedExpr getStructorCall() { result = getTranslatedExpr(call) }
@@ -871,7 +882,9 @@ abstract class TranslatedStructorCallFromStructor extends TranslatedElement, Str
* destructor from within a derived class constructor or destructor.
*/
abstract class TranslatedBaseStructorCall extends TranslatedStructorCallFromStructor {
final override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
final override Instruction getFirstInstruction() {
result = this.getInstruction(OnlyInstructionTag())
}
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
@@ -882,15 +895,15 @@ abstract class TranslatedBaseStructorCall extends TranslatedStructorCallFromStru
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = OnlyInstructionTag() and
kind instanceof GotoEdge and
result = getStructorCall().getFirstInstruction()
result = this.getStructorCall().getFirstInstruction()
}
final override Instruction getReceiver() { result = getInstruction(OnlyInstructionTag()) }
final override Instruction getReceiver() { result = this.getInstruction(OnlyInstructionTag()) }
final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = OnlyInstructionTag() and
operandTag instanceof UnaryOperandTag and
result = getTranslatedFunction(getFunction()).getInitializeThisInstruction()
result = getTranslatedFunction(this.getFunction()).getInitializeThisInstruction()
}
final override predicate getInstructionInheritance(
@@ -898,7 +911,7 @@ abstract class TranslatedBaseStructorCall extends TranslatedStructorCallFromStru
) {
tag = OnlyInstructionTag() and
baseClass = call.getTarget().getDeclaringType().getUnspecifiedType() and
derivedClass = getFunction().getDeclaringType().getUnspecifiedType()
derivedClass = this.getFunction().getDeclaringType().getUnspecifiedType()
}
}
@@ -924,7 +937,7 @@ class TranslatedConstructorDelegationInit extends TranslatedConstructorCallFromC
final override string toString() { result = "delegation construct: " + call.toString() }
final override Instruction getFirstInstruction() {
result = getStructorCall().getFirstInstruction()
result = this.getStructorCall().getFirstInstruction()
}
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
@@ -934,7 +947,7 @@ class TranslatedConstructorDelegationInit extends TranslatedConstructorCallFromC
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
final override Instruction getReceiver() {
result = getTranslatedFunction(getFunction()).getInitializeThisInstruction()
result = getTranslatedFunction(this.getFunction()).getInitializeThisInstruction()
}
}
@@ -981,11 +994,11 @@ class TranslatedConstructorBareInit extends TranslatedElement, TTranslatedConstr
override Locatable getAst() { result = init }
/** DEPRECATED: Alias for getAst */
deprecated override Locatable getAST() { result = getAst() }
deprecated override Locatable getAST() { result = this.getAst() }
final override string toString() { result = "construct base (no constructor)" }
override Instruction getFirstInstruction() { result = getParent().getChildSuccessor(this) }
override Instruction getFirstInstruction() { result = this.getParent().getChildSuccessor(this) }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
none()

View File

@@ -240,7 +240,7 @@ abstract class TranslatedStmt extends TranslatedElement, TTranslatedStmt {
final override Locatable getAst() { result = stmt }
/** DEPRECATED: Alias for getAst */
deprecated override Locatable getAST() { result = getAst() }
deprecated override Locatable getAST() { result = this.getAst() }
final override Function getFunction() { result = stmt.getEnclosingFunction() }
}
@@ -254,7 +254,7 @@ class TranslatedEmptyStmt extends TranslatedStmt {
override TranslatedElement getChild(int id) { none() }
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
override Instruction getFirstInstruction() { result = this.getInstruction(OnlyInstructionTag()) }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
@@ -264,7 +264,7 @@ class TranslatedEmptyStmt extends TranslatedStmt {
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = OnlyInstructionTag() and
result = getParent().getChildSuccessor(this) and
result = this.getParent().getChildSuccessor(this) and
kind instanceof GotoEdge
}
@@ -279,19 +279,19 @@ class TranslatedEmptyStmt extends TranslatedStmt {
class TranslatedDeclStmt extends TranslatedStmt {
override DeclStmt stmt;
override TranslatedElement getChild(int id) { result = getDeclarationEntry(id) }
override TranslatedElement getChild(int id) { result = this.getDeclarationEntry(id) }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
none()
}
override Instruction getFirstInstruction() {
result = getDeclarationEntry(0).getFirstInstruction()
result = this.getDeclarationEntry(0).getFirstInstruction()
or
not exists(getDeclarationEntry(0)) and result = getParent().getChildSuccessor(this)
not exists(this.getDeclarationEntry(0)) and result = this.getParent().getChildSuccessor(this)
}
private int getChildCount() { result = count(getDeclarationEntry(_)) }
private int getChildCount() { result = count(this.getDeclarationEntry(_)) }
IRDeclarationEntry getIRDeclarationEntry(int index) {
result.hasIndex(index) and
@@ -319,10 +319,10 @@ class TranslatedDeclStmt extends TranslatedStmt {
override Instruction getChildSuccessor(TranslatedElement child) {
exists(int index |
child = getDeclarationEntry(index) and
if index = (getChildCount() - 1)
then result = getParent().getChildSuccessor(this)
else result = getDeclarationEntry(index + 1).getFirstInstruction()
child = this.getDeclarationEntry(index) and
if index = (this.getChildCount() - 1)
then result = this.getParent().getChildSuccessor(this)
else result = this.getDeclarationEntry(index + 1).getFirstInstruction()
)
}
}
@@ -332,19 +332,19 @@ class TranslatedExprStmt extends TranslatedStmt {
TranslatedExpr getExpr() { result = getTranslatedExpr(stmt.getExpr().getFullyConverted()) }
override TranslatedElement getChild(int id) { id = 0 and result = getExpr() }
override TranslatedElement getChild(int id) { id = 0 and result = this.getExpr() }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
none()
}
override Instruction getFirstInstruction() { result = getExpr().getFirstInstruction() }
override Instruction getFirstInstruction() { result = this.getExpr().getFirstInstruction() }
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
override Instruction getChildSuccessor(TranslatedElement child) {
child = getExpr() and
result = getParent().getChildSuccessor(this)
child = this.getExpr() and
result = this.getParent().getChildSuccessor(this)
}
}
@@ -363,16 +363,18 @@ class TranslatedReturnValueStmt extends TranslatedReturnStmt, TranslatedVariable
TranslatedReturnValueStmt() { stmt.hasExpr() and hasReturnValue(stmt.getEnclosingFunction()) }
final override Instruction getInitializationSuccessor() {
result = getEnclosingFunction().getReturnSuccessorInstruction()
result = this.getEnclosingFunction().getReturnSuccessorInstruction()
}
final override Type getTargetType() { result = getEnclosingFunction().getReturnType() }
final override Type getTargetType() { result = this.getEnclosingFunction().getReturnType() }
final override TranslatedInitialization getInitialization() {
result = getTranslatedInitialization(stmt.getExpr().getFullyConverted())
}
final override IRVariable getIRVariable() { result = getEnclosingFunction().getReturnVariable() }
final override IRVariable getIRVariable() {
result = this.getEnclosingFunction().getReturnVariable()
}
}
/**
@@ -385,10 +387,10 @@ class TranslatedReturnVoidExpressionStmt extends TranslatedReturnStmt {
override TranslatedElement getChild(int id) {
id = 0 and
result = getExpr()
result = this.getExpr()
}
override Instruction getFirstInstruction() { result = getExpr().getFirstInstruction() }
override Instruction getFirstInstruction() { result = this.getExpr().getFirstInstruction() }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
@@ -398,13 +400,13 @@ class TranslatedReturnVoidExpressionStmt extends TranslatedReturnStmt {
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = OnlyInstructionTag() and
result = getEnclosingFunction().getReturnSuccessorInstruction() and
result = this.getEnclosingFunction().getReturnSuccessorInstruction() and
kind instanceof GotoEdge
}
override Instruction getChildSuccessor(TranslatedElement child) {
child = getExpr() and
result = getInstruction(OnlyInstructionTag())
child = this.getExpr() and
result = this.getInstruction(OnlyInstructionTag())
}
private TranslatedExpr getExpr() { result = getTranslatedExpr(stmt.getExpr()) }
@@ -421,7 +423,7 @@ class TranslatedReturnVoidStmt extends TranslatedReturnStmt {
override TranslatedElement getChild(int id) { none() }
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
override Instruction getFirstInstruction() { result = this.getInstruction(OnlyInstructionTag()) }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
@@ -431,7 +433,7 @@ class TranslatedReturnVoidStmt extends TranslatedReturnStmt {
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = OnlyInstructionTag() and
result = getEnclosingFunction().getReturnSuccessorInstruction() and
result = this.getEnclosingFunction().getReturnSuccessorInstruction() and
kind instanceof GotoEdge
}
@@ -452,7 +454,7 @@ class TranslatedUnreachableReturnStmt extends TranslatedReturnStmt {
override TranslatedElement getChild(int id) { none() }
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
override Instruction getFirstInstruction() { result = this.getInstruction(OnlyInstructionTag()) }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
@@ -511,9 +513,9 @@ class TranslatedTryStmt extends TranslatedStmt {
override TryOrMicrosoftTryStmt stmt;
override TranslatedElement getChild(int id) {
id = 0 and result = getBody()
id = 0 and result = this.getBody()
or
result = getHandler(id - 1)
result = this.getHandler(id - 1)
or
id = stmt.getNumberOfCatchClauses() + 1 and
result = this.getFinally()
@@ -525,7 +527,7 @@ class TranslatedTryStmt extends TranslatedStmt {
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
override Instruction getFirstInstruction() { result = getBody().getFirstInstruction() }
override Instruction getFirstInstruction() { result = this.getBody().getFirstInstruction() }
override Instruction getChildSuccessor(TranslatedElement child) {
// All non-finally children go to the successor of the `try` if
@@ -546,19 +548,19 @@ class TranslatedTryStmt extends TranslatedStmt {
final Instruction getNextHandler(TranslatedHandler handler) {
exists(int index |
handler = getHandler(index) and
result = getHandler(index + 1).getFirstInstruction()
handler = this.getHandler(index) and
result = this.getHandler(index + 1).getFirstInstruction()
)
or
// The last catch clause flows to the exception successor of the parent
// of the `try`, because the exception successor of the `try` itself is
// the first catch clause.
handler = getHandler(stmt.getNumberOfCatchClauses() - 1) and
result = getParent().getExceptionSuccessorInstruction()
handler = this.getHandler(stmt.getNumberOfCatchClauses() - 1) and
result = this.getParent().getExceptionSuccessorInstruction()
}
final override Instruction getExceptionSuccessorInstruction() {
result = getHandler(0).getFirstInstruction()
result = this.getHandler(0).getFirstInstruction()
}
private TranslatedElement getHandler(int index) { result = stmt.getTranslatedHandler(index) }
@@ -571,19 +573,19 @@ class TranslatedTryStmt extends TranslatedStmt {
class TranslatedBlock extends TranslatedStmt {
override BlockStmt stmt;
override TranslatedElement getChild(int id) { result = getStmt(id) }
override TranslatedElement getChild(int id) { result = this.getStmt(id) }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
isEmpty() and
this.isEmpty() and
opcode instanceof Opcode::NoOp and
tag = OnlyInstructionTag() and
resultType = getVoidType()
}
override Instruction getFirstInstruction() {
if isEmpty()
then result = getInstruction(OnlyInstructionTag())
else result = getStmt(0).getFirstInstruction()
if this.isEmpty()
then result = this.getInstruction(OnlyInstructionTag())
else result = this.getStmt(0).getFirstInstruction()
}
private predicate isEmpty() { not exists(stmt.getStmt(0)) }
@@ -594,16 +596,16 @@ class TranslatedBlock extends TranslatedStmt {
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = OnlyInstructionTag() and
result = getParent().getChildSuccessor(this) and
result = this.getParent().getChildSuccessor(this) and
kind instanceof GotoEdge
}
override Instruction getChildSuccessor(TranslatedElement child) {
exists(int index |
child = getStmt(index) and
if index = (getStmtCount() - 1)
then result = getParent().getChildSuccessor(this)
else result = getStmt(index + 1).getFirstInstruction()
child = this.getStmt(index) and
if index = (this.getStmtCount() - 1)
then result = this.getParent().getChildSuccessor(this)
else result = this.getStmt(index + 1).getFirstInstruction()
)
}
}
@@ -614,18 +616,18 @@ class TranslatedBlock extends TranslatedStmt {
abstract class TranslatedHandler extends TranslatedStmt {
override Handler stmt;
override TranslatedElement getChild(int id) { id = 1 and result = getBlock() }
override TranslatedElement getChild(int id) { id = 1 and result = this.getBlock() }
override Instruction getFirstInstruction() { result = getInstruction(CatchTag()) }
override Instruction getFirstInstruction() { result = this.getInstruction(CatchTag()) }
override Instruction getChildSuccessor(TranslatedElement child) {
child = getBlock() and result = getParent().getChildSuccessor(this)
child = this.getBlock() and result = this.getParent().getChildSuccessor(this)
}
override Instruction getExceptionSuccessorInstruction() {
// A throw from within a `catch` block flows to the handler for the parent of
// the `try`.
result = getParent().getParent().getExceptionSuccessorInstruction()
result = this.getParent().getParent().getExceptionSuccessorInstruction()
}
TranslatedStmt getBlock() { result = getTranslatedStmt(stmt.getBlock()) }
@@ -647,23 +649,23 @@ class TranslatedCatchByTypeHandler extends TranslatedHandler {
override TranslatedElement getChild(int id) {
result = super.getChild(id)
or
id = 0 and result = getParameter()
id = 0 and result = this.getParameter()
}
override Instruction getChildSuccessor(TranslatedElement child) {
result = super.getChildSuccessor(child)
or
child = getParameter() and result = getBlock().getFirstInstruction()
child = this.getParameter() and result = this.getBlock().getFirstInstruction()
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = CatchTag() and
(
kind instanceof GotoEdge and
result = getParameter().getFirstInstruction()
result = this.getParameter().getFirstInstruction()
or
kind instanceof ExceptionEdge and
result = getParent().(TranslatedTryStmt).getNextHandler(this)
result = this.getParent().(TranslatedTryStmt).getNextHandler(this)
)
}
@@ -692,7 +694,7 @@ class TranslatedCatchAnyHandler extends TranslatedHandler {
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = CatchTag() and
kind instanceof GotoEdge and
result = getBlock().getFirstInstruction()
result = this.getBlock().getFirstInstruction()
}
}
@@ -700,19 +702,19 @@ class TranslatedIfStmt extends TranslatedStmt, ConditionContext {
override IfStmt stmt;
override Instruction getFirstInstruction() {
if hasInitialization()
then result = getInitialization().getFirstInstruction()
else result = getFirstConditionInstruction()
if this.hasInitialization()
then result = this.getInitialization().getFirstInstruction()
else result = this.getFirstConditionInstruction()
}
override TranslatedElement getChild(int id) {
id = 0 and result = getInitialization()
id = 0 and result = this.getInitialization()
or
id = 1 and result = getCondition()
id = 1 and result = this.getCondition()
or
id = 2 and result = getThen()
id = 2 and result = this.getThen()
or
id = 3 and result = getElse()
id = 3 and result = this.getElse()
}
private predicate hasInitialization() { exists(stmt.getInitialization()) }
@@ -726,7 +728,7 @@ class TranslatedIfStmt extends TranslatedStmt, ConditionContext {
}
private Instruction getFirstConditionInstruction() {
result = getCondition().getFirstInstruction()
result = this.getCondition().getFirstInstruction()
}
private TranslatedStmt getThen() { result = getTranslatedStmt(stmt.getThen()) }
@@ -738,23 +740,23 @@ class TranslatedIfStmt extends TranslatedStmt, ConditionContext {
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
override Instruction getChildTrueSuccessor(TranslatedCondition child) {
child = getCondition() and
result = getThen().getFirstInstruction()
child = this.getCondition() and
result = this.getThen().getFirstInstruction()
}
override Instruction getChildFalseSuccessor(TranslatedCondition child) {
child = getCondition() and
if hasElse()
then result = getElse().getFirstInstruction()
else result = getParent().getChildSuccessor(this)
child = this.getCondition() and
if this.hasElse()
then result = this.getElse().getFirstInstruction()
else result = this.getParent().getChildSuccessor(this)
}
override Instruction getChildSuccessor(TranslatedElement child) {
child = getInitialization() and
result = getFirstConditionInstruction()
child = this.getInitialization() and
result = this.getFirstConditionInstruction()
or
(child = getThen() or child = getElse()) and
result = getParent().getChildSuccessor(this)
(child = this.getThen() or child = this.getElse()) and
result = this.getParent().getChildSuccessor(this)
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
@@ -772,17 +774,17 @@ abstract class TranslatedLoop extends TranslatedStmt, ConditionContext {
final TranslatedStmt getBody() { result = getTranslatedStmt(stmt.getStmt()) }
final Instruction getFirstConditionInstruction() {
if hasCondition()
then result = getCondition().getFirstInstruction()
else result = getBody().getFirstInstruction()
if this.hasCondition()
then result = this.getCondition().getFirstInstruction()
else result = this.getBody().getFirstInstruction()
}
final predicate hasCondition() { exists(stmt.getCondition()) }
override TranslatedElement getChild(int id) {
id = 0 and result = getCondition()
id = 0 and result = this.getCondition()
or
id = 1 and result = getBody()
id = 1 and result = this.getBody()
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
@@ -792,31 +794,31 @@ abstract class TranslatedLoop extends TranslatedStmt, ConditionContext {
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
final override Instruction getChildTrueSuccessor(TranslatedCondition child) {
child = getCondition() and result = getBody().getFirstInstruction()
child = this.getCondition() and result = this.getBody().getFirstInstruction()
}
final override Instruction getChildFalseSuccessor(TranslatedCondition child) {
child = getCondition() and result = getParent().getChildSuccessor(this)
child = this.getCondition() and result = this.getParent().getChildSuccessor(this)
}
}
class TranslatedWhileStmt extends TranslatedLoop {
TranslatedWhileStmt() { stmt instanceof WhileStmt }
override Instruction getFirstInstruction() { result = getFirstConditionInstruction() }
override Instruction getFirstInstruction() { result = this.getFirstConditionInstruction() }
override Instruction getChildSuccessor(TranslatedElement child) {
child = getBody() and result = getFirstConditionInstruction()
child = this.getBody() and result = this.getFirstConditionInstruction()
}
}
class TranslatedDoStmt extends TranslatedLoop {
TranslatedDoStmt() { stmt instanceof DoStmt }
override Instruction getFirstInstruction() { result = getBody().getFirstInstruction() }
override Instruction getFirstInstruction() { result = this.getBody().getFirstInstruction() }
override Instruction getChildSuccessor(TranslatedElement child) {
child = getBody() and result = getFirstConditionInstruction()
child = this.getBody() and result = this.getFirstConditionInstruction()
}
}
@@ -824,13 +826,13 @@ class TranslatedForStmt extends TranslatedLoop {
override ForStmt stmt;
override TranslatedElement getChild(int id) {
id = 0 and result = getInitialization()
id = 0 and result = this.getInitialization()
or
id = 1 and result = getCondition()
id = 1 and result = this.getCondition()
or
id = 2 and result = getUpdate()
id = 2 and result = this.getUpdate()
or
id = 3 and result = getBody()
id = 3 and result = this.getBody()
}
private TranslatedStmt getInitialization() {
@@ -844,23 +846,23 @@ class TranslatedForStmt extends TranslatedLoop {
private predicate hasUpdate() { exists(stmt.getUpdate()) }
override Instruction getFirstInstruction() {
if hasInitialization()
then result = getInitialization().getFirstInstruction()
else result = getFirstConditionInstruction()
if this.hasInitialization()
then result = this.getInitialization().getFirstInstruction()
else result = this.getFirstConditionInstruction()
}
override Instruction getChildSuccessor(TranslatedElement child) {
child = getInitialization() and
result = getFirstConditionInstruction()
child = this.getInitialization() and
result = this.getFirstConditionInstruction()
or
(
child = getBody() and
if hasUpdate()
then result = getUpdate().getFirstInstruction()
else result = getFirstConditionInstruction()
child = this.getBody() and
if this.hasUpdate()
then result = this.getUpdate().getFirstInstruction()
else result = this.getFirstConditionInstruction()
)
or
child = getUpdate() and result = getFirstConditionInstruction()
child = this.getUpdate() and result = this.getFirstConditionInstruction()
}
}
@@ -875,39 +877,39 @@ class TranslatedRangeBasedForStmt extends TranslatedStmt, ConditionContext {
override RangeBasedForStmt stmt;
override TranslatedElement getChild(int id) {
id = 0 and result = getRangeVariableDeclStmt()
id = 0 and result = this.getRangeVariableDeclStmt()
or
// Note: `__begin` and `__end` are declared by the same `DeclStmt`
id = 1 and result = getBeginEndVariableDeclStmt()
id = 1 and result = this.getBeginEndVariableDeclStmt()
or
id = 2 and result = getCondition()
id = 2 and result = this.getCondition()
or
id = 3 and result = getUpdate()
id = 3 and result = this.getUpdate()
or
id = 4 and result = getVariableDeclStmt()
id = 4 and result = this.getVariableDeclStmt()
or
id = 5 and result = getBody()
id = 5 and result = this.getBody()
}
override Instruction getFirstInstruction() {
result = getRangeVariableDeclStmt().getFirstInstruction()
result = this.getRangeVariableDeclStmt().getFirstInstruction()
}
override Instruction getChildSuccessor(TranslatedElement child) {
child = getRangeVariableDeclStmt() and
result = getBeginEndVariableDeclStmt().getFirstInstruction()
child = this.getRangeVariableDeclStmt() and
result = this.getBeginEndVariableDeclStmt().getFirstInstruction()
or
child = getBeginEndVariableDeclStmt() and
result = getCondition().getFirstInstruction()
child = this.getBeginEndVariableDeclStmt() and
result = this.getCondition().getFirstInstruction()
or
child = getVariableDeclStmt() and
result = getBody().getFirstInstruction()
child = this.getVariableDeclStmt() and
result = this.getBody().getFirstInstruction()
or
child = getBody() and
result = getUpdate().getFirstInstruction()
child = this.getBody() and
result = this.getUpdate().getFirstInstruction()
or
child = getUpdate() and
result = getCondition().getFirstInstruction()
child = this.getUpdate() and
result = this.getCondition().getFirstInstruction()
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
@@ -917,11 +919,11 @@ class TranslatedRangeBasedForStmt extends TranslatedStmt, ConditionContext {
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
override Instruction getChildTrueSuccessor(TranslatedCondition child) {
child = getCondition() and result = getVariableDeclStmt().getFirstInstruction()
child = this.getCondition() and result = this.getVariableDeclStmt().getFirstInstruction()
}
override Instruction getChildFalseSuccessor(TranslatedCondition child) {
child = getCondition() and result = getParent().getChildSuccessor(this)
child = this.getCondition() and result = this.getParent().getChildSuccessor(this)
}
private TranslatedDeclStmt getRangeVariableDeclStmt() {
@@ -961,7 +963,7 @@ class TranslatedRangeBasedForStmt extends TranslatedStmt, ConditionContext {
class TranslatedJumpStmt extends TranslatedStmt {
override JumpStmt stmt;
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
override Instruction getFirstInstruction() { result = this.getInstruction(OnlyInstructionTag()) }
override TranslatedElement getChild(int id) { none() }
@@ -996,22 +998,22 @@ class TranslatedSwitchStmt extends TranslatedStmt {
result = getTranslatedExpr(stmt.getExpr().getFullyConverted())
}
private Instruction getFirstExprInstruction() { result = getExpr().getFirstInstruction() }
private Instruction getFirstExprInstruction() { result = this.getExpr().getFirstInstruction() }
private TranslatedStmt getBody() { result = getTranslatedStmt(stmt.getStmt()) }
override Instruction getFirstInstruction() {
if hasInitialization()
then result = getInitialization().getFirstInstruction()
else result = getFirstExprInstruction()
if this.hasInitialization()
then result = this.getInitialization().getFirstInstruction()
else result = this.getFirstExprInstruction()
}
override TranslatedElement getChild(int id) {
id = 0 and result = getInitialization()
id = 0 and result = this.getInitialization()
or
id = 1 and result = getExpr()
id = 1 and result = this.getExpr()
or
id = 2 and result = getBody()
id = 2 and result = this.getBody()
}
private predicate hasInitialization() { exists(stmt.getInitialization()) }
@@ -1029,7 +1031,7 @@ class TranslatedSwitchStmt extends TranslatedStmt {
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = SwitchBranchTag() and
operandTag instanceof ConditionOperandTag and
result = getExpr().getResult()
result = this.getExpr().getResult()
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -1043,15 +1045,15 @@ class TranslatedSwitchStmt extends TranslatedStmt {
not stmt.hasDefaultCase() and
tag = SwitchBranchTag() and
kind instanceof DefaultEdge and
result = getParent().getChildSuccessor(this)
result = this.getParent().getChildSuccessor(this)
}
override Instruction getChildSuccessor(TranslatedElement child) {
child = getInitialization() and result = getFirstExprInstruction()
child = this.getInitialization() and result = this.getFirstExprInstruction()
or
child = getExpr() and result = getInstruction(SwitchBranchTag())
child = this.getExpr() and result = this.getInstruction(SwitchBranchTag())
or
child = getBody() and result = getParent().getChildSuccessor(this)
child = this.getBody() and result = this.getParent().getChildSuccessor(this)
}
}
@@ -1063,9 +1065,9 @@ class TranslatedAsmStmt extends TranslatedStmt {
}
override Instruction getFirstInstruction() {
if exists(getChild(0))
then result = getChild(0).getFirstInstruction()
else result = getInstruction(AsmTag())
if exists(this.getChild(0))
then result = this.getChild(0).getFirstInstruction()
else result = this.getInstruction(AsmTag())
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
@@ -1078,7 +1080,7 @@ class TranslatedAsmStmt extends TranslatedStmt {
exists(int index |
tag = AsmTag() and
operandTag = asmOperand(index) and
result = getChild(index).getResult()
result = this.getChild(index).getResult()
)
}
@@ -1092,16 +1094,16 @@ class TranslatedAsmStmt extends TranslatedStmt {
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = AsmTag() and
result = getParent().getChildSuccessor(this) and
result = this.getParent().getChildSuccessor(this) and
kind instanceof GotoEdge
}
override Instruction getChildSuccessor(TranslatedElement child) {
exists(int index |
child = getChild(index) and
if exists(getChild(index + 1))
then result = getChild(index + 1).getFirstInstruction()
else result = getInstruction(AsmTag())
child = this.getChild(index) and
if exists(this.getChild(index + 1))
then result = this.getChild(index + 1).getFirstInstruction()
else result = this.getInstruction(AsmTag())
)
}
}

View File

@@ -209,6 +209,9 @@ edges
| test.cpp:207:17:207:19 | str indirection [string] | test.cpp:207:22:207:27 | string |
| test.cpp:207:17:207:19 | str indirection [string] | test.cpp:207:22:207:27 | string indirection |
| test.cpp:207:22:207:27 | string indirection | test.cpp:207:22:207:27 | string |
| test.cpp:214:24:214:24 | p | test.cpp:216:10:216:10 | p |
| test.cpp:220:43:220:48 | call to malloc | test.cpp:222:15:222:20 | buffer |
| test.cpp:222:15:222:20 | buffer | test.cpp:214:24:214:24 | p |
nodes
| test.cpp:16:11:16:21 | mk_string_t indirection [string] | semmle.label | mk_string_t indirection [string] |
| test.cpp:18:5:18:30 | ... = ... | semmle.label | ... = ... |
@@ -374,6 +377,10 @@ nodes
| test.cpp:207:17:207:19 | str indirection [string] | semmle.label | str indirection [string] |
| test.cpp:207:22:207:27 | string | semmle.label | string |
| test.cpp:207:22:207:27 | string indirection | semmle.label | string indirection |
| test.cpp:214:24:214:24 | p | semmle.label | p |
| test.cpp:216:10:216:10 | p | semmle.label | p |
| test.cpp:220:43:220:48 | call to malloc | semmle.label | call to malloc |
| test.cpp:222:15:222:20 | buffer | semmle.label | buffer |
subpaths
#select
| test.cpp:42:5:42:11 | call to strncpy | test.cpp:18:19:18:24 | call to malloc | test.cpp:42:18:42:23 | string | This write may overflow $@ by 1 element. | test.cpp:42:18:42:23 | string | string |
@@ -391,3 +398,4 @@ subpaths
| test.cpp:199:9:199:15 | call to strncpy | test.cpp:147:19:147:24 | call to malloc | test.cpp:199:22:199:27 | string | This write may overflow $@ by 2 elements. | test.cpp:199:22:199:27 | string | string |
| test.cpp:203:9:203:15 | call to strncpy | test.cpp:147:19:147:24 | call to malloc | test.cpp:203:22:203:27 | string | This write may overflow $@ by 2 elements. | test.cpp:203:22:203:27 | string | string |
| test.cpp:207:9:207:15 | call to strncpy | test.cpp:147:19:147:24 | call to malloc | test.cpp:207:22:207:27 | string | This write may overflow $@ by 3 elements. | test.cpp:207:22:207:27 | string | string |
| test.cpp:216:3:216:8 | call to memset | test.cpp:220:43:220:48 | call to malloc | test.cpp:216:10:216:10 | p | This write may overflow $@ by 5 elements. | test.cpp:216:10:216:10 | p | p |

View File

@@ -208,3 +208,16 @@ void test5(unsigned size, char *buf, unsigned anotherSize) {
}
}
void *memset(void *, int, unsigned);
void call_memset(void *p, unsigned size)
{
memset(p, 0, size); // GOOD [FALSE POSITIVE]
}
void test_missing_call_context(unsigned char *unrelated_buffer, unsigned size) {
unsigned char* buffer = (unsigned char*)malloc(size);
call_memset(unrelated_buffer, size + 5);
call_memset(buffer, size);
}

View File

@@ -115,6 +115,16 @@ postWithInFlow
| test.cpp:602:3:602:7 | access to array [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:608:3:608:4 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:608:4:608:4 | p [inner post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:639:3:639:3 | x [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:646:3:646:3 | x [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:652:3:652:3 | x [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:653:3:653:3 | x [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:659:3:659:3 | x [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:660:3:660:3 | x [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:671:3:671:3 | s [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:681:3:681:3 | s [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:689:3:689:3 | s [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:690:3:690:3 | s [post update] | PostUpdateNode should not be the target of local flow. |
viableImplInCallContextTooLarge
uniqueParameterNodeAtPosition
uniqueParameterNodePosition

View File

@@ -627,4 +627,66 @@ void test_def_via_phi_read(bool b)
}
intPointerSource(buffer);
sink(buffer); // $ ast,ir
}
}
void test_static_local_1() {
static int x = source();
sink(x); // $ ast,ir
}
void test_static_local_2() {
static int x = source();
x = 0;
sink(x); // clean
}
void test_static_local_3() {
static int x = 0;
sink(x); // $ ir MISSING: ast
x = source();
}
void test_static_local_4() {
static int x = 0;
sink(x); // clean
x = source();
x = 0;
}
void test_static_local_5() {
static int x = 0;
sink(x); // $ ir MISSING: ast
x = 0;
x = source();
}
void test_static_local_6() {
static int s = source();
static int* ptr_to_s = &s;
sink(*ptr_to_s); // $ ir MISSING: ast
}
void test_static_local_7() {
static int s = source();
s = 0;
static int* ptr_to_s = &s;
sink(*ptr_to_s); // clean
}
void test_static_local_8() {
static int s;
static int* ptr_to_s = &s;
sink(*ptr_to_s); // $ ir MISSING: ast
s = source();
}
void test_static_local_9() {
static int s;
static int* ptr_to_s = &s;
sink(*ptr_to_s); // clean
s = source();
s = 0;
}

View File

@@ -4,7 +4,9 @@ uniqueType
uniqueNodeLocation
missingLocation
uniqueNodeToString
| cpp11.cpp:50:15:50:16 | (no string representation) | Node should have one toString but has 0. |
missingToString
| Nodes without toString: 1 |
parameterCallable
localFlowIsLocal
readStepIsLocal

View File

@@ -3,6 +3,7 @@
| nested.cpp:21:23:21:26 | fmt0 | The format string argument to snprintf should be constant to prevent security issues and other potential errors. |
| nested.cpp:79:32:79:38 | call to get_fmt | The format string argument to diagnostic should be constant to prevent security issues and other potential errors. |
| nested.cpp:87:18:87:20 | fmt | The format string argument to diagnostic should be constant to prevent security issues and other potential errors. |
| test.cpp:51:10:51:21 | call to make_message | The format string argument to printf should be constant to prevent security issues and other potential errors. |
| test.cpp:57:12:57:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
| test.cpp:60:12:60:21 | call to const_wash | The format string argument to printf should be constant to prevent security issues and other potential errors. |
| test.cpp:61:12:61:26 | ... + ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |

View File

@@ -48,7 +48,7 @@ int main(int argc, char **argv) {
printf(choose_message(argc - 1), argc - 1); // GOOD
printf(messages[1]); // GOOD
printf(message); // GOOD
printf(make_message(argc - 1)); // BAD [NOT DETECTED]
printf(make_message(argc - 1)); // BAD
printf("Hello, World\n"); // GOOD
printf(_("Hello, World\n")); // GOOD
{

View File

@@ -12,9 +12,9 @@ class Attribute extends Element, @cil_attribute {
Method getConstructor() { cil_attribute(this, _, result) }
/** Gets the type of this attribute. */
Type getType() { result = getConstructor().getDeclaringType() }
Type getType() { result = this.getConstructor().getDeclaringType() }
override string toString() { result = "[" + getType().getName() + "(...)]" }
override string toString() { result = "[" + this.getType().getName() + "(...)]" }
/** Gets the value of the `i`th argument of this attribute. */
string getArgument(int i) { cil_attribute_positional_argument(this, i, result) }
@@ -23,9 +23,9 @@ class Attribute extends Element, @cil_attribute {
string getNamedArgument(string name) { cil_attribute_named_argument(this, name, result) }
/** Gets an argument of this attribute, if any. */
string getAnArgument() { result = getArgument(_) or result = getNamedArgument(_) }
string getAnArgument() { result = this.getArgument(_) or result = this.getNamedArgument(_) }
override CS::Location getLocation() { result = getDeclaration().getLocation() }
override CS::Location getLocation() { result = this.getDeclaration().getLocation() }
}
/** A generic attribute to a declaration. */

View File

@@ -1,156 +1,183 @@
/**
* Provides classes for performing global (inter-procedural)
* content-sensitive data flow analyses.
*
* Unlike `DataFlow::Global`, we allow for data to be stored (possibly nested) inside
* contents of sources and sinks.
* We track flow paths of the form
*
* ```
* source --value-->* node
* (--read--> node --value-->* node)*
* --(non-value|value)-->* node
* (--store--> node --value-->* node)*
* --value-->* sink
* ```
*
* where `--value-->` is a value-preserving flow step, `--read-->` is a read
* step, `--store-->` is a store step, and `--(non-value)-->` is a
* non-value-preserving flow step.
*
* That is, first a sequence of 0 or more reads, followed by 0 or more additional
* steps, followed by 0 or more stores, with value-preserving steps allowed in
* between all other steps.
*/
private import csharp
private import codeql.util.Boolean
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
private import DataFlowImplSpecific::Private as DataFlowPrivate
module ContentDataFlow {
private import DataFlowImplSpecific::Private
private import DataFlowImplSpecific::Private as DataFlowPrivate
private import DataFlowImplForContentDataFlow as DF
class Node = DF::Node;
class FlowFeature = DF::FlowFeature;
class ContentSet = DF::ContentSet;
// predicate stageStats = DF::stageStats/8;
/**
* An input configuration for content data flow.
*/
signature module ConfigSig {
/**
* A configuration of interprocedural data flow analysis. This defines
* sources, sinks, and any other configurable aspect of the analysis. Each
* use of the global data flow library must define its own unique extension
* of this abstract class. To create a configuration, extend this class with
* a subclass whose characteristic predicate is a unique singleton string.
* For example, write
*
* ```ql
* class MyAnalysisConfiguration extends ContentDataFlowConfiguration {
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
* // Override `isSource` and `isSink`.
* // Optionally override `isBarrier`.
* // Optionally override `isAdditionalFlowStep`.
* // Optionally override `getAFeature`.
* // Optionally override `accessPathLimit`.
* // Optionally override `isRelevantContent`.
* }
* ```
*
* Unlike `DataFlow::Configuration` (on which this class is based), we allow
* for data to be stored (possibly nested) inside contents of sources and sinks.
* We track flow paths of the form
*
* ```
* source --value-->* node
* (--read--> node --value-->* node)*
* --(non-value|value)-->* node
* (--store--> node --value-->* node)*
* --value-->* sink
* ```
*
* where `--value-->` is a value-preserving flow step, `--read-->` is a read
* step, `--store-->` is a store step, and `--(non-value)-->` is a
* non-value-preserving flow step.
*
* That is, first a sequence of 0 or more reads, followed by 0 or more additional
* steps, followed by 0 or more stores, with value-preserving steps allowed in
* between all other steps.
* Holds if `source` is a relevant data flow source.
*/
abstract class Configuration extends string {
bindingset[this]
Configuration() { any() }
predicate isSource(DataFlow::Node source);
/**
* Holds if `source` is a relevant data flow source.
*/
abstract predicate isSource(Node source);
/**
* Holds if `sink` is a relevant data flow sink.
*/
predicate isSink(DataFlow::Node sink);
/**
* Holds if `sink` is a relevant data flow sink.
*/
abstract predicate isSink(Node sink);
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
*/
default predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
*/
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
/** Holds if data flow into `node` is prohibited. */
default predicate isBarrier(DataFlow::Node node) { none() }
/** Holds if data flow into `node` is prohibited. */
predicate isBarrier(Node node) { none() }
/**
* Gets a data flow configuration feature to add restrictions to the set of
* valid flow paths.
*
* - `FeatureHasSourceCallContext`:
* Assume that sources have some existing call context to disallow
* conflicting return-flow directly following the source.
* - `FeatureHasSinkCallContext`:
* Assume that sinks have some existing call context to disallow
* conflicting argument-to-parameter flow directly preceding the sink.
* - `FeatureEqualSourceSinkCallContext`:
* Implies both of the above and additionally ensures that the entire flow
* path preserves the call context.
*/
default DataFlow::FlowFeature getAFeature() { none() }
/**
* Gets a data flow configuration feature to add restrictions to the set of
* valid flow paths.
*
* - `FeatureHasSourceCallContext`:
* Assume that sources have some existing call context to disallow
* conflicting return-flow directly following the source.
* - `FeatureHasSinkCallContext`:
* Assume that sinks have some existing call context to disallow
* conflicting argument-to-parameter flow directly preceding the sink.
* - `FeatureEqualSourceSinkCallContext`:
* Implies both of the above and additionally ensures that the entire flow
* path preserves the call context.
*/
FlowFeature getAFeature() { none() }
/** Gets a limit on the number of reads out of sources and number of stores into sinks. */
default int accessPathLimit() { result = DataFlowPrivate::accessPathLimit() }
/** Gets a limit on the number of reads out of sources and number of stores into sinks. */
int accessPathLimit() { result = DataFlowPrivate::accessPathLimit() }
/** Holds if `c` is relevant for reads out of sources or stores into sinks. */
default predicate isRelevantContent(DataFlow::ContentSet c) { any() }
}
/** Holds if `c` is relevant for reads out of sources or stores into sinks. */
predicate isRelevantContent(ContentSet c) { any() }
/**
* Constructs a global content data flow computation.
*/
module Global<ConfigSig ContentConfig> {
private module FlowConfig implements DataFlow::StateConfigSig {
class FlowState = State;
/**
* Holds if data stored inside `sourceAp` on `source` flows to `sinkAp` inside `sink`
* for this configuration. `preservesValue` indicates whether any of the additional
* flow steps defined by `isAdditionalFlowStep` are needed.
*
* For the source access path, `sourceAp`, the top of the stack represents the content
* that was last read from. That is, if `sourceAp` is `Field1.Field2` (with `Field1`
* being the top of the stack), then there is flow from `source.Field2.Field1`.
*
* For the sink access path, `sinkAp`, the top of the stack represents the content
* that was last stored into. That is, if `sinkAp` is `Field1.Field2` (with `Field1`
* being the top of the stack), then there is flow into `sink.Field1.Field2`.
*/
final predicate hasFlow(
Node source, AccessPath sourceAp, Node sink, AccessPath sinkAp, boolean preservesValue
) {
exists(DF::PathNode pathSource, DF::PathNode pathSink |
this.(ConfigurationAdapter).hasFlowPath(pathSource, pathSink) and
nodeReaches(pathSource, TAccessPathNil(), TAccessPathNil(), pathSink, sourceAp, sinkAp) and
source = pathSource.getNode() and
sink = pathSink.getNode()
|
pathSink.getState().(InitState).decode(preservesValue)
or
pathSink.getState().(ReadState).decode(_, preservesValue)
or
pathSink.getState().(StoreState).decode(_, preservesValue)
predicate isSource(DataFlow::Node source, FlowState state) {
ContentConfig::isSource(source) and
state.(InitState).decode(true)
}
predicate isSink(DataFlow::Node sink, FlowState state) {
ContentConfig::isSink(sink) and
(
state instanceof InitState or
state instanceof StoreState or
state instanceof ReadState
)
}
predicate isAdditionalFlowStep(
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
) {
storeStep(node1, state1, _, node2, state2) or
readStep(node1, state1, _, node2, state2) or
additionalStep(node1, state1, node2, state2)
}
predicate isAdditionalFlowStep = ContentConfig::isAdditionalFlowStep/2;
predicate isBarrier = ContentConfig::isBarrier/1;
predicate isBarrier(DataFlow::Node node, FlowState state) { none() }
DataFlow::FlowFeature getAFeature() { result = ContentConfig::getAFeature() }
// needed to record reads/stores inside summarized callables
predicate includeHiddenNodes() { any() }
}
private module Flow = DataFlow::GlobalWithState<FlowConfig>;
/**
* Holds if data stored inside `sourceAp` on `source` flows to `sinkAp` inside `sink`
* for this configuration. `preservesValue` indicates whether any of the additional
* flow steps defined by `isAdditionalFlowStep` are needed.
*
* For the source access path, `sourceAp`, the top of the stack represents the content
* that was last read from. That is, if `sourceAp` is `Field1.Field2` (with `Field1`
* being the top of the stack), then there is flow from `source.Field2.Field1`.
*
* For the sink access path, `sinkAp`, the top of the stack represents the content
* that was last stored into. That is, if `sinkAp` is `Field1.Field2` (with `Field1`
* being the top of the stack), then there is flow into `sink.Field1.Field2`.
*/
predicate flow(
DataFlow::Node source, AccessPath sourceAp, DataFlow::Node sink, AccessPath sinkAp,
boolean preservesValue
) {
exists(Flow::PathNode pathSource, Flow::PathNode pathSink |
Flow::flowPath(pathSource, pathSink) and
nodeReaches(pathSource, TAccessPathNil(), TAccessPathNil(), pathSink, sourceAp, sinkAp) and
source = pathSource.getNode() and
sink = pathSink.getNode()
|
pathSink.getState().(InitState).decode(preservesValue)
or
pathSink.getState().(ReadState).decode(_, preservesValue)
or
pathSink.getState().(StoreState).decode(_, preservesValue)
)
}
private newtype TState =
TInitState(Boolean preservesValue) or
TStoreState(int size, Boolean preservesValue) {
size in [1 .. ContentConfig::accessPathLimit()]
} or
TReadState(int size, Boolean preservesValue) { size in [1 .. ContentConfig::accessPathLimit()] }
abstract private class State extends TState {
abstract string toString();
}
/** A flow state representing no reads or stores. */
private class InitState extends DF::FlowState {
private class InitState extends State, TInitState {
private boolean preservesValue_;
InitState() { this = "Init(" + preservesValue_ + ")" and preservesValue_ in [false, true] }
InitState() { this = TInitState(preservesValue_) }
override string toString() { result = "Init(" + preservesValue_ + ")" }
predicate decode(boolean preservesValue) { preservesValue = preservesValue_ }
}
/** A flow state representing that content has been stored into. */
private class StoreState extends DF::FlowState {
private class StoreState extends State, TStoreState {
private boolean preservesValue_;
private int size_;
StoreState() {
preservesValue_ in [false, true] and
size_ in [1 .. any(Configuration c).accessPathLimit()] and
this = "StoreState(" + size_ + "," + preservesValue_ + ")"
}
StoreState() { this = TStoreState(size_, preservesValue_) }
override string toString() { result = "StoreState(" + size_ + "," + preservesValue_ + ")" }
predicate decode(int size, boolean preservesValue) {
size = size_ and preservesValue = preservesValue_
@@ -158,15 +185,13 @@ module ContentDataFlow {
}
/** A flow state representing that content has been read from. */
private class ReadState extends DF::FlowState {
private class ReadState extends State, TReadState {
private boolean preservesValue_;
private int size_;
ReadState() {
preservesValue_ in [false, true] and
size_ in [1 .. any(Configuration c).accessPathLimit()] and
this = "ReadState(" + size_ + "," + preservesValue_ + ")"
}
ReadState() { this = TReadState(size_, preservesValue_) }
override string toString() { result = "ReadState(" + size_ + "," + preservesValue_ + ")" }
predicate decode(int size, boolean preservesValue) {
size = size_ and preservesValue = preservesValue_
@@ -174,12 +199,12 @@ module ContentDataFlow {
}
private predicate storeStep(
Node node1, DF::FlowState state1, ContentSet c, Node node2, StoreState state2,
Configuration config
DataFlow::Node node1, State state1, DataFlow::ContentSet c, DataFlow::Node node2,
StoreState state2
) {
exists(boolean preservesValue, int size |
storeSet(node1, c, node2, _, _) and
config.isRelevantContent(c) and
ContentConfig::isRelevantContent(c) and
state2.decode(size + 1, preservesValue)
|
state1.(InitState).decode(preservesValue) and size = 0
@@ -191,12 +216,12 @@ module ContentDataFlow {
}
private predicate readStep(
Node node1, DF::FlowState state1, ContentSet c, Node node2, ReadState state2,
Configuration config
DataFlow::Node node1, State state1, DataFlow::ContentSet c, DataFlow::Node node2,
ReadState state2
) {
exists(int size |
readSet(node1, c, node2) and
config.isRelevantContent(c) and
ContentConfig::isRelevantContent(c) and
state2.decode(size + 1, true)
|
state1.(InitState).decode(true) and
@@ -207,9 +232,9 @@ module ContentDataFlow {
}
private predicate additionalStep(
Node node1, DF::FlowState state1, Node node2, DF::FlowState state2, Configuration config
DataFlow::Node node1, State state1, DataFlow::Node node2, State state2
) {
config.isAdditionalFlowStep(node1, node2) and
ContentConfig::isAdditionalFlowStep(node1, node2) and
(
state1 instanceof InitState and
state2.(InitState).decode(false)
@@ -221,40 +246,9 @@ module ContentDataFlow {
)
}
private class ConfigurationAdapter extends DF::Configuration instanceof Configuration {
final override predicate isSource(Node source, DF::FlowState state) {
Configuration.super.isSource(source) and
state.(InitState).decode(true)
}
final override predicate isSink(Node sink, DF::FlowState state) {
Configuration.super.isSink(sink) and
(
state instanceof InitState or
state instanceof StoreState or
state instanceof ReadState
)
}
final override predicate isAdditionalFlowStep(
Node node1, DF::FlowState state1, Node node2, DF::FlowState state2
) {
storeStep(node1, state1, _, node2, state2, this) or
readStep(node1, state1, _, node2, state2, this) or
additionalStep(node1, state1, node2, state2, this)
}
final override predicate isBarrier(Node node) { Configuration.super.isBarrier(node) }
final override FlowFeature getAFeature() { result = Configuration.super.getAFeature() }
// needed to record reads/stores inside summarized callables
final override predicate includeHiddenNodes() { any() }
}
private newtype TAccessPath =
TAccessPathNil() or
TAccessPathCons(ContentSet head, AccessPath tail) {
TAccessPathCons(DataFlow::ContentSet head, AccessPath tail) {
nodeReachesStore(_, _, _, _, head, _, tail)
or
nodeReachesRead(_, _, _, _, head, tail, _)
@@ -263,7 +257,7 @@ module ContentDataFlow {
/** An access path. */
class AccessPath extends TAccessPath {
/** Gets the head of this access path, if any. */
ContentSet getHead() { this = TAccessPathCons(result, _) }
DataFlow::ContentSet getHead() { this = TAccessPathCons(result, _) }
/** Gets the tail of this access path, if any. */
AccessPath getTail() { this = TAccessPathCons(_, result) }
@@ -278,7 +272,7 @@ module ContentDataFlow {
this = TAccessPathNil() and
result = ""
or
exists(ContentSet head, AccessPath tail |
exists(DataFlow::ContentSet head, AccessPath tail |
this = TAccessPathCons(head, tail) and
result = head + "." + tail
)
@@ -287,7 +281,7 @@ module ContentDataFlow {
// important to use `edges` and not `PathNode::getASuccessor()`, as the latter
// is not pruned for reachability
private predicate pathSucc = DF::PathGraph::edges/2;
private predicate pathSucc = Flow::PathGraph::edges/2;
/**
* Provides a big-step flow relation, where flow stops at read/store steps that
@@ -295,10 +289,10 @@ module ContentDataFlow {
* summarized callables can be recorded as well.
*/
private module BigStepFlow {
private predicate reachesSink(DF::PathNode node) {
any(ConfigurationAdapter config).isSink(node.getNode(), node.getState())
private predicate reachesSink(Flow::PathNode node) {
FlowConfig::isSink(node.getNode(), node.getState())
or
exists(DF::PathNode mid |
exists(Flow::PathNode mid |
pathSucc(node, mid) and
reachesSink(mid)
)
@@ -309,76 +303,72 @@ module ContentDataFlow {
* in the big-step relation.
*/
pragma[nomagic]
private predicate excludeStep(DF::PathNode pred, DF::PathNode succ) {
private predicate excludeStep(Flow::PathNode pred, Flow::PathNode succ) {
pathSucc(pred, succ) and
(
// we need to record reads/stores inside summarized callables
DF::PathGraph::subpaths(pred, _, _, succ)
Flow::PathGraph::subpaths(pred, _, _, succ)
or
// only allow flow into a summarized callable, as part of the big-step
// relation, when flow can reach a sink without going back out
DF::PathGraph::subpaths(pred, succ, _, _) and
Flow::PathGraph::subpaths(pred, succ, _, _) and
not reachesSink(succ)
or
// needed to record store steps
storeStep(pred.getNode(), pred.getState(), _, succ.getNode(), succ.getState(),
pred.getConfiguration())
storeStep(pred.getNode(), pred.getState(), _, succ.getNode(), succ.getState())
or
// needed to record read steps
readStep(pred.getNode(), pred.getState(), _, succ.getNode(), succ.getState(),
pred.getConfiguration())
readStep(pred.getNode(), pred.getState(), _, succ.getNode(), succ.getState())
)
}
pragma[nomagic]
private DataFlowCallable getEnclosingCallableImpl(DF::PathNode node) {
private DataFlowCallable getEnclosingCallableImpl(Flow::PathNode node) {
result = getNodeEnclosingCallable(node.getNode())
}
pragma[inline]
private DataFlowCallable getEnclosingCallable(DF::PathNode node) {
private DataFlowCallable getEnclosingCallable(Flow::PathNode node) {
pragma[only_bind_into](result) = getEnclosingCallableImpl(pragma[only_bind_out](node))
}
pragma[nomagic]
private predicate bigStepEntry(DF::PathNode node) {
node.getConfiguration() instanceof Configuration and
private predicate bigStepEntry(Flow::PathNode node) {
(
any(ConfigurationAdapter config).isSource(node.getNode(), node.getState())
FlowConfig::isSource(node.getNode(), node.getState())
or
excludeStep(_, node)
or
DF::PathGraph::subpaths(_, node, _, _)
Flow::PathGraph::subpaths(_, node, _, _)
)
}
pragma[nomagic]
private predicate bigStepExit(DF::PathNode node) {
node.getConfiguration() instanceof Configuration and
private predicate bigStepExit(Flow::PathNode node) {
(
bigStepEntry(node)
or
any(ConfigurationAdapter config).isSink(node.getNode(), node.getState())
FlowConfig::isSink(node.getNode(), node.getState())
or
excludeStep(node, _)
or
DF::PathGraph::subpaths(_, _, node, _)
Flow::PathGraph::subpaths(_, _, node, _)
)
}
pragma[nomagic]
private predicate step(DF::PathNode pred, DF::PathNode succ) {
private predicate step(Flow::PathNode pred, Flow::PathNode succ) {
pathSucc(pred, succ) and
not excludeStep(pred, succ)
}
pragma[nomagic]
private predicate stepRec(DF::PathNode pred, DF::PathNode succ) {
private predicate stepRec(Flow::PathNode pred, Flow::PathNode succ) {
step(pred, succ) and
not bigStepEntry(pred)
}
private predicate stepRecPlus(DF::PathNode n1, DF::PathNode n2) = fastTC(stepRec/2)(n1, n2)
private predicate stepRecPlus(Flow::PathNode n1, Flow::PathNode n2) = fastTC(stepRec/2)(n1, n2)
/**
* Holds if there is flow `pathSucc+(pred) = succ`, and such a flow path does
@@ -386,8 +376,8 @@ module ContentDataFlow {
* steps.
*/
pragma[nomagic]
private predicate bigStep(DF::PathNode pred, DF::PathNode succ) {
exists(DF::PathNode mid |
private predicate bigStep(Flow::PathNode pred, Flow::PathNode succ) {
exists(Flow::PathNode mid |
bigStepEntry(pred) and
step(pred, mid)
|
@@ -399,13 +389,13 @@ module ContentDataFlow {
}
pragma[nomagic]
predicate bigStepNotLocal(DF::PathNode pred, DF::PathNode succ) {
predicate bigStepNotLocal(Flow::PathNode pred, Flow::PathNode succ) {
bigStep(pred, succ) and
not getEnclosingCallable(pred) = getEnclosingCallable(succ)
}
pragma[nomagic]
predicate bigStepMaybeLocal(DF::PathNode pred, DF::PathNode succ) {
predicate bigStepMaybeLocal(Flow::PathNode pred, Flow::PathNode succ) {
bigStep(pred, succ) and
getEnclosingCallable(pred) = getEnclosingCallable(succ)
}
@@ -422,55 +412,54 @@ module ContentDataFlow {
*/
pragma[nomagic]
private predicate nodeReaches(
DF::PathNode source, AccessPath scReads, AccessPath scStores, DF::PathNode node,
Flow::PathNode source, AccessPath scReads, AccessPath scStores, Flow::PathNode node,
AccessPath reads, AccessPath stores
) {
exists(ConfigurationAdapter config |
node = source and
reads = scReads and
stores = scStores
|
config.hasFlowPath(source, _) and
node = source and
reads = scReads and
stores = scStores and
(
Flow::flowPath(source, _) and
scReads = TAccessPathNil() and
scStores = TAccessPathNil()
or
// the argument in a sub path can be reached, so we start flow from the sub path
// parameter, while recording the read/store summary context
exists(DF::PathNode arg |
exists(Flow::PathNode arg |
nodeReachesSubpathArg(_, _, _, arg, scReads, scStores) and
DF::PathGraph::subpaths(arg, source, _, _)
Flow::PathGraph::subpaths(arg, source, _, _)
)
)
or
exists(DF::PathNode mid |
exists(Flow::PathNode mid |
nodeReaches(source, scReads, scStores, mid, reads, stores) and
BigStepFlow::bigStepMaybeLocal(mid, node)
)
or
exists(DF::PathNode mid |
exists(Flow::PathNode mid |
nodeReaches(source, scReads, scStores, mid, reads, stores) and
BigStepFlow::bigStepNotLocal(mid, node) and
// when flow is not local, we cannot flow back out, so we may stop
// flow early when computing summary flow
any(ConfigurationAdapter config).hasFlowPath(source, _) and
Flow::flowPath(source, _) and
scReads = TAccessPathNil() and
scStores = TAccessPathNil()
)
or
// store step
exists(AccessPath storesMid, ContentSet c |
exists(AccessPath storesMid, DataFlow::ContentSet c |
nodeReachesStore(source, scReads, scStores, node, c, reads, storesMid) and
stores = TAccessPathCons(c, storesMid)
)
or
// read step
exists(AccessPath readsMid, ContentSet c |
exists(AccessPath readsMid, DataFlow::ContentSet c |
nodeReachesRead(source, scReads, scStores, node, c, readsMid, stores) and
reads = TAccessPathCons(c, readsMid)
)
or
// flow-through step; match outer stores/reads with inner store/read summary contexts
exists(DF::PathNode mid, AccessPath innerScReads, AccessPath innerScStores |
exists(Flow::PathNode mid, AccessPath innerScReads, AccessPath innerScStores |
nodeReachesSubpathArg(source, scReads, scStores, mid, innerScReads, innerScStores) and
subpathArgReachesOut(mid, innerScReads, innerScStores, node, reads, stores)
)
@@ -478,47 +467,45 @@ module ContentDataFlow {
pragma[nomagic]
private predicate nodeReachesStore(
DF::PathNode source, AccessPath scReads, AccessPath scStores, DF::PathNode node, ContentSet c,
AccessPath reads, AccessPath stores
Flow::PathNode source, AccessPath scReads, AccessPath scStores, Flow::PathNode node,
DataFlow::ContentSet c, AccessPath reads, AccessPath stores
) {
exists(DF::PathNode mid |
exists(Flow::PathNode mid |
nodeReaches(source, scReads, scStores, mid, reads, stores) and
storeStep(mid.getNode(), mid.getState(), c, node.getNode(), node.getState(),
node.getConfiguration()) and
storeStep(mid.getNode(), mid.getState(), c, node.getNode(), node.getState()) and
pathSucc(mid, node)
)
}
pragma[nomagic]
private predicate nodeReachesRead(
DF::PathNode source, AccessPath scReads, AccessPath scStores, DF::PathNode node, ContentSet c,
AccessPath reads, AccessPath stores
Flow::PathNode source, AccessPath scReads, AccessPath scStores, Flow::PathNode node,
DataFlow::ContentSet c, AccessPath reads, AccessPath stores
) {
exists(DF::PathNode mid |
exists(Flow::PathNode mid |
nodeReaches(source, scReads, scStores, mid, reads, stores) and
readStep(mid.getNode(), mid.getState(), c, node.getNode(), node.getState(),
node.getConfiguration()) and
readStep(mid.getNode(), mid.getState(), c, node.getNode(), node.getState()) and
pathSucc(mid, node)
)
}
pragma[nomagic]
private predicate nodeReachesSubpathArg(
DF::PathNode source, AccessPath scReads, AccessPath scStores, DF::PathNode arg,
Flow::PathNode source, AccessPath scReads, AccessPath scStores, Flow::PathNode arg,
AccessPath reads, AccessPath stores
) {
nodeReaches(source, scReads, scStores, arg, reads, stores) and
DF::PathGraph::subpaths(arg, _, _, _)
Flow::PathGraph::subpaths(arg, _, _, _)
}
pragma[nomagic]
private predicate subpathArgReachesOut(
DF::PathNode arg, AccessPath scReads, AccessPath scStores, DF::PathNode out, AccessPath reads,
AccessPath stores
Flow::PathNode arg, AccessPath scReads, AccessPath scStores, Flow::PathNode out,
AccessPath reads, AccessPath stores
) {
exists(DF::PathNode source, DF::PathNode ret |
exists(Flow::PathNode source, Flow::PathNode ret |
nodeReaches(source, scReads, scStores, ret, reads, stores) and
DF::PathGraph::subpaths(arg, source, ret, out)
Flow::PathGraph::subpaths(arg, source, ret, out)
)
}
}

View File

@@ -815,24 +815,20 @@ private module Cached {
)
}
private predicate store(
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
exists(ContentSet cs |
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
)
}
/**
* Holds if data can flow from `node1` to `node2` via a direct assignment to
* `f`.
* `c`.
*
* This includes reverse steps through reads when the result of the read has
* been stored into, in order to handle cases like `x.f1.f2 = y`.
*/
cached
predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) {
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
predicate store(
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
exists(ContentSet cs |
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
)
}
/**
@@ -932,36 +928,15 @@ private module Cached {
TReturnCtxNoFlowThrough() or
TReturnCtxMaybeFlowThrough(ReturnPosition pos)
cached
newtype TTypedContentApprox =
MkTypedContentApprox(ContentApprox c, DataFlowType t) {
exists(Content cont |
c = getContentApprox(cont) and
store(_, cont, _, _, t)
)
}
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
cached
TypedContent getATypedContent(TypedContentApprox c) {
exists(ContentApprox cls, DataFlowType t, Content cont |
c = MkTypedContentApprox(cls, pragma[only_bind_into](t)) and
result = MkTypedContent(cont, pragma[only_bind_into](t)) and
cls = getContentApprox(cont)
)
}
cached
newtype TAccessPathFront =
TFrontNil(DataFlowType t) or
TFrontHead(TypedContent tc)
TFrontNil() or
TFrontHead(Content c)
cached
newtype TApproxAccessPathFront =
TApproxFrontNil(DataFlowType t) or
TApproxFrontHead(TypedContentApprox tc)
TApproxFrontNil() or
TApproxFrontHead(ContentApprox c)
cached
newtype TAccessPathFrontOption =
@@ -986,8 +961,16 @@ predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
/**
* A `Node` at which a cast can occur such that the type should be checked.
*/
class CastingNode extends Node {
class CastingNode instanceof Node {
CastingNode() { castingNode(this) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
private predicate readStepWithTypes(
@@ -1135,9 +1118,17 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable)
* The value of a parameter at function entry, viewed as a node in a data
* flow graph.
*/
class ParamNode extends Node {
class ParamNode instanceof Node {
ParamNode() { parameterNode(this, _, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/**
* Holds if this node is the parameter of callable `c` at the specified
* position.
@@ -1146,9 +1137,17 @@ class ParamNode extends Node {
}
/** A data-flow node that represents a call argument. */
class ArgNode extends Node {
class ArgNode instanceof Node {
ArgNode() { argumentNode(this, _, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Holds if this argument occurs at the given position in the given call. */
final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
argumentNode(this, call, pos)
@@ -1159,9 +1158,17 @@ class ArgNode extends Node {
* A node from which flow can return to the caller. This is either a regular
* `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter.
*/
class ReturnNodeExt extends Node {
class ReturnNodeExt instanceof Node {
ReturnNodeExt() { returnNodeExt(this, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the kind of this returned value. */
ReturnKindExt getKind() { returnNodeExt(this, result) }
}
@@ -1170,8 +1177,16 @@ class ReturnNodeExt extends Node {
* A node to which data can flow from a call. Either an ordinary out node
* or a post-update node associated with a call argument.
*/
class OutNodeExt extends Node {
class OutNodeExt instanceof Node {
OutNodeExt() { outNodeExt(this) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
/**
@@ -1387,67 +1402,37 @@ class ReturnCtx extends TReturnCtx {
}
}
/** An approximated `Content` tagged with the type of a containing object. */
class TypedContentApprox extends MkTypedContentApprox {
private ContentApprox c;
private DataFlowType t;
TypedContentApprox() { this = MkTypedContentApprox(c, t) }
/** Gets a typed content approximated by this value. */
TypedContent getATypedContent() { result = getATypedContent(this) }
/** Gets the content. */
ContentApprox getContent() { result = c }
/** Gets the container type. */
DataFlowType getContainerType() { result = t }
/** Gets a textual representation of this approximated content. */
string toString() { result = c.toString() }
}
/**
* The front of an approximated access path. This is either a head or a nil.
*/
abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
abstract string toString();
abstract DataFlowType getType();
abstract boolean toBoolNonEmpty();
TypedContentApprox getHead() { this = TApproxFrontHead(result) }
ContentApprox getHead() { this = TApproxFrontHead(result) }
pragma[nomagic]
TypedContent getAHead() {
exists(TypedContentApprox cont |
Content getAHead() {
exists(ContentApprox cont |
this = TApproxFrontHead(cont) and
result = cont.getATypedContent()
cont = getContentApprox(result)
)
}
}
class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil {
private DataFlowType t;
ApproxAccessPathFrontNil() { this = TApproxFrontNil(t) }
override string toString() { result = ppReprType(t) }
override DataFlowType getType() { result = t }
override string toString() { result = "nil" }
override boolean toBoolNonEmpty() { result = false }
}
class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead {
private TypedContentApprox tc;
private ContentApprox c;
ApproxAccessPathFrontHead() { this = TApproxFrontHead(tc) }
ApproxAccessPathFrontHead() { this = TApproxFrontHead(c) }
override string toString() { result = tc.toString() }
override DataFlowType getType() { result = tc.getContainerType() }
override string toString() { result = c.toString() }
override boolean toBoolNonEmpty() { result = true }
}
@@ -1461,65 +1446,31 @@ class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption {
}
}
/** A `Content` tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;
private DataFlowType t;
TypedContent() { this = MkTypedContent(c, t) }
/** Gets the content. */
Content getContent() { result = c }
/** Gets the container type. */
DataFlowType getContainerType() { result = t }
/** Gets a textual representation of this content. */
string toString() { result = c.toString() }
/**
* Holds if access paths with this `TypedContent` at their head always should
* be tracked at high precision. This disables adaptive access path precision
* for such access paths.
*/
predicate forceHighPrecision() { forceHighPrecision(c) }
}
/**
* The front of an access path. This is either a head or a nil.
*/
abstract class AccessPathFront extends TAccessPathFront {
abstract string toString();
abstract DataFlowType getType();
abstract ApproxAccessPathFront toApprox();
TypedContent getHead() { this = TFrontHead(result) }
Content getHead() { this = TFrontHead(result) }
}
class AccessPathFrontNil extends AccessPathFront, TFrontNil {
private DataFlowType t;
override string toString() { result = "nil" }
AccessPathFrontNil() { this = TFrontNil(t) }
override string toString() { result = ppReprType(t) }
override DataFlowType getType() { result = t }
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil(t) }
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil() }
}
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
private TypedContent tc;
private Content c;
AccessPathFrontHead() { this = TFrontHead(tc) }
AccessPathFrontHead() { this = TFrontHead(c) }
override string toString() { result = tc.toString() }
override string toString() { result = c.toString() }
override DataFlowType getType() { result = tc.getContainerType() }
override ApproxAccessPathFront toApprox() { result.getAHead() = tc }
override ApproxAccessPathFront toApprox() { result.getAHead() = c }
}
/** An optional access path front. */

View File

@@ -1,398 +0,0 @@
/**
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* Provides a `Configuration` class backwards-compatible interface to the data
* flow library.
*/
private import DataFlowImplCommon
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
private import DataFlowImpl
import DataFlowImplCommonPublic
import FlowStateString
private import codeql.util.Unit
/**
* A configuration of interprocedural data flow analysis. This defines
* sources, sinks, and any other configurable aspect of the analysis. Each
* use of the global data flow library must define its own unique extension
* of this abstract class. To create a configuration, extend this class with
* a subclass whose characteristic predicate is a unique singleton string.
* For example, write
*
* ```ql
* class MyAnalysisConfiguration extends DataFlow::Configuration {
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
* // Override `isSource` and `isSink`.
* // Optionally override `isBarrier`.
* // Optionally override `isAdditionalFlowStep`.
* }
* ```
* Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and
* the edges are those data-flow steps that preserve the value of the node
* along with any additional edges defined by `isAdditionalFlowStep`.
* Specifying nodes in `isBarrier` will remove those nodes from the graph, and
* specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going
* and/or out-going edges from those nodes, respectively.
*
* Then, to query whether there is flow between some `source` and `sink`,
* write
*
* ```ql
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
* ```
*
* Multiple configurations can coexist, but two classes extending
* `DataFlow::Configuration` should never depend on each other. One of them
* should instead depend on a `DataFlow2::Configuration`, a
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
*/
abstract class Configuration extends string {
bindingset[this]
Configuration() { any() }
/**
* Holds if `source` is a relevant data flow source.
*/
predicate isSource(Node source) { none() }
/**
* Holds if `source` is a relevant data flow source with the given initial
* `state`.
*/
predicate isSource(Node source, FlowState state) { none() }
/**
* Holds if `sink` is a relevant data flow sink.
*/
predicate isSink(Node sink) { none() }
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
* `node` from the data flow graph.
*/
predicate isBarrier(Node node) { none() }
/**
* Holds if data flow through `node` is prohibited when the flow state is
* `state`.
*/
predicate isBarrier(Node node, FlowState state) { none() }
/** Holds if data flow into `node` is prohibited. */
predicate isBarrierIn(Node node) { none() }
/** Holds if data flow out of `node` is prohibited. */
predicate isBarrierOut(Node node) { none() }
/**
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
*
* Holds if data flow through nodes guarded by `guard` is prohibited.
*/
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
/**
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
*
* Holds if data flow through nodes guarded by `guard` is prohibited when
* the flow state is `state`
*/
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
*/
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
* This step is only applicable in `state1` and updates the flow state to `state2`.
*/
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
none()
}
/**
* Holds if an arbitrary number of implicit read steps of content `c` may be
* taken at `node`.
*/
predicate allowImplicitRead(Node node, ContentSet c) { none() }
/**
* Gets the virtual dispatch branching limit when calculating field flow.
* This can be overridden to a smaller value to improve performance (a
* value of 0 disables field flow), or a larger value to get more results.
*/
int fieldFlowBranchLimit() { result = 2 }
/**
* Gets a data flow configuration feature to add restrictions to the set of
* valid flow paths.
*
* - `FeatureHasSourceCallContext`:
* Assume that sources have some existing call context to disallow
* conflicting return-flow directly following the source.
* - `FeatureHasSinkCallContext`:
* Assume that sinks have some existing call context to disallow
* conflicting argument-to-parameter flow directly preceding the sink.
* - `FeatureEqualSourceSinkCallContext`:
* Implies both of the above and additionally ensures that the entire flow
* path preserves the call context.
*
* These features are generally not relevant for typical end-to-end data flow
* queries, but should only be used for constructing paths that need to
* somehow be pluggable in another path context.
*/
FlowFeature getAFeature() { none() }
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
predicate sourceGrouping(Node source, string sourceGroup) { none() }
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
predicate sinkGrouping(Node sink, string sinkGroup) { none() }
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
predicate hasFlow(Node source, Node sink) { hasFlow(source, sink, this) }
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*
* The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`.
*/
predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
/**
* Holds if data may flow from some source to `sink` for this configuration.
*/
predicate hasFlowTo(Node sink) { hasFlowTo(sink, this) }
/**
* Holds if data may flow from some source to `sink` for this configuration.
*/
predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
/**
* DEPRECATED: Use `FlowExploration<explorationLimit>` instead.
*
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
* measured in approximate number of interprocedural steps.
*/
deprecated int explorationLimit() { none() }
/**
* Holds if hidden nodes should be included in the data flow graph.
*
* This feature should only be used for debugging or when the data flow graph
* is not visualized (for example in a `path-problem` query).
*/
predicate includeHiddenNodes() { none() }
}
/**
* This class exists to prevent mutual recursion between the user-overridden
* member predicates of `Configuration` and the rest of the data-flow library.
* Good performance cannot be guaranteed in the presence of such recursion, so
* it should be replaced by using more than one copy of the data flow library.
*/
abstract private class ConfigurationRecursionPrevention extends Configuration {
bindingset[this]
ConfigurationRecursionPrevention() { any() }
override predicate hasFlow(Node source, Node sink) {
strictcount(Node n | this.isSource(n)) < 0
or
strictcount(Node n | this.isSource(n, _)) < 0
or
strictcount(Node n | this.isSink(n)) < 0
or
strictcount(Node n | this.isSink(n, _)) < 0
or
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
or
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, _, n2, _)) < 0
or
super.hasFlow(source, sink)
}
}
/** A bridge class to access the deprecated `isBarrierGuard`. */
private class BarrierGuardGuardedNodeBridge extends Unit {
abstract predicate guardedNode(Node n, Configuration config);
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
}
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
deprecated override predicate guardedNode(Node n, Configuration config) {
exists(BarrierGuard g |
config.isBarrierGuard(g) and
n = g.getAGuardedNode()
)
}
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
exists(BarrierGuard g |
config.isBarrierGuard(g, state) and
n = g.getAGuardedNode()
)
}
}
private FlowState relevantState(Configuration config) {
config.isSource(_, result) or
config.isSink(_, result) or
config.isBarrier(_, result) or
config.isAdditionalFlowStep(_, result, _, _) or
config.isAdditionalFlowStep(_, _, _, result)
}
private newtype TConfigState =
TMkConfigState(Configuration config, FlowState state) {
state = relevantState(config) or state instanceof FlowStateEmpty
}
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
private module Config implements FullStateConfigSig {
class FlowState = TConfigState;
predicate isSource(Node source, FlowState state) {
getConfig(state).isSource(source, getState(state))
or
getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty
}
predicate isSink(Node sink, FlowState state) {
getConfig(state).isSink(sink, getState(state))
or
getConfig(state).isSink(sink) and getState(state) instanceof FlowStateEmpty
}
predicate isBarrier(Node node) { none() }
predicate isBarrier(Node node, FlowState state) {
getConfig(state).isBarrier(node, getState(state)) or
getConfig(state).isBarrier(node) or
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
}
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
predicate isAdditionalFlowStep(Node node1, Node node2) {
singleConfiguration() and
any(Configuration config).isAdditionalFlowStep(node1, node2)
}
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and
getConfig(state2) = getConfig(state1)
or
not singleConfiguration() and
getConfig(state1).isAdditionalFlowStep(node1, node2) and
state2 = state1
}
predicate allowImplicitRead(Node node, ContentSet c) {
any(Configuration config).allowImplicitRead(node, c)
}
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
predicate sourceGrouping(Node source, string sourceGroup) {
any(Configuration config).sourceGrouping(source, sourceGroup)
}
predicate sinkGrouping(Node sink, string sinkGroup) {
any(Configuration config).sinkGrouping(sink, sinkGroup)
}
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
}
private import Impl<Config> as I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
*/
class PathNode instanceof I::PathNode {
/** Gets a textual representation of this element. */
final string toString() { result = super.toString() }
/**
* Gets a textual representation of this element, including a textual
* representation of the call context.
*/
final string toStringWithContext() { result = super.toStringWithContext() }
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
final predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the underlying `Node`. */
final Node getNode() { result = super.getNode() }
/** Gets the `FlowState` of this node. */
final FlowState getState() { result = getState(super.getState()) }
/** Gets the associated configuration. */
final Configuration getConfiguration() { result = getConfig(super.getState()) }
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() { result = super.getASuccessor() }
/** Holds if this node is a source. */
final predicate isSource() { super.isSource() }
/** Holds if this node is a grouping of source nodes. */
final predicate isSourceGroup(string group) { super.isSourceGroup(group) }
/** Holds if this node is a grouping of sink nodes. */
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
source0.getNode() = source and
sink0.getNode() = sink
)
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
predicate flowsTo = hasFlow/3;

View File

@@ -139,9 +139,9 @@ module ThroughFlowConfig implements DataFlow::StateConfigSig {
predicate isAdditionalFlowStep(
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
) {
exists(DataFlowImplCommon::TypedContent tc |
DataFlowImplCommon::store(node1, tc, node2, _) and
isRelevantContent(tc.getContent()) and
exists(DataFlow::Content c |
DataFlowImplCommon::store(node1, c, node2, _, _) and
isRelevantContent(c) and
(
state1 instanceof TaintRead and state2.(TaintStore).getStep() = 1
or
@@ -178,7 +178,7 @@ string captureThroughFlow(DataFlowTargetApi api) {
string output
|
ThroughFlow::flow(p, returnNodeExt) and
returnNodeExt.getEnclosingCallable() = api and
returnNodeExt.(DataFlow::Node).getEnclosingCallable() = api and
input = parameterNodeAsInput(p) and
output = returnNodeAsOutput(returnNodeExt) and
input != output and

View File

@@ -121,7 +121,7 @@ class InstanceParameterNode = DataFlowPrivate::InstanceParameterNode;
pragma[nomagic]
private CS::Parameter getParameter(DataFlowImplCommon::ReturnNodeExt node, ParameterPosition pos) {
result = node.getEnclosingCallable().getParameter(pos.getPosition())
result = node.(DataFlow::Node).getEnclosingCallable().getParameter(pos.getPosition())
}
/**

View File

@@ -1,23 +1,23 @@
import csharp
import semmle.code.csharp.dataflow.internal.ContentDataFlow
import semmle.code.csharp.dataflow.internal.ContentDataFlow as ContentDataFlow
class Conf extends ContentDataFlow::Configuration {
Conf() { this = "ContentFlowConf" }
module ContentConfig implements ContentDataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof ObjectCreation }
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof ObjectCreation }
override predicate isSink(DataFlow::Node sink) {
predicate isSink(DataFlow::Node sink) {
exists(MethodCall mc |
mc.getTarget().hasUndecoratedName("Sink") and
mc.getAnArgument() = sink.asExpr()
)
}
override int accessPathLimit() { result = 2 }
int accessPathLimit() { result = 2 }
}
module ContentFlow = ContentDataFlow::Global<ContentConfig>;
from
Conf conf, ContentDataFlow::Node source, ContentDataFlow::AccessPath sourceAp,
ContentDataFlow::Node sink, ContentDataFlow::AccessPath sinkAp, boolean preservesValue
where conf.hasFlow(source, sourceAp, sink, sinkAp, preservesValue)
DataFlow::Node source, ContentFlow::AccessPath sourceAp, DataFlow::Node sink,
ContentFlow::AccessPath sinkAp, boolean preservesValue
where ContentFlow::flow(source, sourceAp, sink, sinkAp, preservesValue)
select source, sourceAp, sink, sinkAp, preservesValue

File diff suppressed because it is too large Load Diff

View File

@@ -815,24 +815,20 @@ private module Cached {
)
}
private predicate store(
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
exists(ContentSet cs |
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
)
}
/**
* Holds if data can flow from `node1` to `node2` via a direct assignment to
* `f`.
* `c`.
*
* This includes reverse steps through reads when the result of the read has
* been stored into, in order to handle cases like `x.f1.f2 = y`.
*/
cached
predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) {
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
predicate store(
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
exists(ContentSet cs |
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
)
}
/**
@@ -932,36 +928,15 @@ private module Cached {
TReturnCtxNoFlowThrough() or
TReturnCtxMaybeFlowThrough(ReturnPosition pos)
cached
newtype TTypedContentApprox =
MkTypedContentApprox(ContentApprox c, DataFlowType t) {
exists(Content cont |
c = getContentApprox(cont) and
store(_, cont, _, _, t)
)
}
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
cached
TypedContent getATypedContent(TypedContentApprox c) {
exists(ContentApprox cls, DataFlowType t, Content cont |
c = MkTypedContentApprox(cls, pragma[only_bind_into](t)) and
result = MkTypedContent(cont, pragma[only_bind_into](t)) and
cls = getContentApprox(cont)
)
}
cached
newtype TAccessPathFront =
TFrontNil(DataFlowType t) or
TFrontHead(TypedContent tc)
TFrontNil() or
TFrontHead(Content c)
cached
newtype TApproxAccessPathFront =
TApproxFrontNil(DataFlowType t) or
TApproxFrontHead(TypedContentApprox tc)
TApproxFrontNil() or
TApproxFrontHead(ContentApprox c)
cached
newtype TAccessPathFrontOption =
@@ -986,8 +961,16 @@ predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
/**
* A `Node` at which a cast can occur such that the type should be checked.
*/
class CastingNode extends Node {
class CastingNode instanceof Node {
CastingNode() { castingNode(this) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
private predicate readStepWithTypes(
@@ -1135,9 +1118,17 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable)
* The value of a parameter at function entry, viewed as a node in a data
* flow graph.
*/
class ParamNode extends Node {
class ParamNode instanceof Node {
ParamNode() { parameterNode(this, _, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/**
* Holds if this node is the parameter of callable `c` at the specified
* position.
@@ -1146,9 +1137,17 @@ class ParamNode extends Node {
}
/** A data-flow node that represents a call argument. */
class ArgNode extends Node {
class ArgNode instanceof Node {
ArgNode() { argumentNode(this, _, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Holds if this argument occurs at the given position in the given call. */
final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
argumentNode(this, call, pos)
@@ -1159,9 +1158,17 @@ class ArgNode extends Node {
* A node from which flow can return to the caller. This is either a regular
* `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter.
*/
class ReturnNodeExt extends Node {
class ReturnNodeExt instanceof Node {
ReturnNodeExt() { returnNodeExt(this, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the kind of this returned value. */
ReturnKindExt getKind() { returnNodeExt(this, result) }
}
@@ -1170,8 +1177,16 @@ class ReturnNodeExt extends Node {
* A node to which data can flow from a call. Either an ordinary out node
* or a post-update node associated with a call argument.
*/
class OutNodeExt extends Node {
class OutNodeExt instanceof Node {
OutNodeExt() { outNodeExt(this) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
/**
@@ -1387,67 +1402,37 @@ class ReturnCtx extends TReturnCtx {
}
}
/** An approximated `Content` tagged with the type of a containing object. */
class TypedContentApprox extends MkTypedContentApprox {
private ContentApprox c;
private DataFlowType t;
TypedContentApprox() { this = MkTypedContentApprox(c, t) }
/** Gets a typed content approximated by this value. */
TypedContent getATypedContent() { result = getATypedContent(this) }
/** Gets the content. */
ContentApprox getContent() { result = c }
/** Gets the container type. */
DataFlowType getContainerType() { result = t }
/** Gets a textual representation of this approximated content. */
string toString() { result = c.toString() }
}
/**
* The front of an approximated access path. This is either a head or a nil.
*/
abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
abstract string toString();
abstract DataFlowType getType();
abstract boolean toBoolNonEmpty();
TypedContentApprox getHead() { this = TApproxFrontHead(result) }
ContentApprox getHead() { this = TApproxFrontHead(result) }
pragma[nomagic]
TypedContent getAHead() {
exists(TypedContentApprox cont |
Content getAHead() {
exists(ContentApprox cont |
this = TApproxFrontHead(cont) and
result = cont.getATypedContent()
cont = getContentApprox(result)
)
}
}
class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil {
private DataFlowType t;
ApproxAccessPathFrontNil() { this = TApproxFrontNil(t) }
override string toString() { result = ppReprType(t) }
override DataFlowType getType() { result = t }
override string toString() { result = "nil" }
override boolean toBoolNonEmpty() { result = false }
}
class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead {
private TypedContentApprox tc;
private ContentApprox c;
ApproxAccessPathFrontHead() { this = TApproxFrontHead(tc) }
ApproxAccessPathFrontHead() { this = TApproxFrontHead(c) }
override string toString() { result = tc.toString() }
override DataFlowType getType() { result = tc.getContainerType() }
override string toString() { result = c.toString() }
override boolean toBoolNonEmpty() { result = true }
}
@@ -1461,65 +1446,31 @@ class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption {
}
}
/** A `Content` tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;
private DataFlowType t;
TypedContent() { this = MkTypedContent(c, t) }
/** Gets the content. */
Content getContent() { result = c }
/** Gets the container type. */
DataFlowType getContainerType() { result = t }
/** Gets a textual representation of this content. */
string toString() { result = c.toString() }
/**
* Holds if access paths with this `TypedContent` at their head always should
* be tracked at high precision. This disables adaptive access path precision
* for such access paths.
*/
predicate forceHighPrecision() { forceHighPrecision(c) }
}
/**
* The front of an access path. This is either a head or a nil.
*/
abstract class AccessPathFront extends TAccessPathFront {
abstract string toString();
abstract DataFlowType getType();
abstract ApproxAccessPathFront toApprox();
TypedContent getHead() { this = TFrontHead(result) }
Content getHead() { this = TFrontHead(result) }
}
class AccessPathFrontNil extends AccessPathFront, TFrontNil {
private DataFlowType t;
override string toString() { result = "nil" }
AccessPathFrontNil() { this = TFrontNil(t) }
override string toString() { result = ppReprType(t) }
override DataFlowType getType() { result = t }
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil(t) }
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil() }
}
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
private TypedContent tc;
private Content c;
AccessPathFrontHead() { this = TFrontHead(tc) }
AccessPathFrontHead() { this = TFrontHead(c) }
override string toString() { result = tc.toString() }
override string toString() { result = c.toString() }
override DataFlowType getType() { result = tc.getContainerType() }
override ApproxAccessPathFront toApprox() { result.getAHead() = tc }
override ApproxAccessPathFront toApprox() { result.getAHead() = c }
}
/** An optional access path front. */

View File

@@ -127,4 +127,20 @@ module SharedXss {
)
}
}
/**
* A `Template` from `html/template` will HTML-escape data automatically
* and therefore acts as a sanitizer for XSS vulnerabilities.
*/
class HtmlTemplateSanitizer extends Sanitizer, DataFlow::Node {
HtmlTemplateSanitizer() {
exists(Method m, DataFlow::CallNode call | m = call.getCall().getTarget() |
m.hasQualifiedName("html/template", "Template", "ExecuteTemplate") and
call.getArgument(2) = this
or
m.hasQualifiedName("html/template", "Template", "Execute") and
call.getArgument(1) = this
)
}
}
}

View File

@@ -64,6 +64,10 @@ class FlowConfFromUntrustedToPassthroughTypeConversion extends TaintTracking::Co
}
override predicate isSink(DataFlow::Node sink) { isSinkToPassthroughType(sink, dstTypeName) }
override predicate isSanitizer(DataFlow::Node sanitizer) {
sanitizer instanceof SharedXss::Sanitizer or sanitizer.getType() instanceof NumericType
}
}
/**
@@ -100,7 +104,7 @@ class FlowConfPassthroughTypeConversionToTemplateExecutionCall extends TaintTrac
PassthroughTypeName getDstTypeName() { result = dstTypeName }
override predicate isSource(DataFlow::Node source) {
isSourceConversionToPassthroughType(source, _)
isSourceConversionToPassthroughType(source, dstTypeName)
}
private predicate isSourceConversionToPassthroughType(
@@ -141,10 +145,6 @@ class FlowConfFromUntrustedToTemplateExecutionCall extends TaintTracking::Config
override predicate isSource(DataFlow::Node source) { source instanceof UntrustedFlowSource }
override predicate isSink(DataFlow::Node sink) { isSinkToTemplateExec(sink, _) }
override predicate isSanitizer(DataFlow::Node sanitizer) {
sanitizer instanceof SharedXss::Sanitizer or sanitizer.getType() instanceof NumericType
}
}
/**

View File

@@ -1,311 +1,151 @@
edges
| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion | HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a |
| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion | HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a |
| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion | HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a |
| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion | HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a |
| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion | HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a |
| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion | HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a |
| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion | HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a |
| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion | HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a |
| HTMLTemplateEscapingPassthrough.go:28:26:28:40 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion |
| HTMLTemplateEscapingPassthrough.go:28:26:28:40 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion |
| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion | HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a |
| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion | HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a |
| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion | HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a |
| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion | HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a |
| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion | HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a |
| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion | HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a |
| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion | HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a |
| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion | HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a |
| HTMLTemplateEscapingPassthrough.go:34:23:34:37 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion |
| HTMLTemplateEscapingPassthrough.go:34:23:34:37 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion |
| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion | HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a |
| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion | HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a |
| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion | HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a |
| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion | HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a |
| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion | HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a |
| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion | HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a |
| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion | HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a |
| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion | HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a |
| HTMLTemplateEscapingPassthrough.go:39:19:39:33 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion |
| HTMLTemplateEscapingPassthrough.go:39:19:39:33 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion |
| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion | HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c |
| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion | HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c |
| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion | HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c |
| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion | HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c |
| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion | HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c |
| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion | HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c |
| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion | HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c |
| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion | HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c |
| HTMLTemplateEscapingPassthrough.go:45:29:45:43 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion |
| HTMLTemplateEscapingPassthrough.go:45:29:45:43 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion |
| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion | HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d |
| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion | HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d |
| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion | HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d |
| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion | HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d |
| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion | HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d |
| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion | HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d |
| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion | HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d |
| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion | HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d |
| HTMLTemplateEscapingPassthrough.go:49:23:49:37 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion |
| HTMLTemplateEscapingPassthrough.go:49:23:49:37 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion |
| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion | HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e |
| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion | HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e |
| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion | HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e |
| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion | HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e |
| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion | HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e |
| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion | HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e |
| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion | HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e |
| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion | HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e |
| HTMLTemplateEscapingPassthrough.go:53:26:53:40 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion |
| HTMLTemplateEscapingPassthrough.go:53:26:53:40 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion |
| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion | HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b |
| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion | HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b |
| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion | HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b |
| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion | HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b |
| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion | HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b |
| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion | HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b |
| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion | HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b |
| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion | HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b |
| HTMLTemplateEscapingPassthrough.go:57:24:57:38 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion |
| HTMLTemplateEscapingPassthrough.go:57:24:57:38 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion |
| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion | HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f |
| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion | HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f |
| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion | HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f |
| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion | HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f |
| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion | HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f |
| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion | HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f |
| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion | HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f |
| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion | HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f |
| HTMLTemplateEscapingPassthrough.go:61:27:61:41 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion |
| HTMLTemplateEscapingPassthrough.go:61:27:61:41 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion |
| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion | HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g |
| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion | HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g |
| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion | HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g |
| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion | HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g |
| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion | HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g |
| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion | HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g |
| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion | HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g |
| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion | HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g |
| HTMLTemplateEscapingPassthrough.go:65:24:65:38 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion |
| HTMLTemplateEscapingPassthrough.go:65:24:65:38 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion |
| HTMLTemplateEscapingPassthrough.go:74:17:74:31 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:75:38:75:44 | escaped |
| HTMLTemplateEscapingPassthrough.go:80:10:80:24 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:81:16:81:33 | type conversion |
| HTMLTemplateEscapingPassthrough.go:80:10:80:24 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:83:38:83:40 | src |
| HTMLTemplateEscapingPassthrough.go:88:10:88:24 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:90:64:90:66 | src |
| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion | HTMLTemplateEscapingPassthrough.go:91:38:91:46 | converted |
| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion | HTMLTemplateEscapingPassthrough.go:91:38:91:46 | converted |
| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion | HTMLTemplateEscapingPassthrough.go:91:38:91:46 | converted |
| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion | HTMLTemplateEscapingPassthrough.go:91:38:91:46 | converted |
| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion | HTMLTemplateEscapingPassthrough.go:91:38:91:46 | converted |
| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion | HTMLTemplateEscapingPassthrough.go:91:38:91:46 | converted |
| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion | HTMLTemplateEscapingPassthrough.go:91:38:91:46 | converted |
| HTMLTemplateEscapingPassthrough.go:90:38:90:67 | call to HTMLEscapeString | HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion |
| HTMLTemplateEscapingPassthrough.go:90:64:90:66 | src | HTMLTemplateEscapingPassthrough.go:90:38:90:67 | call to HTMLEscapeString |
| HTMLTemplateEscapingPassthrough.go:29:12:29:41 | type conversion | HTMLTemplateEscapingPassthrough.go:30:39:30:39 | a |
| HTMLTemplateEscapingPassthrough.go:29:12:29:41 | type conversion | HTMLTemplateEscapingPassthrough.go:30:39:30:39 | a |
| HTMLTemplateEscapingPassthrough.go:29:26:29:40 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:29:12:29:41 | type conversion |
| HTMLTemplateEscapingPassthrough.go:29:26:29:40 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:29:12:29:41 | type conversion |
| HTMLTemplateEscapingPassthrough.go:35:9:35:38 | type conversion | HTMLTemplateEscapingPassthrough.go:36:40:36:40 | a |
| HTMLTemplateEscapingPassthrough.go:35:9:35:38 | type conversion | HTMLTemplateEscapingPassthrough.go:36:40:36:40 | a |
| HTMLTemplateEscapingPassthrough.go:35:23:35:37 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:35:9:35:38 | type conversion |
| HTMLTemplateEscapingPassthrough.go:35:23:35:37 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:35:9:35:38 | type conversion |
| HTMLTemplateEscapingPassthrough.go:40:9:40:34 | type conversion | HTMLTemplateEscapingPassthrough.go:41:40:41:40 | a |
| HTMLTemplateEscapingPassthrough.go:40:9:40:34 | type conversion | HTMLTemplateEscapingPassthrough.go:41:40:41:40 | a |
| HTMLTemplateEscapingPassthrough.go:40:19:40:33 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:40:9:40:34 | type conversion |
| HTMLTemplateEscapingPassthrough.go:40:19:40:33 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:40:9:40:34 | type conversion |
| HTMLTemplateEscapingPassthrough.go:46:11:46:44 | type conversion | HTMLTemplateEscapingPassthrough.go:47:41:47:41 | c |
| HTMLTemplateEscapingPassthrough.go:46:11:46:44 | type conversion | HTMLTemplateEscapingPassthrough.go:47:41:47:41 | c |
| HTMLTemplateEscapingPassthrough.go:46:29:46:43 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:46:11:46:44 | type conversion |
| HTMLTemplateEscapingPassthrough.go:46:29:46:43 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:46:11:46:44 | type conversion |
| HTMLTemplateEscapingPassthrough.go:50:11:50:38 | type conversion | HTMLTemplateEscapingPassthrough.go:51:44:51:44 | d |
| HTMLTemplateEscapingPassthrough.go:50:11:50:38 | type conversion | HTMLTemplateEscapingPassthrough.go:51:44:51:44 | d |
| HTMLTemplateEscapingPassthrough.go:50:23:50:37 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:50:11:50:38 | type conversion |
| HTMLTemplateEscapingPassthrough.go:50:23:50:37 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:50:11:50:38 | type conversion |
| HTMLTemplateEscapingPassthrough.go:54:11:54:41 | type conversion | HTMLTemplateEscapingPassthrough.go:55:44:55:44 | e |
| HTMLTemplateEscapingPassthrough.go:54:11:54:41 | type conversion | HTMLTemplateEscapingPassthrough.go:55:44:55:44 | e |
| HTMLTemplateEscapingPassthrough.go:54:26:54:40 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:54:11:54:41 | type conversion |
| HTMLTemplateEscapingPassthrough.go:54:26:54:40 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:54:11:54:41 | type conversion |
| HTMLTemplateEscapingPassthrough.go:58:11:58:39 | type conversion | HTMLTemplateEscapingPassthrough.go:59:38:59:38 | b |
| HTMLTemplateEscapingPassthrough.go:58:11:58:39 | type conversion | HTMLTemplateEscapingPassthrough.go:59:38:59:38 | b |
| HTMLTemplateEscapingPassthrough.go:58:24:58:38 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:58:11:58:39 | type conversion |
| HTMLTemplateEscapingPassthrough.go:58:24:58:38 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:58:11:58:39 | type conversion |
| HTMLTemplateEscapingPassthrough.go:62:11:62:42 | type conversion | HTMLTemplateEscapingPassthrough.go:63:44:63:44 | f |
| HTMLTemplateEscapingPassthrough.go:62:11:62:42 | type conversion | HTMLTemplateEscapingPassthrough.go:63:44:63:44 | f |
| HTMLTemplateEscapingPassthrough.go:62:27:62:41 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:62:11:62:42 | type conversion |
| HTMLTemplateEscapingPassthrough.go:62:27:62:41 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:62:11:62:42 | type conversion |
| HTMLTemplateEscapingPassthrough.go:66:11:66:39 | type conversion | HTMLTemplateEscapingPassthrough.go:67:38:67:38 | g |
| HTMLTemplateEscapingPassthrough.go:66:11:66:39 | type conversion | HTMLTemplateEscapingPassthrough.go:67:38:67:38 | g |
| HTMLTemplateEscapingPassthrough.go:66:24:66:38 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:66:11:66:39 | type conversion |
| HTMLTemplateEscapingPassthrough.go:66:24:66:38 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:66:11:66:39 | type conversion |
| HTMLTemplateEscapingPassthrough.go:75:17:75:31 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:76:38:76:44 | escaped |
| HTMLTemplateEscapingPassthrough.go:81:10:81:24 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:82:16:82:33 | type conversion |
| HTMLTemplateEscapingPassthrough.go:81:10:81:24 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:84:38:84:40 | src |
| HTMLTemplateEscapingPassthrough.go:89:10:89:24 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:91:64:91:66 | src |
| HTMLTemplateEscapingPassthrough.go:91:16:91:77 | type conversion | HTMLTemplateEscapingPassthrough.go:92:38:92:46 | converted |
| HTMLTemplateEscapingPassthrough.go:91:16:91:77 | type conversion | HTMLTemplateEscapingPassthrough.go:92:38:92:46 | converted |
| HTMLTemplateEscapingPassthrough.go:91:38:91:67 | call to HTMLEscapeString | HTMLTemplateEscapingPassthrough.go:91:16:91:77 | type conversion |
| HTMLTemplateEscapingPassthrough.go:91:64:91:66 | src | HTMLTemplateEscapingPassthrough.go:91:38:91:67 | call to HTMLEscapeString |
| HTMLTemplateEscapingPassthrough.go:101:9:101:14 | selection of Form | HTMLTemplateEscapingPassthrough.go:101:9:101:24 | call to Get |
| HTMLTemplateEscapingPassthrough.go:101:9:101:24 | call to Get | HTMLTemplateEscapingPassthrough.go:115:8:115:15 | call to getId |
| HTMLTemplateEscapingPassthrough.go:104:18:104:18 | definition of x | HTMLTemplateEscapingPassthrough.go:105:9:105:24 | type conversion |
| HTMLTemplateEscapingPassthrough.go:105:9:105:24 | type conversion | HTMLTemplateEscapingPassthrough.go:123:11:123:36 | call to passthrough |
| HTMLTemplateEscapingPassthrough.go:108:35:108:35 | definition of x | HTMLTemplateEscapingPassthrough.go:110:19:110:19 | x |
| HTMLTemplateEscapingPassthrough.go:115:8:115:15 | call to getId | HTMLTemplateEscapingPassthrough.go:116:15:116:15 | x |
| HTMLTemplateEscapingPassthrough.go:116:15:116:15 | x | HTMLTemplateEscapingPassthrough.go:104:18:104:18 | definition of x |
| HTMLTemplateEscapingPassthrough.go:123:11:123:36 | call to passthrough | HTMLTemplateEscapingPassthrough.go:108:35:108:35 | definition of x |
nodes
| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:28:26:28:40 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:28:26:28:40 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:34:23:34:37 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:34:23:34:37 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:39:19:39:33 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:39:19:39:33 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:45:29:45:43 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:45:29:45:43 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c | semmle.label | c |
| HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c | semmle.label | c |
| HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c | semmle.label | c |
| HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c | semmle.label | c |
| HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c | semmle.label | c |
| HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c | semmle.label | c |
| HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c | semmle.label | c |
| HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c | semmle.label | c |
| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:49:23:49:37 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:49:23:49:37 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d | semmle.label | d |
| HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d | semmle.label | d |
| HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d | semmle.label | d |
| HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d | semmle.label | d |
| HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d | semmle.label | d |
| HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d | semmle.label | d |
| HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d | semmle.label | d |
| HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d | semmle.label | d |
| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:53:26:53:40 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:53:26:53:40 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e | semmle.label | e |
| HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e | semmle.label | e |
| HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e | semmle.label | e |
| HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e | semmle.label | e |
| HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e | semmle.label | e |
| HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e | semmle.label | e |
| HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e | semmle.label | e |
| HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e | semmle.label | e |
| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:57:24:57:38 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:57:24:57:38 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b | semmle.label | b |
| HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b | semmle.label | b |
| HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b | semmle.label | b |
| HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b | semmle.label | b |
| HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b | semmle.label | b |
| HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b | semmle.label | b |
| HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b | semmle.label | b |
| HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b | semmle.label | b |
| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:61:27:61:41 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:61:27:61:41 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f | semmle.label | f |
| HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f | semmle.label | f |
| HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f | semmle.label | f |
| HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f | semmle.label | f |
| HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f | semmle.label | f |
| HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f | semmle.label | f |
| HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f | semmle.label | f |
| HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f | semmle.label | f |
| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:65:24:65:38 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:65:24:65:38 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g | semmle.label | g |
| HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g | semmle.label | g |
| HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g | semmle.label | g |
| HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g | semmle.label | g |
| HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g | semmle.label | g |
| HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g | semmle.label | g |
| HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g | semmle.label | g |
| HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g | semmle.label | g |
| HTMLTemplateEscapingPassthrough.go:74:17:74:31 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:75:38:75:44 | escaped | semmle.label | escaped |
| HTMLTemplateEscapingPassthrough.go:80:10:80:24 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:80:10:80:24 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:81:16:81:33 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:83:38:83:40 | src | semmle.label | src |
| HTMLTemplateEscapingPassthrough.go:88:10:88:24 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:90:38:90:67 | call to HTMLEscapeString | semmle.label | call to HTMLEscapeString |
| HTMLTemplateEscapingPassthrough.go:90:64:90:66 | src | semmle.label | src |
| HTMLTemplateEscapingPassthrough.go:91:38:91:46 | converted | semmle.label | converted |
| HTMLTemplateEscapingPassthrough.go:91:38:91:46 | converted | semmle.label | converted |
| HTMLTemplateEscapingPassthrough.go:91:38:91:46 | converted | semmle.label | converted |
| HTMLTemplateEscapingPassthrough.go:91:38:91:46 | converted | semmle.label | converted |
| HTMLTemplateEscapingPassthrough.go:91:38:91:46 | converted | semmle.label | converted |
| HTMLTemplateEscapingPassthrough.go:91:38:91:46 | converted | semmle.label | converted |
| HTMLTemplateEscapingPassthrough.go:91:38:91:46 | converted | semmle.label | converted |
| HTMLTemplateEscapingPassthrough.go:29:12:29:41 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:29:12:29:41 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:29:12:29:41 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:29:26:29:40 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:29:26:29:40 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:30:39:30:39 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:30:39:30:39 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:35:9:35:38 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:35:9:35:38 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:35:9:35:38 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:35:23:35:37 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:35:23:35:37 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:36:40:36:40 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:36:40:36:40 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:40:9:40:34 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:40:9:40:34 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:40:9:40:34 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:40:19:40:33 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:40:19:40:33 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:41:40:41:40 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:41:40:41:40 | a | semmle.label | a |
| HTMLTemplateEscapingPassthrough.go:46:11:46:44 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:46:11:46:44 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:46:11:46:44 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:46:29:46:43 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:46:29:46:43 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:47:41:47:41 | c | semmle.label | c |
| HTMLTemplateEscapingPassthrough.go:47:41:47:41 | c | semmle.label | c |
| HTMLTemplateEscapingPassthrough.go:50:11:50:38 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:50:11:50:38 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:50:11:50:38 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:50:23:50:37 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:50:23:50:37 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:51:44:51:44 | d | semmle.label | d |
| HTMLTemplateEscapingPassthrough.go:51:44:51:44 | d | semmle.label | d |
| HTMLTemplateEscapingPassthrough.go:54:11:54:41 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:54:11:54:41 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:54:11:54:41 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:54:26:54:40 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:54:26:54:40 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:55:44:55:44 | e | semmle.label | e |
| HTMLTemplateEscapingPassthrough.go:55:44:55:44 | e | semmle.label | e |
| HTMLTemplateEscapingPassthrough.go:58:11:58:39 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:58:11:58:39 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:58:11:58:39 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:58:24:58:38 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:58:24:58:38 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:59:38:59:38 | b | semmle.label | b |
| HTMLTemplateEscapingPassthrough.go:59:38:59:38 | b | semmle.label | b |
| HTMLTemplateEscapingPassthrough.go:62:11:62:42 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:62:11:62:42 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:62:11:62:42 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:62:27:62:41 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:62:27:62:41 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:63:44:63:44 | f | semmle.label | f |
| HTMLTemplateEscapingPassthrough.go:63:44:63:44 | f | semmle.label | f |
| HTMLTemplateEscapingPassthrough.go:66:11:66:39 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:66:11:66:39 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:66:11:66:39 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:66:24:66:38 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:66:24:66:38 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:67:38:67:38 | g | semmle.label | g |
| HTMLTemplateEscapingPassthrough.go:67:38:67:38 | g | semmle.label | g |
| HTMLTemplateEscapingPassthrough.go:75:17:75:31 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:76:38:76:44 | escaped | semmle.label | escaped |
| HTMLTemplateEscapingPassthrough.go:81:10:81:24 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:81:10:81:24 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:82:16:82:33 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:84:38:84:40 | src | semmle.label | src |
| HTMLTemplateEscapingPassthrough.go:89:10:89:24 | call to UserAgent | semmle.label | call to UserAgent |
| HTMLTemplateEscapingPassthrough.go:91:16:91:77 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:91:16:91:77 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:91:38:91:67 | call to HTMLEscapeString | semmle.label | call to HTMLEscapeString |
| HTMLTemplateEscapingPassthrough.go:91:64:91:66 | src | semmle.label | src |
| HTMLTemplateEscapingPassthrough.go:92:38:92:46 | converted | semmle.label | converted |
| HTMLTemplateEscapingPassthrough.go:92:38:92:46 | converted | semmle.label | converted |
| HTMLTemplateEscapingPassthrough.go:101:9:101:14 | selection of Form | semmle.label | selection of Form |
| HTMLTemplateEscapingPassthrough.go:101:9:101:24 | call to Get | semmle.label | call to Get |
| HTMLTemplateEscapingPassthrough.go:104:18:104:18 | definition of x | semmle.label | definition of x |
| HTMLTemplateEscapingPassthrough.go:105:9:105:24 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:105:9:105:24 | type conversion | semmle.label | type conversion |
| HTMLTemplateEscapingPassthrough.go:108:35:108:35 | definition of x | semmle.label | definition of x |
| HTMLTemplateEscapingPassthrough.go:110:19:110:19 | x | semmle.label | x |
| HTMLTemplateEscapingPassthrough.go:115:8:115:15 | call to getId | semmle.label | call to getId |
| HTMLTemplateEscapingPassthrough.go:116:15:116:15 | x | semmle.label | x |
| HTMLTemplateEscapingPassthrough.go:123:11:123:36 | call to passthrough | semmle.label | call to passthrough |
subpaths
#select
| HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a | HTMLTemplateEscapingPassthrough.go:28:26:28:40 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a | Data from an $@ will not be auto-escaped because it was $@ to template.HTML | HTMLTemplateEscapingPassthrough.go:28:26:28:40 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion | converted |
| HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a | HTMLTemplateEscapingPassthrough.go:34:23:34:37 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a | Data from an $@ will not be auto-escaped because it was $@ to template.HTML | HTMLTemplateEscapingPassthrough.go:34:23:34:37 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion | converted |
| HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a | HTMLTemplateEscapingPassthrough.go:39:19:39:33 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a | Data from an $@ will not be auto-escaped because it was $@ to template.HTML | HTMLTemplateEscapingPassthrough.go:39:19:39:33 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion | converted |
| HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c | HTMLTemplateEscapingPassthrough.go:45:29:45:43 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c | Data from an $@ will not be auto-escaped because it was $@ to template.HTMLAttr | HTMLTemplateEscapingPassthrough.go:45:29:45:43 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion | converted |
| HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d | HTMLTemplateEscapingPassthrough.go:49:23:49:37 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d | Data from an $@ will not be auto-escaped because it was $@ to template.JS | HTMLTemplateEscapingPassthrough.go:49:23:49:37 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion | converted |
| HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e | HTMLTemplateEscapingPassthrough.go:53:26:53:40 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e | Data from an $@ will not be auto-escaped because it was $@ to template.JSStr | HTMLTemplateEscapingPassthrough.go:53:26:53:40 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion | converted |
| HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b | HTMLTemplateEscapingPassthrough.go:57:24:57:38 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b | Data from an $@ will not be auto-escaped because it was $@ to template.CSS | HTMLTemplateEscapingPassthrough.go:57:24:57:38 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion | converted |
| HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f | HTMLTemplateEscapingPassthrough.go:61:27:61:41 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f | Data from an $@ will not be auto-escaped because it was $@ to template.Srcset | HTMLTemplateEscapingPassthrough.go:61:27:61:41 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion | converted |
| HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g | HTMLTemplateEscapingPassthrough.go:65:24:65:38 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g | Data from an $@ will not be auto-escaped because it was $@ to template.URL | HTMLTemplateEscapingPassthrough.go:65:24:65:38 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion | converted |
| HTMLTemplateEscapingPassthrough.go:30:39:30:39 | a | HTMLTemplateEscapingPassthrough.go:29:26:29:40 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:30:39:30:39 | a | Data from an $@ will not be auto-escaped because it was $@ to template.HTML | HTMLTemplateEscapingPassthrough.go:29:26:29:40 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:29:12:29:41 | type conversion | converted |
| HTMLTemplateEscapingPassthrough.go:36:40:36:40 | a | HTMLTemplateEscapingPassthrough.go:35:23:35:37 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:36:40:36:40 | a | Data from an $@ will not be auto-escaped because it was $@ to template.HTML | HTMLTemplateEscapingPassthrough.go:35:23:35:37 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:35:9:35:38 | type conversion | converted |
| HTMLTemplateEscapingPassthrough.go:41:40:41:40 | a | HTMLTemplateEscapingPassthrough.go:40:19:40:33 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:41:40:41:40 | a | Data from an $@ will not be auto-escaped because it was $@ to template.HTML | HTMLTemplateEscapingPassthrough.go:40:19:40:33 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:40:9:40:34 | type conversion | converted |
| HTMLTemplateEscapingPassthrough.go:47:41:47:41 | c | HTMLTemplateEscapingPassthrough.go:46:29:46:43 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:47:41:47:41 | c | Data from an $@ will not be auto-escaped because it was $@ to template.HTMLAttr | HTMLTemplateEscapingPassthrough.go:46:29:46:43 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:46:11:46:44 | type conversion | converted |
| HTMLTemplateEscapingPassthrough.go:51:44:51:44 | d | HTMLTemplateEscapingPassthrough.go:50:23:50:37 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:51:44:51:44 | d | Data from an $@ will not be auto-escaped because it was $@ to template.JS | HTMLTemplateEscapingPassthrough.go:50:23:50:37 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:50:11:50:38 | type conversion | converted |
| HTMLTemplateEscapingPassthrough.go:55:44:55:44 | e | HTMLTemplateEscapingPassthrough.go:54:26:54:40 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:55:44:55:44 | e | Data from an $@ will not be auto-escaped because it was $@ to template.JSStr | HTMLTemplateEscapingPassthrough.go:54:26:54:40 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:54:11:54:41 | type conversion | converted |
| HTMLTemplateEscapingPassthrough.go:59:38:59:38 | b | HTMLTemplateEscapingPassthrough.go:58:24:58:38 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:59:38:59:38 | b | Data from an $@ will not be auto-escaped because it was $@ to template.CSS | HTMLTemplateEscapingPassthrough.go:58:24:58:38 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:58:11:58:39 | type conversion | converted |
| HTMLTemplateEscapingPassthrough.go:63:44:63:44 | f | HTMLTemplateEscapingPassthrough.go:62:27:62:41 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:63:44:63:44 | f | Data from an $@ will not be auto-escaped because it was $@ to template.Srcset | HTMLTemplateEscapingPassthrough.go:62:27:62:41 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:62:11:62:42 | type conversion | converted |
| HTMLTemplateEscapingPassthrough.go:67:38:67:38 | g | HTMLTemplateEscapingPassthrough.go:66:24:66:38 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:67:38:67:38 | g | Data from an $@ will not be auto-escaped because it was $@ to template.URL | HTMLTemplateEscapingPassthrough.go:66:24:66:38 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:66:11:66:39 | type conversion | converted |

View File

@@ -4,6 +4,7 @@ import (
"html/template"
"net/http"
"os"
"strconv"
)
func main() {}
@@ -91,3 +92,48 @@ func good(req *http.Request) {
checkError(tmpl.Execute(os.Stdout, converted))
}
}
// good: the following example demonstrates data flow from untrusted input
// to a passthrough type and data flow from a passthrough type to
// a template, but crucially no data flow from the untrusted input to the
// template without a sanitizer.
func getId(r *http.Request) string {
return r.Form.Get("id") // untrusted
}
func passthrough(x string) template.HTML {
return template.HTML(x) // passthrough type
}
func sink(wr http.ResponseWriter, x any) {
tmpl, _ := template.New("test").Parse(`Hello, {{.}}\n`)
tmpl.Execute(wr, x) // template sink
}
func source2waypoint() {
http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) {
x := getId(r)
passthrough(x) // untrusted input goes to the passthrough type
})
}
func waypoint2sink() {
http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) {
// passthrough type with trusted input goes to the sink
sink(w, passthrough("not tainted"))
})
}
func source2sinkSanitized() {
http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) {
x := getId(r) // untrusted input
// TODO: We expected this test to fail with the current implementation, since the A->C
// taint tracking configuration does not actually check for sanitizers. However, the
// sink in `sink` only gets flagged if we remove the line with the sanitizer here.
// While this behaviour is desired, it's unclear why it works right now.
// Once we rewrite the query using the new data flow implementation, we should
// probably use flow states for this query, which will then also address this issue.
y, _ := strconv.Atoi(x) // sanitizer
sink(w, y) // sink
})
}

View File

@@ -3,16 +3,22 @@ package main
import (
"fmt"
"html"
"html/template"
"net/http"
)
func serve1() {
var template template.Template
http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
username := r.Form.Get("username")
if !isValidUsername(username) {
// GOOD: a request parameter is escaped before being put into the response
fmt.Fprintf(w, "%q is an unknown user", html.EscapeString(username))
// GOOD: using html/template escapes values for us
template.Execute(w, username)
template.ExecuteTemplate(w, "test", username)
} else {
// TODO: do something exciting
}

View File

@@ -2,15 +2,18 @@ package main
import (
"html"
"html/template"
"io"
"io/ioutil"
"net/http"
)
func ListFiles1(w http.ResponseWriter, r *http.Request) {
var template template.Template
files, _ := ioutil.ReadDir(".")
for _, file := range files {
io.WriteString(w, html.EscapeString(file.Name())+"\n")
template.Execute(w, file.Name())
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -815,24 +815,20 @@ private module Cached {
)
}
private predicate store(
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
exists(ContentSet cs |
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
)
}
/**
* Holds if data can flow from `node1` to `node2` via a direct assignment to
* `f`.
* `c`.
*
* This includes reverse steps through reads when the result of the read has
* been stored into, in order to handle cases like `x.f1.f2 = y`.
*/
cached
predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) {
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
predicate store(
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
exists(ContentSet cs |
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
)
}
/**
@@ -932,36 +928,15 @@ private module Cached {
TReturnCtxNoFlowThrough() or
TReturnCtxMaybeFlowThrough(ReturnPosition pos)
cached
newtype TTypedContentApprox =
MkTypedContentApprox(ContentApprox c, DataFlowType t) {
exists(Content cont |
c = getContentApprox(cont) and
store(_, cont, _, _, t)
)
}
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
cached
TypedContent getATypedContent(TypedContentApprox c) {
exists(ContentApprox cls, DataFlowType t, Content cont |
c = MkTypedContentApprox(cls, pragma[only_bind_into](t)) and
result = MkTypedContent(cont, pragma[only_bind_into](t)) and
cls = getContentApprox(cont)
)
}
cached
newtype TAccessPathFront =
TFrontNil(DataFlowType t) or
TFrontHead(TypedContent tc)
TFrontNil() or
TFrontHead(Content c)
cached
newtype TApproxAccessPathFront =
TApproxFrontNil(DataFlowType t) or
TApproxFrontHead(TypedContentApprox tc)
TApproxFrontNil() or
TApproxFrontHead(ContentApprox c)
cached
newtype TAccessPathFrontOption =
@@ -986,8 +961,16 @@ predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
/**
* A `Node` at which a cast can occur such that the type should be checked.
*/
class CastingNode extends Node {
class CastingNode instanceof Node {
CastingNode() { castingNode(this) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
private predicate readStepWithTypes(
@@ -1135,9 +1118,17 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable)
* The value of a parameter at function entry, viewed as a node in a data
* flow graph.
*/
class ParamNode extends Node {
class ParamNode instanceof Node {
ParamNode() { parameterNode(this, _, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/**
* Holds if this node is the parameter of callable `c` at the specified
* position.
@@ -1146,9 +1137,17 @@ class ParamNode extends Node {
}
/** A data-flow node that represents a call argument. */
class ArgNode extends Node {
class ArgNode instanceof Node {
ArgNode() { argumentNode(this, _, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Holds if this argument occurs at the given position in the given call. */
final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
argumentNode(this, call, pos)
@@ -1159,9 +1158,17 @@ class ArgNode extends Node {
* A node from which flow can return to the caller. This is either a regular
* `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter.
*/
class ReturnNodeExt extends Node {
class ReturnNodeExt instanceof Node {
ReturnNodeExt() { returnNodeExt(this, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the kind of this returned value. */
ReturnKindExt getKind() { returnNodeExt(this, result) }
}
@@ -1170,8 +1177,16 @@ class ReturnNodeExt extends Node {
* A node to which data can flow from a call. Either an ordinary out node
* or a post-update node associated with a call argument.
*/
class OutNodeExt extends Node {
class OutNodeExt instanceof Node {
OutNodeExt() { outNodeExt(this) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
/**
@@ -1387,67 +1402,37 @@ class ReturnCtx extends TReturnCtx {
}
}
/** An approximated `Content` tagged with the type of a containing object. */
class TypedContentApprox extends MkTypedContentApprox {
private ContentApprox c;
private DataFlowType t;
TypedContentApprox() { this = MkTypedContentApprox(c, t) }
/** Gets a typed content approximated by this value. */
TypedContent getATypedContent() { result = getATypedContent(this) }
/** Gets the content. */
ContentApprox getContent() { result = c }
/** Gets the container type. */
DataFlowType getContainerType() { result = t }
/** Gets a textual representation of this approximated content. */
string toString() { result = c.toString() }
}
/**
* The front of an approximated access path. This is either a head or a nil.
*/
abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
abstract string toString();
abstract DataFlowType getType();
abstract boolean toBoolNonEmpty();
TypedContentApprox getHead() { this = TApproxFrontHead(result) }
ContentApprox getHead() { this = TApproxFrontHead(result) }
pragma[nomagic]
TypedContent getAHead() {
exists(TypedContentApprox cont |
Content getAHead() {
exists(ContentApprox cont |
this = TApproxFrontHead(cont) and
result = cont.getATypedContent()
cont = getContentApprox(result)
)
}
}
class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil {
private DataFlowType t;
ApproxAccessPathFrontNil() { this = TApproxFrontNil(t) }
override string toString() { result = ppReprType(t) }
override DataFlowType getType() { result = t }
override string toString() { result = "nil" }
override boolean toBoolNonEmpty() { result = false }
}
class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead {
private TypedContentApprox tc;
private ContentApprox c;
ApproxAccessPathFrontHead() { this = TApproxFrontHead(tc) }
ApproxAccessPathFrontHead() { this = TApproxFrontHead(c) }
override string toString() { result = tc.toString() }
override DataFlowType getType() { result = tc.getContainerType() }
override string toString() { result = c.toString() }
override boolean toBoolNonEmpty() { result = true }
}
@@ -1461,65 +1446,31 @@ class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption {
}
}
/** A `Content` tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;
private DataFlowType t;
TypedContent() { this = MkTypedContent(c, t) }
/** Gets the content. */
Content getContent() { result = c }
/** Gets the container type. */
DataFlowType getContainerType() { result = t }
/** Gets a textual representation of this content. */
string toString() { result = c.toString() }
/**
* Holds if access paths with this `TypedContent` at their head always should
* be tracked at high precision. This disables adaptive access path precision
* for such access paths.
*/
predicate forceHighPrecision() { forceHighPrecision(c) }
}
/**
* The front of an access path. This is either a head or a nil.
*/
abstract class AccessPathFront extends TAccessPathFront {
abstract string toString();
abstract DataFlowType getType();
abstract ApproxAccessPathFront toApprox();
TypedContent getHead() { this = TFrontHead(result) }
Content getHead() { this = TFrontHead(result) }
}
class AccessPathFrontNil extends AccessPathFront, TFrontNil {
private DataFlowType t;
override string toString() { result = "nil" }
AccessPathFrontNil() { this = TFrontNil(t) }
override string toString() { result = ppReprType(t) }
override DataFlowType getType() { result = t }
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil(t) }
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil() }
}
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
private TypedContent tc;
private Content c;
AccessPathFrontHead() { this = TFrontHead(tc) }
AccessPathFrontHead() { this = TFrontHead(c) }
override string toString() { result = tc.toString() }
override string toString() { result = c.toString() }
override DataFlowType getType() { result = tc.getContainerType() }
override ApproxAccessPathFront toApprox() { result.getAHead() = tc }
override ApproxAccessPathFront toApprox() { result.getAHead() = c }
}
/** An optional access path front. */

View File

@@ -139,9 +139,9 @@ module ThroughFlowConfig implements DataFlow::StateConfigSig {
predicate isAdditionalFlowStep(
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
) {
exists(DataFlowImplCommon::TypedContent tc |
DataFlowImplCommon::store(node1, tc, node2, _) and
isRelevantContent(tc.getContent()) and
exists(DataFlow::Content c |
DataFlowImplCommon::store(node1, c, node2, _, _) and
isRelevantContent(c) and
(
state1 instanceof TaintRead and state2.(TaintStore).getStep() = 1
or
@@ -178,7 +178,7 @@ string captureThroughFlow(DataFlowTargetApi api) {
string output
|
ThroughFlow::flow(p, returnNodeExt) and
returnNodeExt.getEnclosingCallable() = api and
returnNodeExt.(DataFlow::Node).getEnclosingCallable() = api and
input = parameterNodeAsInput(p) and
output = returnNodeAsOutput(returnNodeExt) and
input != output and

View File

@@ -73,7 +73,9 @@ private string isExtensible(J::RefType ref) {
}
private string typeAsModel(J::RefType type) {
result = type.getCompilationUnit().getPackage().getName() + ";" + type.nestedName()
result =
type.getCompilationUnit().getPackage().getName() + ";" +
type.getErasure().(J::RefType).nestedName()
}
private J::RefType bestTypeForModel(TargetApiSpecific api) {
@@ -184,7 +186,7 @@ string returnNodeAsOutput(DataFlowImplCommon::ReturnNodeExt node) {
exists(int pos |
pos = node.getKind().(DataFlowImplCommon::ParamUpdateReturnKind).getPosition()
|
result = parameterAccess(node.getEnclosingCallable().getParameter(pos))
result = parameterAccess(node.(DataFlow::Node).getEnclosingCallable().getParameter(pos))
or
result = qualifierString() and pos = -1
)
@@ -236,7 +238,7 @@ predicate apiSource(DataFlow::Node source) {
string asInputArgumentSpecific(DataFlow::Node source) {
exists(int pos |
source.(DataFlow::ParameterNode).isParameterOf(_, pos) and
result = "Argument[" + pos + "]"
if pos >= 0 then result = "Argument[" + pos + "]" else result = qualifierString()
)
or
source.asExpr() instanceof J::FieldAccess and

View File

@@ -21,6 +21,7 @@ import com.semmle.util.exception.Exceptions;
import com.semmle.util.io.WholeIO;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -32,7 +33,6 @@ public class JSONParser {
private int offset;
private int length;
private String src;
private List<ParseError> recoverableErrors;
public static Pair<JSONValue, List<ParseError>> parseValue(String json) throws ParseError {
JSONParser parser = new JSONParser(json);
@@ -41,14 +41,13 @@ public class JSONParser {
parser.consumeWhitespace();
if (parser.offset < parser.length) parser.raise("Expected end of input");
return Pair.make(value, parser.recoverableErrors);
return Pair.make(value, Collections.emptyList());
}
private JSONParser(String json) throws ParseError {
this.line = 1;
this.column = 0;
this.offset = 0;
this.recoverableErrors = new ArrayList<ParseError>();
if (json == null) raise("Input string may not be null");
this.length = json.length();
@@ -351,17 +350,16 @@ public class JSONParser {
}
}
/** Skips the line comment starting at the current position and records a recoverable error. */
/** Skips the line comment starting at the current position. */
private void skipLineComment() throws ParseError {
Position pos = new Position(line, column, offset);
char c;
next();
next();
while ((c = peek()) != '\r' && c != '\n' && c != -1) next();
recoverableErrors.add(new ParseError("Comments are not legal in JSON.", pos));
}
/** Skips the block comment starting at the current position and records a recoverable error. */
/** Skips the block comment starting at the current position. */
private void skipBlockComment() throws ParseError {
Position pos = new Position(line, column, offset);
char c;
@@ -376,7 +374,6 @@ public class JSONParser {
break;
}
} while (true);
recoverableErrors.add(new ParseError("Comments are not legal in JSON.", pos));
}
private void consume(char token) throws ParseError {

View File

@@ -18,15 +18,5 @@ locations_default(#20003,#10000,3,12,3,18)
json_locations(#20002,#20003)
json_literals("world","""world""",#20002)
json_properties(#20000,"hello",#20002)
#20004=*
json_errors(#20004,"Error: Comments are not legal in JSON.")
#20005=@"loc,{#10000},2,3,2,3"
locations_default(#20005,#10000,2,3,2,3)
json_locations(#20004,#20005)
#20006=*
json_errors(#20006,"Error: Comments are not legal in JSON.")
#20007=@"loc,{#10000},4,3,4,3"
locations_default(#20007,#10000,4,3,4,3)
json_locations(#20006,#20007)
numlines(#10000,5,0,0)
filetype(#10000,"json")

View File

@@ -491,6 +491,7 @@ module API {
* In other words, the value of a use of `that` may flow into the right-hand side of a
* definition of this node.
*/
pragma[inline]
predicate refersTo(Node that) { this.asSink() = that.getAValueReachableFromSource() }
/**

View File

@@ -421,6 +421,9 @@ module DOM {
t.startInProp("target") and
result = domEventSource()
or
t.startInProp(DataFlow::PseudoProperties::arrayElement()) and
result = domElementCollection()
or
exists(DataFlow::TypeTracker t2 | result = domValueRef(t2).track(t2, t))
}

View File

@@ -20,20 +20,20 @@ class ES2015Module extends Module {
override ModuleScope getScope() { result.getScopeElement() = this }
/** Gets the full path of the file containing this module. */
override string getPath() { result = getFile().getAbsolutePath() }
override string getPath() { result = this.getFile().getAbsolutePath() }
/** Gets the short name of this module without file extension. */
override string getName() { result = getFile().getStem() }
override string getName() { result = this.getFile().getStem() }
/** Gets an export declaration in this module. */
ExportDeclaration getAnExport() { result.getTopLevel() = this }
override DataFlow::Node getAnExportedValue(string name) {
exists(ExportDeclaration ed | ed = getAnExport() and result = ed.getSourceNode(name))
exists(ExportDeclaration ed | ed = this.getAnExport() and result = ed.getSourceNode(name))
}
/** Holds if this module exports variable `v` under the name `name`. */
predicate exportsAs(LexicalName v, string name) { getAnExport().exportsAs(v, name) }
predicate exportsAs(LexicalName v, string name) { this.getAnExport().exportsAs(v, name) }
override predicate isStrict() {
// modules are implicitly strict
@@ -86,9 +86,9 @@ private predicate hasBothNamedAndDefaultExports(ES2015Module mod) {
* ```
*/
class ImportDeclaration extends Stmt, Import, @import_declaration {
override ES2015Module getEnclosingModule() { result = getTopLevel() }
override ES2015Module getEnclosingModule() { result = this.getTopLevel() }
override PathExpr getImportedPath() { result = getChildExpr(-1) }
override PathExpr getImportedPath() { result = this.getChildExpr(-1) }
/**
* Gets the object literal passed as part of the `assert` clause in this import declaration.
@@ -101,24 +101,24 @@ class ImportDeclaration extends Stmt, Import, @import_declaration {
ObjectExpr getImportAssertion() { result = this.getChildExpr(-10) }
/** Gets the `i`th import specifier of this import declaration. */
ImportSpecifier getSpecifier(int i) { result = getChildExpr(i) }
ImportSpecifier getSpecifier(int i) { result = this.getChildExpr(i) }
/** Gets an import specifier of this import declaration. */
ImportSpecifier getASpecifier() { result = getSpecifier(_) }
ImportSpecifier getASpecifier() { result = this.getSpecifier(_) }
override DataFlow::Node getImportedModuleNode() {
// `import * as http from 'http'` or `import http from `http`'
exists(ImportSpecifier is |
is = getASpecifier() and
is = this.getASpecifier() and
result = DataFlow::valueNode(is)
|
is instanceof ImportNamespaceSpecifier and
count(getASpecifier()) = 1
count(this.getASpecifier()) = 1
or
// For compatibility with the non-standard implementation of default imports,
// treat default imports as namespace imports in cases where it can't cause ambiguity
// between named exports and the properties of a default-exported object.
not hasBothNamedAndDefaultExports(getImportedModule()) and
not hasBothNamedAndDefaultExports(this.getImportedModule()) and
is.getImportedName() = "default"
)
or
@@ -136,7 +136,7 @@ class ImportDeclaration extends Stmt, Import, @import_declaration {
private class LiteralImportPath extends PathExpr, ConstantString {
LiteralImportPath() { exists(ImportDeclaration req | this = req.getChildExpr(-1)) }
override string getValue() { result = getStringValue() }
override string getValue() { result = this.getStringValue() }
}
/**
@@ -159,7 +159,7 @@ private class LiteralImportPath extends PathExpr, ConstantString {
*/
class ImportSpecifier extends Expr, @import_specifier {
/** Gets the imported symbol; undefined for default and namespace import specifiers. */
Identifier getImported() { result = getChildExpr(0) }
Identifier getImported() { result = this.getChildExpr(0) }
/**
* Gets the name of the imported symbol.
@@ -176,10 +176,10 @@ class ImportSpecifier extends Expr, @import_specifier {
* The names of the imported symbols for the first three of them are, respectively,
* `x`, `y` and `default`, while the last one does not import an individual symbol.
*/
string getImportedName() { result = getImported().getName() }
string getImportedName() { result = this.getImported().getName() }
/** Gets the local variable into which this specifier imports. */
VarDecl getLocal() { result = getChildExpr(1) }
VarDecl getLocal() { result = this.getChildExpr(1) }
override string getAPrimaryQlClass() { result = "ImportSpecifier" }
@@ -240,10 +240,10 @@ class ImportNamespaceSpecifier extends ImportSpecifier, @import_namespace_specif
* ```
*/
class BulkImportDeclaration extends ImportDeclaration {
BulkImportDeclaration() { getASpecifier() instanceof ImportNamespaceSpecifier }
BulkImportDeclaration() { this.getASpecifier() instanceof ImportNamespaceSpecifier }
/** Gets the local namespace variable under which the module is imported. */
VarDecl getLocal() { result = getASpecifier().getLocal() }
VarDecl getLocal() { result = this.getASpecifier().getLocal() }
}
/**
@@ -260,12 +260,12 @@ class SelectiveImportDeclaration extends ImportDeclaration {
/** Holds if `local` is the local variable into which `imported` is imported. */
predicate importsAs(string imported, LexicalDecl local) {
exists(ImportSpecifier spec | spec = getASpecifier() |
exists(ImportSpecifier spec | spec = this.getASpecifier() |
imported = spec.getImported().getName() and
local = spec.getLocal()
)
or
imported = "default" and local = getASpecifier().(ImportDefaultSpecifier).getLocal()
imported = "default" and local = this.getASpecifier().(ImportDefaultSpecifier).getLocal()
}
}
@@ -347,15 +347,15 @@ abstract class ExportDeclaration extends Stmt, @export_declaration {
*/
class BulkReExportDeclaration extends ReExportDeclaration, @export_all_declaration {
/** Gets the name of the module from which this declaration re-exports. */
override ConstantString getImportedPath() { result = getChildExpr(0) }
override ConstantString getImportedPath() { result = this.getChildExpr(0) }
override predicate exportsAs(LexicalName v, string name) {
getReExportedES2015Module().exportsAs(v, name) and
this.getReExportedES2015Module().exportsAs(v, name) and
not isShadowedFromBulkExport(this, name)
}
override DataFlow::Node getSourceNode(string name) {
result = getReExportedES2015Module().getAnExport().getSourceNode(name)
result = this.getReExportedES2015Module().getAnExport().getSourceNode(name)
}
}
@@ -392,22 +392,22 @@ private predicate isShadowedFromBulkExport(BulkReExportDeclaration reExport, str
*/
class ExportDefaultDeclaration extends ExportDeclaration, @export_default_declaration {
/** Gets the operand statement or expression that is exported by this declaration. */
ExprOrStmt getOperand() { result = getChild(0) }
ExprOrStmt getOperand() { result = this.getChild(0) }
override predicate exportsAs(LexicalName v, string name) {
name = "default" and v = getADecl().getVariable()
name = "default" and v = this.getADecl().getVariable()
}
/** Gets the declaration, if any, exported by this default export. */
VarDecl getADecl() {
exists(ExprOrStmt op | op = getOperand() |
exists(ExprOrStmt op | op = this.getOperand() |
result = op.(FunctionDeclStmt).getIdentifier() or
result = op.(ClassDeclStmt).getIdentifier()
)
}
override DataFlow::Node getSourceNode(string name) {
name = "default" and result = DataFlow::valueNode(getOperand())
name = "default" and result = DataFlow::valueNode(this.getOperand())
}
}
@@ -424,7 +424,7 @@ class ExportDefaultDeclaration extends ExportDeclaration, @export_default_declar
*/
class ExportNamedDeclaration extends ExportDeclaration, @export_named_declaration {
/** Gets the operand statement or expression that is exported by this declaration. */
ExprOrStmt getOperand() { result = getChild(-1) }
ExprOrStmt getOperand() { result = this.getChild(-1) }
/**
* Gets an identifier, if any, exported as part of a declaration by this named export.
@@ -433,7 +433,7 @@ class ExportNamedDeclaration extends ExportDeclaration, @export_named_declaratio
* That is, it includes the `v` in `export var v` but not in `export {v}`.
*/
Identifier getAnExportedDecl() {
exists(ExprOrStmt op | op = getOperand() |
exists(ExprOrStmt op | op = this.getOperand() |
result = op.(DeclStmt).getADecl().getBindingPattern().getABindingVarRef() or
result = op.(FunctionDeclStmt).getIdentifier() or
result = op.(ClassDeclStmt).getIdentifier() or
@@ -446,14 +446,14 @@ class ExportNamedDeclaration extends ExportDeclaration, @export_named_declaratio
}
/** Gets the variable declaration, if any, exported by this named export. */
VarDecl getADecl() { result = getAnExportedDecl() }
VarDecl getADecl() { result = this.getAnExportedDecl() }
override predicate exportsAs(LexicalName v, string name) {
exists(LexicalDecl vd | vd = getAnExportedDecl() |
exists(LexicalDecl vd | vd = this.getAnExportedDecl() |
name = vd.getName() and v = vd.getALexicalName()
)
or
exists(ExportSpecifier spec | spec = getASpecifier() and name = spec.getExportedName() |
exists(ExportSpecifier spec | spec = this.getASpecifier() and name = spec.getExportedName() |
v = spec.getLocal().(LexicalAccess).getALexicalName()
or
this.(ReExportDeclaration).getReExportedES2015Module().exportsAs(v, spec.getLocalName())
@@ -461,20 +461,20 @@ class ExportNamedDeclaration extends ExportDeclaration, @export_named_declaratio
}
override DataFlow::Node getSourceNode(string name) {
exists(VarDef d | d.getTarget() = getADecl() |
exists(VarDef d | d.getTarget() = this.getADecl() |
name = d.getTarget().(VarDecl).getName() and
result = DataFlow::valueNode(d.getSource())
)
or
exists(ObjectPattern obj | obj = getOperand().(DeclStmt).getADecl().getBindingPattern() |
exists(ObjectPattern obj | obj = this.getOperand().(DeclStmt).getADecl().getBindingPattern() |
exists(DataFlow::PropRead read | read = result |
read.getBase() = obj.flow() and
name = read.getPropertyName()
)
)
or
exists(ExportSpecifier spec | spec = getASpecifier() and name = spec.getExportedName() |
not exists(getImportedPath()) and result = DataFlow::valueNode(spec.getLocal())
exists(ExportSpecifier spec | spec = this.getASpecifier() and name = spec.getExportedName() |
not exists(this.getImportedPath()) and result = DataFlow::valueNode(spec.getLocal())
or
exists(ReExportDeclaration red | red = this |
result = red.getReExportedES2015Module().getAnExport().getSourceNode(spec.getLocalName())
@@ -483,20 +483,20 @@ class ExportNamedDeclaration extends ExportDeclaration, @export_named_declaratio
}
/** Gets the module from which the exports are taken if this is a re-export. */
ConstantString getImportedPath() { result = getChildExpr(-2) }
ConstantString getImportedPath() { result = this.getChildExpr(-2) }
/** Gets the `i`th export specifier of this declaration. */
ExportSpecifier getSpecifier(int i) { result = getChildExpr(i) }
ExportSpecifier getSpecifier(int i) { result = this.getChildExpr(i) }
/** Gets an export specifier of this declaration. */
ExportSpecifier getASpecifier() { result = getSpecifier(_) }
ExportSpecifier getASpecifier() { result = this.getSpecifier(_) }
}
/**
* An export declaration with the `type` modifier.
*/
private class TypeOnlyExportDeclaration extends ExportNamedDeclaration {
TypeOnlyExportDeclaration() { isTypeOnly() }
TypeOnlyExportDeclaration() { this.isTypeOnly() }
override predicate exportsAs(LexicalName v, string name) {
super.exportsAs(v, name) and
@@ -530,13 +530,13 @@ private class TypeOnlyExportDeclaration extends ExportNamedDeclaration {
*/
class ExportSpecifier extends Expr, @exportspecifier {
/** Gets the declaration to which this specifier belongs. */
ExportDeclaration getExportDeclaration() { result = getParent() }
ExportDeclaration getExportDeclaration() { result = this.getParent() }
/** Gets the local symbol that is being exported. */
Identifier getLocal() { result = getChildExpr(0) }
Identifier getLocal() { result = this.getChildExpr(0) }
/** Gets the name under which the symbol is exported. */
Identifier getExported() { result = getChildExpr(1) }
Identifier getExported() { result = this.getChildExpr(1) }
/**
* Gets the local name of the exported symbol, that is, the name
@@ -562,7 +562,7 @@ class ExportSpecifier extends Expr, @exportspecifier {
* The sixth one (unlike the fourth one) _does_ have a local name
* (that is, `default`), since it is a re-export.
*/
string getLocalName() { result = getLocal().getName() }
string getLocalName() { result = this.getLocal().getName() }
/**
* Gets the name under which the symbol is exported.
@@ -581,7 +581,7 @@ class ExportSpecifier extends Expr, @exportspecifier {
* `x`, `z`, `f` and `default`, while the last one does not have
* an exported name since it does not export a unique symbol.
*/
string getExportedName() { result = getExported().getName() }
string getExportedName() { result = this.getExported().getName() }
override string getAPrimaryQlClass() { result = "ExportSpecifier" }
}
@@ -630,11 +630,11 @@ class ExportDefaultSpecifier extends ExportSpecifier, @export_default_specifier
* ```
*/
class ReExportDefaultSpecifier extends ExportDefaultSpecifier {
ReExportDefaultSpecifier() { getExportDeclaration() instanceof ReExportDeclaration }
ReExportDefaultSpecifier() { this.getExportDeclaration() instanceof ReExportDeclaration }
override string getLocalName() { result = "default" }
override string getExportedName() { result = getExported().getName() }
override string getExportedName() { result = this.getExported().getName() }
}
/**
@@ -671,15 +671,15 @@ abstract class ReExportDeclaration extends ExportDeclaration {
abstract ConstantString getImportedPath();
/** Gets the module from which this declaration re-exports, if it is an ES2015 module. */
ES2015Module getReExportedES2015Module() { result = getReExportedModule() }
ES2015Module getReExportedES2015Module() { result = this.getReExportedModule() }
/** Gets the module from which this declaration re-exports. */
cached
Module getReExportedModule() {
Stages::Imports::ref() and
result.getFile() = getEnclosingModule().resolve(getImportedPath())
result.getFile() = this.getEnclosingModule().resolve(this.getImportedPath())
or
result = resolveFromTypeRoot()
result = this.resolveFromTypeRoot()
}
/**
@@ -689,9 +689,9 @@ abstract class ReExportDeclaration extends ExportDeclaration {
result.getFile() =
min(TypeRootFolder typeRoot |
|
typeRoot.getModuleFile(getImportedPath().getStringValue())
typeRoot.getModuleFile(this.getImportedPath().getStringValue())
order by
typeRoot.getSearchPriority(getFile().getParentContainer())
typeRoot.getSearchPriority(this.getFile().getParentContainer())
)
}
}
@@ -700,7 +700,7 @@ abstract class ReExportDeclaration extends ExportDeclaration {
private class LiteralReExportPath extends PathExpr, ConstantString {
LiteralReExportPath() { exists(ReExportDeclaration bred | this = bred.getImportedPath()) }
override string getValue() { result = getStringValue() }
override string getValue() { result = this.getStringValue() }
}
/**

View File

@@ -143,7 +143,7 @@ abstract class Configuration extends string {
* indicating whether the step preserves values or just taintedness.
*/
predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node trg, boolean valuePreserving) {
isAdditionalFlowStep(src, trg) and valuePreserving = true
this.isAdditionalFlowStep(src, trg) and valuePreserving = true
}
/**
@@ -205,7 +205,7 @@ abstract class Configuration extends string {
isSource(_, this, _) and
isSink(_, this, _) and
exists(SourcePathNode flowsource, SinkPathNode flowsink |
hasFlowPath(flowsource, flowsink) and
this.hasFlowPath(flowsource, flowsink) and
source = flowsource.getNode() and
sink = flowsink.getNode()
)
@@ -297,7 +297,7 @@ abstract class FlowLabel extends string {
* Holds if this is one of the standard flow labels `FlowLabel::data()`
* or `FlowLabel::taint()`.
*/
final predicate isDataOrTaint() { isData() or isTaint() }
final predicate isDataOrTaint() { this.isData() or this.isTaint() }
}
/**
@@ -1726,7 +1726,7 @@ class PathNode extends TPathNode {
/**
* Gets a flow label for the path node.
*/
FlowLabel getFlowLabel() { result = getPathSummary().getEndLabel() }
FlowLabel getFlowLabel() { result = this.getPathSummary().getEndLabel() }
}
/** Gets the mid node corresponding to `src`. */
@@ -1957,15 +1957,15 @@ private class BarrierGuardFunction extends Function {
|
exists(SsaExplicitDefinition ssa |
ssa.getDef().getSource() = returnExpr and
ssa.getVariable().getAUse() = getAReturnedExpr()
ssa.getVariable().getAUse() = this.getAReturnedExpr()
)
or
returnExpr = getAReturnedExpr()
returnExpr = this.getAReturnedExpr()
) and
sanitizedParameter.flowsToExpr(e) and
barrierGuardBlocksExpr(guard, guardOutcome, e, label)
) and
sanitizedParameter.getParameter() = getParameter(paramIndex)
sanitizedParameter.getParameter() = this.getParameter(paramIndex)
}
/**

View File

@@ -547,4 +547,10 @@ module Angular2 {
)
}
}
private class DomValueSources extends DOM::DomValueSource::Range {
DomValueSources() {
this = API::Node::ofType("@angular/core", "ElementRef").getMember("nativeElement").asSource()
}
}
}

View File

@@ -106,10 +106,10 @@ private predicate isBrowserifyDependencyMap(ObjectExpr deps) {
* or their name must contain the substring "webpack_require"
* or "webpack_module_template_argument".
*/
private predicate isWebpackModule(FunctionExpr m) {
private predicate isWebpackModule(Function m) {
forex(Parameter parm | parm = m.getAParameter() |
exists(string name | name = parm.getName() |
name.regexpMatch("module|exports|.*webpack_require.*|.*webpack_module_template_argument.*")
name.regexpMatch("module|exports|.*webpack_require.*|.*webpack_module_template_argument.*|.*unused_webpack_module.*")
)
)
}
@@ -161,6 +161,23 @@ predicate isWebpackBundle(ArrayExpr ae) {
)
}
/**
* Holds if `object` looks like a Webpack bundle of form:
* ```javascript
* var __webpack_modules__ = ({
* "file1": ((module, __webpack__exports__, __webpack_require__) => ...)
* ...
* })
* ```
*/
predicate isWebpackNamedBundle(ObjectExpr object) {
isWebpackModule(object.getAProperty().getInit().getUnderlyingValue()) and
exists(VarDef def |
def.getSource().(Expr).getUnderlyingValue() = object and
def.getTarget().(VarRef).getName() = "__webpack_modules__"
)
}
/**
* Holds if `tl` is a collection of concatenated files by [atpackager](https://github.com/ariatemplates/atpackager).
*/
@@ -233,7 +250,8 @@ predicate isDirectiveBundle(TopLevel tl) { exists(BundleDirective d | d.getTopLe
predicate isBundle(TopLevel tl) {
exists(Expr e | e.getTopLevel() = tl |
isBrowserifyBundle(e) or
isWebpackBundle(e)
isWebpackBundle(e) or
isWebpackNamedBundle(e)
)
or
isMultiPartBundle(tl)

View File

@@ -627,6 +627,15 @@ private module Forge {
// require("forge").md.md5.create().update('The quick brown fox jumps over the lazy dog');
this =
getAnImportNode().getMember("md").getMember(algorithmName).getMember("create").getACall()
or
// require("forge").sha512.sha256.create().update('The quick brown fox jumps over the lazy dog');
this =
getAnImportNode()
.getMember("md")
.getMember(algorithmName)
.getAMember()
.getMember("create")
.getACall()
)
}

View File

@@ -248,7 +248,7 @@ module NextJS {
* Gets a reference to a [Next.js router](https://nextjs.org/docs/api-reference/next/router).
*/
DataFlow::SourceNode nextRouter() {
result = DataFlow::moduleMember("next/router", "useRouter").getACall()
result = API::moduleImport("next/router").getMember("useRouter").getACall()
or
result =
API::moduleImport("next/router")

View File

@@ -25,25 +25,36 @@ module UnsafeDeserialization {
/** A source of remote user input, considered as a flow source for unsafe deserialization. */
class RemoteFlowSourceAsSource extends Source instanceof RemoteFlowSource { }
private API::Node unsafeYamlSchema() {
result = API::moduleImport("js-yaml").getMember("DEFAULT_FULL_SCHEMA") // from older versions
or
result = API::moduleImport("js-yaml-js-types").getMember(["all", "function"])
or
result = unsafeYamlSchema().getMember("extend").getReturn()
or
exists(API::CallNode call |
call.getAParameter().refersTo(unsafeYamlSchema()) and
call.getCalleeName() = "extend" and
result = call.getReturn()
)
}
/**
* An expression passed to one of the unsafe load functions of the `js-yaml` package.
*
* `js-yaml` since v4 defaults to being safe, but is unsafe when invoked with a schema
* that permits unsafe values.
*/
class JsYamlUnsafeLoad extends Sink {
JsYamlUnsafeLoad() {
exists(DataFlow::ModuleImportNode mi | mi.getPath() = "js-yaml" |
// the first argument to a call to `load` or `loadAll`
exists(string n | n = "load" or n = "loadAll" | this = mi.getAMemberCall(n).getArgument(0))
or
// the first argument to a call to `safeLoad` or `safeLoadAll` where
// the schema is specified to be `DEFAULT_FULL_SCHEMA`
exists(string n, DataFlow::CallNode c, DataFlow::Node fullSchema |
n = "safeLoad" or n = "safeLoadAll"
|
c = mi.getAMemberCall(n) and
this = c.getArgument(0) and
fullSchema = c.getOptionArgument(c.getNumArgument() - 1, "schema") and
mi.getAPropertyRead("DEFAULT_FULL_SCHEMA").flowsTo(fullSchema)
)
exists(API::CallNode call |
// Note: we include the old 'safeLoad' and 'safeLoadAll' functon because they were also unsafe when invoked with an unsafe schema.
call =
API::moduleImport("js-yaml")
.getMember(["load", "loadAll", "safeLoad", "safeLoadAll"])
.getACall() and
call.getAParameter().getMember("schema").refersTo(unsafeYamlSchema()) and
this = call.getArgument(0)
)
}
}

View File

@@ -4,6 +4,7 @@
private import codeql.regex.nfa.NfaUtils as NfaUtils
private import codeql.regex.RegexTreeView
private import semmle.javascript.frameworks.Bundling
/** An implementation that parses a regular expression into a tree of `RegExpTerm`s. */
module RegExpTreeView implements RegexTreeViewSig {
@@ -42,7 +43,11 @@ module RegExpTreeView implements RegexTreeViewSig {
*
* For javascript we make the pragmatic performance optimization to ignore minified files.
*/
predicate isExcluded(RegExpParent parent) { parent.(Expr).getTopLevel().isMinified() }
predicate isExcluded(RegExpParent parent) {
parent.(Expr).getTopLevel().isMinified()
or
isBundle(parent.(Expr).getTopLevel())
}
/**
* Holds if `root` has the `i` flag for case-insensitive matching.

View File

@@ -0,0 +1,5 @@
---
category: minorAnalysis
---
* The Forge module in `CryptoLibraries.qll` now correctly classifies SHA-512/224,
SHA-512/256, and SHA-512/384 hashes used in message digests as NonKeyCiphers.

View File

@@ -0,0 +1,5 @@
---
category: minorAnalysis
---
* The `js/unsafe-deserialization` query no longer flags deserialization through the `js-yaml` library, except
when it is used with an unsafe schema.

View File

@@ -0,0 +1,5 @@
---
category: fix
---
* Fixed a spurious diagnostic warning about comments in JSON files being illegal.
Comments in JSON files are in fact fully supported, and the diagnostic message was misleading.

View File

@@ -3,6 +3,7 @@ test_documentRef
| event-handler-receiver.js:1:1:1:8 | document |
| event-handler-receiver.js:5:1:5:8 | document |
| nameditems.js:1:1:1:8 | document |
| querySelectorAll.js:2:5:2:12 | document |
test_locationRef
| customization.js:3:3:3:14 | doc.location |
test_domValueRef
@@ -20,5 +21,8 @@ test_domValueRef
| nameditems.js:1:1:1:23 | documen ... entById |
| nameditems.js:1:1:1:30 | documen ... ('foo') |
| nameditems.js:1:1:2:19 | documen ... em('x') |
| querySelectorAll.js:2:5:2:29 | documen ... ctorAll |
| querySelectorAll.js:2:5:2:36 | documen ... ('foo') |
| querySelectorAll.js:2:46:2:48 | elm |
| tst.js:49:3:49:8 | window |
| tst.js:50:3:50:8 | window |

View File

@@ -0,0 +1,5 @@
(function() {
document.querySelectorAll('foo').forEach(elm => {
elm.innerHTML = 'hey';
});
});

View File

@@ -1 +0,0 @@
| invalid.json:3:1:3:1 | Error: Comments are not legal in JSON. |

View File

@@ -1,4 +1,4 @@
import { Component } from "@angular/core";
import { Component,ElementRef } from "@angular/core";
import { DomSanitizer } from '@angular/platform-browser';
@Component({
@@ -9,6 +9,7 @@ export class Source {
taint: string;
taintedArray: string[];
safeArray: string[];
elementRef: ElementRef;
constructor(private sanitizer: DomSanitizer) {
this.taint = source();
@@ -18,5 +19,6 @@ export class Source {
methodOnComponent(x) {
this.sanitizer.bypassSecurityTrustHtml(x);
this.elementRef.nativeElement.innerHTML = x;
}
}

View File

@@ -24,13 +24,14 @@ pipeClassRef
taintFlow
| inline.component.ts:15:22:15:29 | source() | sink.component.ts:28:48:28:57 | this.sink7 |
| inline.component.ts:15:22:15:29 | source() | sink.component.ts:30:48:30:57 | this.sink9 |
| source.component.ts:14:22:14:29 | source() | TestPipe.ts:6:31:6:35 | value |
| source.component.ts:14:22:14:29 | source() | sink.component.ts:22:48:22:57 | this.sink1 |
| source.component.ts:14:22:14:29 | source() | sink.component.ts:25:48:25:57 | this.sink4 |
| source.component.ts:14:22:14:29 | source() | sink.component.ts:26:48:26:57 | this.sink5 |
| source.component.ts:14:22:14:29 | source() | sink.component.ts:27:48:27:57 | this.sink6 |
| source.component.ts:14:22:14:29 | source() | sink.component.ts:29:48:29:57 | this.sink8 |
| source.component.ts:14:22:14:29 | source() | source.component.ts:20:48:20:48 | x |
| source.component.ts:15:33:15:40 | source() | sink.component.ts:22:48:22:57 | this.sink1 |
| source.component.ts:15:22:15:29 | source() | TestPipe.ts:6:31:6:35 | value |
| source.component.ts:15:22:15:29 | source() | sink.component.ts:22:48:22:57 | this.sink1 |
| source.component.ts:15:22:15:29 | source() | sink.component.ts:25:48:25:57 | this.sink4 |
| source.component.ts:15:22:15:29 | source() | sink.component.ts:26:48:26:57 | this.sink5 |
| source.component.ts:15:22:15:29 | source() | sink.component.ts:27:48:27:57 | this.sink6 |
| source.component.ts:15:22:15:29 | source() | sink.component.ts:29:48:29:57 | this.sink8 |
| source.component.ts:15:22:15:29 | source() | source.component.ts:21:48:21:48 | x |
| source.component.ts:15:22:15:29 | source() | source.component.ts:22:51:22:51 | x |
| source.component.ts:16:33:16:40 | source() | sink.component.ts:22:48:22:57 | this.sink1 |
testAttrSourceLocation
| inline.component.ts:8:43:8:60 | [testAttr]=taint | inline.component.ts:8:55:8:59 | <toplevel> |

View File

@@ -579,6 +579,13 @@ nodes
| react-use-router.js:23:43:23:54 | router.query |
| react-use-router.js:23:43:23:61 | router.query.foobar |
| react-use-router.js:23:43:23:61 | router.query.foobar |
| react-use-router.js:29:9:29:30 | router |
| react-use-router.js:29:18:29:30 | myUseRouter() |
| react-use-router.js:33:21:33:26 | router |
| react-use-router.js:33:21:33:32 | router.query |
| react-use-router.js:33:21:33:32 | router.query |
| react-use-router.js:33:21:33:39 | router.query.foobar |
| react-use-router.js:33:21:33:39 | router.query.foobar |
| react-use-state.js:4:9:4:49 | state |
| react-use-state.js:4:9:4:49 | state |
| react-use-state.js:4:10:4:14 | state |
@@ -1749,6 +1756,14 @@ edges
| react-use-router.js:23:43:23:54 | router.query | react-use-router.js:23:43:23:61 | router.query.foobar |
| react-use-router.js:23:43:23:54 | router.query | react-use-router.js:23:43:23:61 | router.query.foobar |
| react-use-router.js:23:43:23:61 | router.query.foobar | react-use-router.js:22:17:22:22 | router |
| react-use-router.js:29:9:29:30 | router | react-use-router.js:33:21:33:26 | router |
| react-use-router.js:29:18:29:30 | myUseRouter() | react-use-router.js:29:9:29:30 | router |
| react-use-router.js:33:21:33:26 | router | react-use-router.js:33:21:33:32 | router.query |
| react-use-router.js:33:21:33:32 | router.query | react-use-router.js:33:21:33:39 | router.query.foobar |
| react-use-router.js:33:21:33:32 | router.query | react-use-router.js:33:21:33:39 | router.query.foobar |
| react-use-router.js:33:21:33:32 | router.query | react-use-router.js:33:21:33:39 | router.query.foobar |
| react-use-router.js:33:21:33:32 | router.query | react-use-router.js:33:21:33:39 | router.query.foobar |
| react-use-router.js:33:21:33:39 | router.query.foobar | react-use-router.js:29:18:29:30 | myUseRouter() |
| react-use-state.js:4:9:4:49 | state | react-use-state.js:5:51:5:55 | state |
| react-use-state.js:4:9:4:49 | state | react-use-state.js:5:51:5:55 | state |
| react-use-state.js:4:9:4:49 | state | react-use-state.js:5:51:5:55 | state |
@@ -2447,6 +2462,7 @@ edges
| react-use-router.js:11:24:11:42 | router.query.foobar | react-use-router.js:8:21:8:32 | router.query | react-use-router.js:11:24:11:42 | router.query.foobar | Cross-site scripting vulnerability due to $@. | react-use-router.js:8:21:8:32 | router.query | user-provided value |
| react-use-router.js:11:24:11:42 | router.query.foobar | react-use-router.js:11:24:11:35 | router.query | react-use-router.js:11:24:11:42 | router.query.foobar | Cross-site scripting vulnerability due to $@. | react-use-router.js:11:24:11:35 | router.query | user-provided value |
| react-use-router.js:23:43:23:61 | router.query.foobar | react-use-router.js:23:43:23:54 | router.query | react-use-router.js:23:43:23:61 | router.query.foobar | Cross-site scripting vulnerability due to $@. | react-use-router.js:23:43:23:54 | router.query | user-provided value |
| react-use-router.js:33:21:33:39 | router.query.foobar | react-use-router.js:33:21:33:32 | router.query | react-use-router.js:33:21:33:39 | router.query.foobar | Cross-site scripting vulnerability due to $@. | react-use-router.js:33:21:33:32 | router.query | user-provided value |
| react-use-state.js:5:51:5:55 | state | react-use-state.js:4:38:4:48 | window.name | react-use-state.js:5:51:5:55 | state | Cross-site scripting vulnerability due to $@. | react-use-state.js:4:38:4:48 | window.name | user-provided value |
| react-use-state.js:11:51:11:55 | state | react-use-state.js:10:14:10:24 | window.name | react-use-state.js:11:51:11:55 | state | Cross-site scripting vulnerability due to $@. | react-use-state.js:10:14:10:24 | window.name | user-provided value |
| react-use-state.js:17:51:17:55 | state | react-use-state.js:16:20:16:30 | window.name | react-use-state.js:17:51:17:55 | state | Cross-site scripting vulnerability due to $@. | react-use-state.js:16:20:16:30 | window.name | user-provided value |

View File

@@ -591,6 +591,13 @@ nodes
| react-use-router.js:23:43:23:54 | router.query |
| react-use-router.js:23:43:23:61 | router.query.foobar |
| react-use-router.js:23:43:23:61 | router.query.foobar |
| react-use-router.js:29:9:29:30 | router |
| react-use-router.js:29:18:29:30 | myUseRouter() |
| react-use-router.js:33:21:33:26 | router |
| react-use-router.js:33:21:33:32 | router.query |
| react-use-router.js:33:21:33:32 | router.query |
| react-use-router.js:33:21:33:39 | router.query.foobar |
| react-use-router.js:33:21:33:39 | router.query.foobar |
| react-use-state.js:4:9:4:49 | state |
| react-use-state.js:4:9:4:49 | state |
| react-use-state.js:4:10:4:14 | state |
@@ -1811,6 +1818,14 @@ edges
| react-use-router.js:23:43:23:54 | router.query | react-use-router.js:23:43:23:61 | router.query.foobar |
| react-use-router.js:23:43:23:54 | router.query | react-use-router.js:23:43:23:61 | router.query.foobar |
| react-use-router.js:23:43:23:61 | router.query.foobar | react-use-router.js:22:17:22:22 | router |
| react-use-router.js:29:9:29:30 | router | react-use-router.js:33:21:33:26 | router |
| react-use-router.js:29:18:29:30 | myUseRouter() | react-use-router.js:29:9:29:30 | router |
| react-use-router.js:33:21:33:26 | router | react-use-router.js:33:21:33:32 | router.query |
| react-use-router.js:33:21:33:32 | router.query | react-use-router.js:33:21:33:39 | router.query.foobar |
| react-use-router.js:33:21:33:32 | router.query | react-use-router.js:33:21:33:39 | router.query.foobar |
| react-use-router.js:33:21:33:32 | router.query | react-use-router.js:33:21:33:39 | router.query.foobar |
| react-use-router.js:33:21:33:32 | router.query | react-use-router.js:33:21:33:39 | router.query.foobar |
| react-use-router.js:33:21:33:39 | router.query.foobar | react-use-router.js:29:18:29:30 | myUseRouter() |
| react-use-state.js:4:9:4:49 | state | react-use-state.js:5:51:5:55 | state |
| react-use-state.js:4:9:4:49 | state | react-use-state.js:5:51:5:55 | state |
| react-use-state.js:4:9:4:49 | state | react-use-state.js:5:51:5:55 | state |

View File

@@ -0,0 +1,2 @@
import { useRouter } from "next/router";
export let myUseRouter = useRouter;

View File

@@ -23,3 +23,15 @@ function Page({ router }) {
return <span onClick={() => router.push(router.query.foobar)}>Click to XSS 3</span> // NOT OK
}
export const pageWithRouter = withRouter(Page);
import { myUseRouter } from './react-use-router-lib';
export function nextRouterWithLib() {
const router = myUseRouter()
return (
<div>
<span onClick={() => {
router.push(router.query.foobar) // NOT OK
}}>Click to XSS 1</span>
</div>
)
}

View File

@@ -0,0 +1,3 @@
import javascript
import semmle.javascript.security.dataflow.UnsafeDeserializationQuery
import testUtilities.ConsistencyChecking

View File

@@ -1,23 +1,43 @@
nodes
| tst.js:7:22:7:36 | req.params.data |
| tst.js:7:22:7:36 | req.params.data |
| tst.js:7:22:7:36 | req.params.data |
| tst.js:8:25:8:39 | req.params.data |
| tst.js:8:25:8:39 | req.params.data |
| tst.js:8:25:8:39 | req.params.data |
| tst.js:12:26:12:40 | req.params.data |
| tst.js:12:26:12:40 | req.params.data |
| tst.js:12:26:12:40 | req.params.data |
| tst.js:13:29:13:43 | req.params.data |
| tst.js:13:29:13:43 | req.params.data |
| tst.js:13:29:13:43 | req.params.data |
| tst.js:13:22:13:36 | req.params.data |
| tst.js:13:22:13:36 | req.params.data |
| tst.js:13:22:13:36 | req.params.data |
| tst.js:14:25:14:39 | req.params.data |
| tst.js:14:25:14:39 | req.params.data |
| tst.js:14:25:14:39 | req.params.data |
| tst.js:15:26:15:40 | req.params.data |
| tst.js:15:26:15:40 | req.params.data |
| tst.js:15:26:15:40 | req.params.data |
| tst.js:16:29:16:43 | req.params.data |
| tst.js:16:29:16:43 | req.params.data |
| tst.js:16:29:16:43 | req.params.data |
| tst.js:20:22:20:36 | req.params.data |
| tst.js:20:22:20:36 | req.params.data |
| tst.js:20:22:20:36 | req.params.data |
| tst.js:21:22:21:36 | req.params.data |
| tst.js:21:22:21:36 | req.params.data |
| tst.js:21:22:21:36 | req.params.data |
| tst.js:24:22:24:36 | req.params.data |
| tst.js:24:22:24:36 | req.params.data |
| tst.js:24:22:24:36 | req.params.data |
| tst.js:25:22:25:36 | req.params.data |
| tst.js:25:22:25:36 | req.params.data |
| tst.js:25:22:25:36 | req.params.data |
edges
| tst.js:7:22:7:36 | req.params.data | tst.js:7:22:7:36 | req.params.data |
| tst.js:8:25:8:39 | req.params.data | tst.js:8:25:8:39 | req.params.data |
| tst.js:12:26:12:40 | req.params.data | tst.js:12:26:12:40 | req.params.data |
| tst.js:13:29:13:43 | req.params.data | tst.js:13:29:13:43 | req.params.data |
| tst.js:13:22:13:36 | req.params.data | tst.js:13:22:13:36 | req.params.data |
| tst.js:14:25:14:39 | req.params.data | tst.js:14:25:14:39 | req.params.data |
| tst.js:15:26:15:40 | req.params.data | tst.js:15:26:15:40 | req.params.data |
| tst.js:16:29:16:43 | req.params.data | tst.js:16:29:16:43 | req.params.data |
| tst.js:20:22:20:36 | req.params.data | tst.js:20:22:20:36 | req.params.data |
| tst.js:21:22:21:36 | req.params.data | tst.js:21:22:21:36 | req.params.data |
| tst.js:24:22:24:36 | req.params.data | tst.js:24:22:24:36 | req.params.data |
| tst.js:25:22:25:36 | req.params.data | tst.js:25:22:25:36 | req.params.data |
#select
| tst.js:7:22:7:36 | req.params.data | tst.js:7:22:7:36 | req.params.data | tst.js:7:22:7:36 | req.params.data | Unsafe deserialization depends on a $@. | tst.js:7:22:7:36 | req.params.data | user-provided value |
| tst.js:8:25:8:39 | req.params.data | tst.js:8:25:8:39 | req.params.data | tst.js:8:25:8:39 | req.params.data | Unsafe deserialization depends on a $@. | tst.js:8:25:8:39 | req.params.data | user-provided value |
| tst.js:12:26:12:40 | req.params.data | tst.js:12:26:12:40 | req.params.data | tst.js:12:26:12:40 | req.params.data | Unsafe deserialization depends on a $@. | tst.js:12:26:12:40 | req.params.data | user-provided value |
| tst.js:13:29:13:43 | req.params.data | tst.js:13:29:13:43 | req.params.data | tst.js:13:29:13:43 | req.params.data | Unsafe deserialization depends on a $@. | tst.js:13:29:13:43 | req.params.data | user-provided value |
| tst.js:13:22:13:36 | req.params.data | tst.js:13:22:13:36 | req.params.data | tst.js:13:22:13:36 | req.params.data | Unsafe deserialization depends on a $@. | tst.js:13:22:13:36 | req.params.data | user-provided value |
| tst.js:14:25:14:39 | req.params.data | tst.js:14:25:14:39 | req.params.data | tst.js:14:25:14:39 | req.params.data | Unsafe deserialization depends on a $@. | tst.js:14:25:14:39 | req.params.data | user-provided value |
| tst.js:15:26:15:40 | req.params.data | tst.js:15:26:15:40 | req.params.data | tst.js:15:26:15:40 | req.params.data | Unsafe deserialization depends on a $@. | tst.js:15:26:15:40 | req.params.data | user-provided value |
| tst.js:16:29:16:43 | req.params.data | tst.js:16:29:16:43 | req.params.data | tst.js:16:29:16:43 | req.params.data | Unsafe deserialization depends on a $@. | tst.js:16:29:16:43 | req.params.data | user-provided value |
| tst.js:20:22:20:36 | req.params.data | tst.js:20:22:20:36 | req.params.data | tst.js:20:22:20:36 | req.params.data | Unsafe deserialization depends on a $@. | tst.js:20:22:20:36 | req.params.data | user-provided value |
| tst.js:21:22:21:36 | req.params.data | tst.js:21:22:21:36 | req.params.data | tst.js:21:22:21:36 | req.params.data | Unsafe deserialization depends on a $@. | tst.js:21:22:21:36 | req.params.data | user-provided value |
| tst.js:24:22:24:36 | req.params.data | tst.js:24:22:24:36 | req.params.data | tst.js:24:22:24:36 | req.params.data | Unsafe deserialization depends on a $@. | tst.js:24:22:24:36 | req.params.data | user-provided value |
| tst.js:25:22:25:36 | req.params.data | tst.js:25:22:25:36 | req.params.data | tst.js:25:22:25:36 | req.params.data | Unsafe deserialization depends on a $@. | tst.js:25:22:25:36 | req.params.data | user-provided value |

View File

@@ -4,11 +4,24 @@ var express = require('express');
var app = express();
app.post('/store/:id', function(req, res) {
let data;
data = jsyaml.load(req.params.data); // NOT OK
data = jsyaml.loadAll(req.params.data); // NOT OK
data = jsyaml.load(req.params.data); // OK
data = jsyaml.loadAll(req.params.data); // OK
data = jsyaml.safeLoad(req.params.data); // OK
data = jsyaml.safeLoadAll(req.params.data); // OK
let unsafeConfig = { schema: jsyaml.DEFAULT_FULL_SCHEMA };
data = jsyaml.load(req.params.data, unsafeConfig); // NOT OK
data = jsyaml.loadAll(req.params.data, unsafeConfig); // NOT OK
data = jsyaml.safeLoad(req.params.data, unsafeConfig); // NOT OK
data = jsyaml.safeLoadAll(req.params.data, unsafeConfig); // NOT OK
data = jsyaml.load(req.params.data, { schema: jsyaml.DEFAULT_SCHEMA }); // OK
data = jsyaml.load(req.params.data, { schema: jsyaml.DEFAULT_SCHEMA.extend(require('js-yaml-js-types').all) }); // NOT OK
data = jsyaml.load(req.params.data, { schema: jsyaml.DEFAULT_SCHEMA.extend(require('js-yaml-js-types').function) }); // NOT OK
data = jsyaml.load(req.params.data, { schema: jsyaml.DEFAULT_SCHEMA.extend(require('js-yaml-js-types').undefined) }); // OK
data = jsyaml.load(req.params.data, { schema: require('js-yaml-js-types').all.extend(jsyaml.DEFAULT_SCHEMA) }); // NOT OK
data = jsyaml.load(req.params.data, { schema: require('js-yaml-js-types').function.extend(jsyaml.DEFAULT_SCHEMA) }); // NOT OK
data = jsyaml.load(req.params.data, { schema: require('js-yaml-js-types').undefined.extend(jsyaml.DEFAULT_SCHEMA) }); // OK
});

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added more content-flow/field-flow for dictionaries, by adding support for reads through `mydict.get("key")` and `mydict.setdefault("key", value)`, and store steps through `dict["key"] = value` and `mydict.setdefault("key", value)`.

View File

@@ -815,24 +815,20 @@ private module Cached {
)
}
private predicate store(
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
exists(ContentSet cs |
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
)
}
/**
* Holds if data can flow from `node1` to `node2` via a direct assignment to
* `f`.
* `c`.
*
* This includes reverse steps through reads when the result of the read has
* been stored into, in order to handle cases like `x.f1.f2 = y`.
*/
cached
predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) {
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
predicate store(
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
exists(ContentSet cs |
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
)
}
/**
@@ -932,36 +928,15 @@ private module Cached {
TReturnCtxNoFlowThrough() or
TReturnCtxMaybeFlowThrough(ReturnPosition pos)
cached
newtype TTypedContentApprox =
MkTypedContentApprox(ContentApprox c, DataFlowType t) {
exists(Content cont |
c = getContentApprox(cont) and
store(_, cont, _, _, t)
)
}
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
cached
TypedContent getATypedContent(TypedContentApprox c) {
exists(ContentApprox cls, DataFlowType t, Content cont |
c = MkTypedContentApprox(cls, pragma[only_bind_into](t)) and
result = MkTypedContent(cont, pragma[only_bind_into](t)) and
cls = getContentApprox(cont)
)
}
cached
newtype TAccessPathFront =
TFrontNil(DataFlowType t) or
TFrontHead(TypedContent tc)
TFrontNil() or
TFrontHead(Content c)
cached
newtype TApproxAccessPathFront =
TApproxFrontNil(DataFlowType t) or
TApproxFrontHead(TypedContentApprox tc)
TApproxFrontNil() or
TApproxFrontHead(ContentApprox c)
cached
newtype TAccessPathFrontOption =
@@ -986,8 +961,16 @@ predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
/**
* A `Node` at which a cast can occur such that the type should be checked.
*/
class CastingNode extends Node {
class CastingNode instanceof Node {
CastingNode() { castingNode(this) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
private predicate readStepWithTypes(
@@ -1135,9 +1118,17 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable)
* The value of a parameter at function entry, viewed as a node in a data
* flow graph.
*/
class ParamNode extends Node {
class ParamNode instanceof Node {
ParamNode() { parameterNode(this, _, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/**
* Holds if this node is the parameter of callable `c` at the specified
* position.
@@ -1146,9 +1137,17 @@ class ParamNode extends Node {
}
/** A data-flow node that represents a call argument. */
class ArgNode extends Node {
class ArgNode instanceof Node {
ArgNode() { argumentNode(this, _, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Holds if this argument occurs at the given position in the given call. */
final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
argumentNode(this, call, pos)
@@ -1159,9 +1158,17 @@ class ArgNode extends Node {
* A node from which flow can return to the caller. This is either a regular
* `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter.
*/
class ReturnNodeExt extends Node {
class ReturnNodeExt instanceof Node {
ReturnNodeExt() { returnNodeExt(this, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the kind of this returned value. */
ReturnKindExt getKind() { returnNodeExt(this, result) }
}
@@ -1170,8 +1177,16 @@ class ReturnNodeExt extends Node {
* A node to which data can flow from a call. Either an ordinary out node
* or a post-update node associated with a call argument.
*/
class OutNodeExt extends Node {
class OutNodeExt instanceof Node {
OutNodeExt() { outNodeExt(this) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
/**
@@ -1387,67 +1402,37 @@ class ReturnCtx extends TReturnCtx {
}
}
/** An approximated `Content` tagged with the type of a containing object. */
class TypedContentApprox extends MkTypedContentApprox {
private ContentApprox c;
private DataFlowType t;
TypedContentApprox() { this = MkTypedContentApprox(c, t) }
/** Gets a typed content approximated by this value. */
TypedContent getATypedContent() { result = getATypedContent(this) }
/** Gets the content. */
ContentApprox getContent() { result = c }
/** Gets the container type. */
DataFlowType getContainerType() { result = t }
/** Gets a textual representation of this approximated content. */
string toString() { result = c.toString() }
}
/**
* The front of an approximated access path. This is either a head or a nil.
*/
abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
abstract string toString();
abstract DataFlowType getType();
abstract boolean toBoolNonEmpty();
TypedContentApprox getHead() { this = TApproxFrontHead(result) }
ContentApprox getHead() { this = TApproxFrontHead(result) }
pragma[nomagic]
TypedContent getAHead() {
exists(TypedContentApprox cont |
Content getAHead() {
exists(ContentApprox cont |
this = TApproxFrontHead(cont) and
result = cont.getATypedContent()
cont = getContentApprox(result)
)
}
}
class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil {
private DataFlowType t;
ApproxAccessPathFrontNil() { this = TApproxFrontNil(t) }
override string toString() { result = ppReprType(t) }
override DataFlowType getType() { result = t }
override string toString() { result = "nil" }
override boolean toBoolNonEmpty() { result = false }
}
class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead {
private TypedContentApprox tc;
private ContentApprox c;
ApproxAccessPathFrontHead() { this = TApproxFrontHead(tc) }
ApproxAccessPathFrontHead() { this = TApproxFrontHead(c) }
override string toString() { result = tc.toString() }
override DataFlowType getType() { result = tc.getContainerType() }
override string toString() { result = c.toString() }
override boolean toBoolNonEmpty() { result = true }
}
@@ -1461,65 +1446,31 @@ class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption {
}
}
/** A `Content` tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;
private DataFlowType t;
TypedContent() { this = MkTypedContent(c, t) }
/** Gets the content. */
Content getContent() { result = c }
/** Gets the container type. */
DataFlowType getContainerType() { result = t }
/** Gets a textual representation of this content. */
string toString() { result = c.toString() }
/**
* Holds if access paths with this `TypedContent` at their head always should
* be tracked at high precision. This disables adaptive access path precision
* for such access paths.
*/
predicate forceHighPrecision() { forceHighPrecision(c) }
}
/**
* The front of an access path. This is either a head or a nil.
*/
abstract class AccessPathFront extends TAccessPathFront {
abstract string toString();
abstract DataFlowType getType();
abstract ApproxAccessPathFront toApprox();
TypedContent getHead() { this = TFrontHead(result) }
Content getHead() { this = TFrontHead(result) }
}
class AccessPathFrontNil extends AccessPathFront, TFrontNil {
private DataFlowType t;
override string toString() { result = "nil" }
AccessPathFrontNil() { this = TFrontNil(t) }
override string toString() { result = ppReprType(t) }
override DataFlowType getType() { result = t }
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil(t) }
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil() }
}
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
private TypedContent tc;
private Content c;
AccessPathFrontHead() { this = TFrontHead(tc) }
AccessPathFrontHead() { this = TFrontHead(c) }
override string toString() { result = tc.toString() }
override string toString() { result = c.toString() }
override DataFlowType getType() { result = tc.getContainerType() }
override ApproxAccessPathFront toApprox() { result.getAHead() = tc }
override ApproxAccessPathFront toApprox() { result.getAHead() = c }
}
/** An optional access path front. */

View File

@@ -588,6 +588,8 @@ predicate storeStep(Node nodeFrom, Content c, Node nodeTo) {
or
dictStoreStep(nodeFrom, c, nodeTo)
or
moreDictStoreSteps(nodeFrom, c, nodeTo)
or
comprehensionStoreStep(nodeFrom, c, nodeTo)
or
iterableUnpackingStoreStep(nodeFrom, c, nodeTo)
@@ -688,19 +690,48 @@ predicate tupleStoreStep(CfgNode nodeFrom, TupleElementContent c, CfgNode nodeTo
}
/** Data flows from an element of a dictionary to the dictionary at a specific key. */
predicate dictStoreStep(CfgNode nodeFrom, DictionaryElementContent c, CfgNode nodeTo) {
predicate dictStoreStep(CfgNode nodeFrom, DictionaryElementContent c, Node nodeTo) {
// Dictionary
// `{..., "key" = 42, ...}`
// nodeFrom is `42`, cfg node
// nodeTo is the dict, `{..., "key" = 42, ...}`, cfg node
// c denotes element of dictionary and the key `"key"`
exists(KeyValuePair item |
item = nodeTo.getNode().(DictNode).getNode().(Dict).getAnItem() and
item = nodeTo.asCfgNode().(DictNode).getNode().(Dict).getAnItem() and
nodeFrom.getNode().getNode() = item.getValue() and
c.getKey() = item.getKey().(StrConst).getS()
)
}
/**
* This has been made private since `dictStoreStep` is used by taint-tracking, and
* adding these extra steps made some alerts very noisy.
*
* TODO: Once TaintTracking no longer uses `dictStoreStep`, unify the two predicates.
*/
private predicate moreDictStoreSteps(CfgNode nodeFrom, DictionaryElementContent c, Node nodeTo) {
exists(SubscriptNode subscript |
nodeTo.(PostUpdateNode).getPreUpdateNode().asCfgNode() = subscript.getObject() and
nodeFrom.asCfgNode() = subscript.(DefinitionNode).getValue() and
c.getKey() = subscript.getIndex().getNode().(StrConst).getText()
)
or
// see https://docs.python.org/3.10/library/stdtypes.html#dict.setdefault
exists(MethodCallNode call |
call.calls(nodeTo.(PostUpdateNode).getPreUpdateNode(), "setdefault") and
call.getArg(0).asExpr().(StrConst).getText() = c.getKey() and
nodeFrom = call.getArg(1)
)
}
predicate dictClearStep(Node node, DictionaryElementContent c) {
exists(SubscriptNode subscript |
subscript instanceof DefinitionNode and
node.asCfgNode() = subscript.getObject() and
c.getKey() = subscript.getIndex().getNode().(StrConst).getText()
)
}
/** Data flows from an element expression in a comprehension to the comprehension. */
predicate comprehensionStoreStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
// Comprehension
@@ -761,6 +792,8 @@ predicate defaultValueFlowStep(CfgNode nodeFrom, CfgNode nodeTo) {
predicate readStep(Node nodeFrom, Content c, Node nodeTo) {
subscriptReadStep(nodeFrom, c, nodeTo)
or
dictReadStep(nodeFrom, c, nodeTo)
or
iterableUnpackingReadStep(nodeFrom, c, nodeTo)
or
matchReadStep(nodeFrom, c, nodeTo)
@@ -799,6 +832,17 @@ predicate subscriptReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
)
}
predicate dictReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
// see
// - https://docs.python.org/3.10/library/stdtypes.html#dict.get
// - https://docs.python.org/3.10/library/stdtypes.html#dict.setdefault
exists(MethodCallNode call |
call.calls(nodeFrom, ["get", "setdefault"]) and
call.getArg(0).asExpr().(StrConst).getText() = c.(DictionaryElementContent).getKey() and
nodeTo = call
)
}
/** Data flows from a sequence to a call to `pop` on the sequence. */
predicate popReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
// set.pop or list.pop
@@ -873,6 +917,8 @@ predicate clearsContent(Node n, Content c) {
or
attributeClearStep(n, c)
or
dictClearStep(n, c)
or
FlowSummaryImpl::Private::Steps::summaryClearsContent(n, c)
or
dictSplatParameterNodeClearStep(n, c)

View File

@@ -3795,6 +3795,30 @@ private module StdlibPrivate {
preservesValue = true
}
}
/**
* A flow summary for `dict.setdefault`.
*
* See https://docs.python.org/3.10/library/stdtypes.html#dict.setdefault
*/
class DictSetdefaultSummary extends SummarizedCallable {
DictSetdefaultSummary() { this = "dict.setdefault" }
override DataFlow::CallCfgNode getACall() {
result.(DataFlow::MethodCallNode).calls(_, "setdefault")
}
override DataFlow::ArgumentNode getACallback() {
result.(DataFlow::AttrRead).getAttributeName() = "setdefault"
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
// store/read steps with dictionary content of this is modeled in DataFlowPrivate
input = "Argument[1]" and
output = "ReturnValue" and
preservesValue = true
}
}
}
// ---------------------------------------------------------------------------

View File

@@ -0,0 +1,17 @@
<!DOCTYPE qhelp SYSTEM "qhelp.dtd">
<qhelp>
<overview>
<p>
Processing an unvalidated user input can allow an attacker to inject arbitrary command in your local and remote servers when creating a ssh connection.
</p>
</overview>
<recommendation>
<p>
This vulnerability can be prevented by not allowing untrusted user input to be passed as ProxyCommand or exec_command.
</p>
</recommendation>
<example>
<p>In the example below, the ProxyCommand and exec_command are controlled by the user and hence leads to a vulnerability.</p>
<sample src="paramikoBad.py" />
</example>
</qhelp>

View File

@@ -0,0 +1,57 @@
/**
* @name RCE with user provided command with paramiko ssh client
* @description user provided command can lead to execute code on a external server that can be belong to other users or admins
* @kind path-problem
* @problem.severity error
* @security-severity 9.3
* @precision high
* @id py/command-injection
* @tags security
* experimental
* external/cwe/cwe-074
*/
import python
import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.TaintTracking
import semmle.python.dataflow.new.RemoteFlowSources
import semmle.python.ApiGraphs
import DataFlow::PathGraph
private API::Node paramikoClient() {
result = API::moduleImport("paramiko").getMember("SSHClient").getReturn()
}
class ParamikoCmdInjectionConfiguration extends TaintTracking::Configuration {
ParamikoCmdInjectionConfiguration() { this = "ParamikoCMDInjectionConfiguration" }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
/**
* exec_command of `paramiko.SSHClient` class execute command on ssh target server
* the `paramiko.ProxyCommand` is equivalent of `ssh -o ProxyCommand="CMD"`
* and it run CMD on current system that running the ssh command
* the Sink related to proxy command is the `connect` method of `paramiko.SSHClient` class
*/
override predicate isSink(DataFlow::Node sink) {
sink = paramikoClient().getMember("exec_command").getACall().getParameter(0, "command").asSink()
or
sink = paramikoClient().getMember("connect").getACall().getParameter(11, "sock").asSink()
}
/**
* this additional taint step help taint tracking to find the vulnerable `connect` method of `paramiko.SSHClient` class
*/
override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
exists(API::CallNode call |
call = API::moduleImport("paramiko").getMember("ProxyCommand").getACall() and
nodeFrom = call.getParameter(0, "command_line").asSink() and
nodeTo = call
)
}
}
from ParamikoCmdInjectionConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "This code execution depends on a $@.", source.getNode(),
"a user-provided value"

View File

@@ -0,0 +1,36 @@
#!/usr/bin/env python
from flask import request, Flask
import paramiko
from paramiko import SSHClient
app = Flask(__name__)
paramiko_ssh_client = SSHClient()
paramiko_ssh_client.load_system_host_keys()
paramiko_ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
paramiko_ssh_client.connect(hostname="127.0.0.1", port="22", username="ssh_user_name", pkey="k", timeout=11, banner_timeout=200)
@app.route('/external_exec_command_1')
def bad1():
user_cmd = request.args.get('command')
stdin, stdout, stderr = paramiko_ssh_client.exec_command(user_cmd)
return stdout
@app.route('/external_exec_command_2')
def bad2():
user_cmd = request.args.get('command')
stdin, stdout, stderr = paramiko_ssh_client.exec_command(command=user_cmd)
return stdout
@app.route('/proxycommand')
def bad2():
user_cmd = request.args.get('command')
stdin, stdout, stderr = paramiko_ssh_client.connect('hostname', username='user',password='yourpassword',sock=paramiko.ProxyCommand(user_cmd))
return stdout
if __name__ == '__main__':
app.debug = False
app.run()

View File

@@ -9,14 +9,17 @@ class UnresolvedCallExpectations extends InlineExpectationsTest {
override string getARelevantTag() { result = "unresolved_call" }
predicate unresolvedCall(CallNode call) {
not exists(DataFlowPrivate::DataFlowCall dfc |
exists(dfc.getCallable()) and dfc.getNode() = call
) and
not DataFlowPrivate::resolveClassCall(call, _) and
not call = API::builtin(_).getACall().asCfgNode()
}
override predicate hasActualResult(Location location, string element, string tag, string value) {
exists(location.getFile().getRelativePath()) and
exists(CallNode call |
not exists(DataFlowPrivate::DataFlowCall dfc |
exists(dfc.getCallable()) and dfc.getNode() = call
) and
not DataFlowPrivate::resolveClassCall(call, _) and
not call = API::builtin(_).getACall().asCfgNode() and
exists(CallNode call | this.unresolvedCall(call) |
location = call.getLocation() and
tag = "unresolved_call" and
value = prettyExpr(call.getNode()) and

View File

@@ -1,4 +1,5 @@
| file://:0:0:0:0 | parameter position 0 of builtins.reversed |
| file://:0:0:0:0 | parameter position 1 of dict.setdefault |
| test.py:1:1:1:21 | SynthDictSplatParameterNode |
| test.py:1:19:1:19 | ControlFlowNode for x |
| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |

View File

@@ -1,3 +1,4 @@
| file://:0:0:0:0 | [summary] to write: return (return) in builtins.reversed |
| file://:0:0:0:0 | [summary] to write: return (return) in dict.setdefault |
| test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:7:19:7:19 | ControlFlowNode for a |

View File

@@ -1,4 +1,5 @@
| file://:0:0:0:0 | [summary] read: argument position 0.List element in builtins.reversed | file://:0:0:0:0 | [summary] to write: return (return).List element in builtins.reversed |
| file://:0:0:0:0 | parameter position 1 of dict.setdefault | file://:0:0:0:0 | [summary] to write: return (return) in dict.setdefault |
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | GSSA Variable obfuscated_id |
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |

View File

@@ -1,4 +1,5 @@
| file://:0:0:0:0 | [summary] read: argument position 0.List element in builtins.reversed | file://:0:0:0:0 | [summary] to write: return (return).List element in builtins.reversed |
| file://:0:0:0:0 | parameter position 1 of dict.setdefault | file://:0:0:0:0 | [summary] to write: return (return) in dict.setdefault |
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | GSSA Variable obfuscated_id |
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | GSSA Variable obfuscated_id |
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |

View File

@@ -1,8 +1,11 @@
| file://:0:0:0:0 | [summary] read: argument position 0.List element in builtins.reversed | file://:0:0:0:0 | [summary] read: argument position 0.List element in builtins.reversed |
| file://:0:0:0:0 | [summary] read: argument position 0.List element in builtins.reversed | file://:0:0:0:0 | [summary] to write: return (return).List element in builtins.reversed |
| file://:0:0:0:0 | [summary] to write: return (return) in builtins.reversed | file://:0:0:0:0 | [summary] to write: return (return) in builtins.reversed |
| file://:0:0:0:0 | [summary] to write: return (return) in dict.setdefault | file://:0:0:0:0 | [summary] to write: return (return) in dict.setdefault |
| file://:0:0:0:0 | [summary] to write: return (return).List element in builtins.reversed | file://:0:0:0:0 | [summary] to write: return (return).List element in builtins.reversed |
| file://:0:0:0:0 | parameter position 0 of builtins.reversed | file://:0:0:0:0 | parameter position 0 of builtins.reversed |
| file://:0:0:0:0 | parameter position 1 of dict.setdefault | file://:0:0:0:0 | [summary] to write: return (return) in dict.setdefault |
| file://:0:0:0:0 | parameter position 1 of dict.setdefault | file://:0:0:0:0 | parameter position 1 of dict.setdefault |
| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:0:0:0:0 | GSSA Variable __name__ |
| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:0:0:0:0 | GSSA Variable __package__ |
| test.py:0:0:0:0 | GSSA Variable b | test.py:0:0:0:0 | GSSA Variable b |

View File

@@ -1,4 +1,5 @@
| file://:0:0:0:0 | [summary] read: argument position 0.List element in builtins.reversed | file://:0:0:0:0 | [summary] to write: return (return).List element in builtins.reversed |
| file://:0:0:0:0 | parameter position 1 of dict.setdefault | file://:0:0:0:0 | [summary] to write: return (return) in dict.setdefault |
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | GSSA Variable obfuscated_id |
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:1:19:1:19 | SSA variable x |

View File

@@ -1,3 +1,4 @@
| file://:0:0:0:0 | parameter position 1 of dict.setdefault | file://:0:0:0:0 | [summary] to write: return (return) in dict.setdefault |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:4:10:4:10 | ControlFlowNode for z |
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:7:1:7:1 | GSSA Variable b |
| test.py:1:19:1:19 | SSA variable x | test.py:4:10:4:10 | ControlFlowNode for z |

View File

@@ -1,7 +1,9 @@
| file://:0:0:0:0 | [summary] read: argument position 0.List element in builtins.reversed |
| file://:0:0:0:0 | [summary] to write: return (return) in builtins.reversed |
| file://:0:0:0:0 | [summary] to write: return (return) in dict.setdefault |
| file://:0:0:0:0 | [summary] to write: return (return).List element in builtins.reversed |
| file://:0:0:0:0 | parameter position 0 of builtins.reversed |
| file://:0:0:0:0 | parameter position 1 of dict.setdefault |
| test.py:0:0:0:0 | GSSA Variable __name__ |
| test.py:0:0:0:0 | GSSA Variable __package__ |
| test.py:0:0:0:0 | GSSA Variable b |

View File

@@ -1,7 +1,9 @@
| file://:0:0:0:0 | [summary] read: argument position 0.List element in builtins.reversed |
| file://:0:0:0:0 | [summary] to write: return (return) in builtins.reversed |
| file://:0:0:0:0 | [summary] to write: return (return) in dict.setdefault |
| file://:0:0:0:0 | [summary] to write: return (return).List element in builtins.reversed |
| file://:0:0:0:0 | parameter position 0 of builtins.reversed |
| file://:0:0:0:0 | parameter position 1 of dict.setdefault |
| test.py:0:0:0:0 | GSSA Variable __name__ |
| test.py:0:0:0:0 | GSSA Variable __package__ |
| test.py:0:0:0:0 | GSSA Variable b |

View File

@@ -1,2 +1,12 @@
import python
import experimental.dataflow.TestUtil.UnresolvedCalls
private import semmle.python.dataflow.new.DataFlow
class IgnoreDictMethod extends UnresolvedCallExpectations {
override predicate unresolvedCall(CallNode call) {
super.unresolvedCall(call) and
not any(DataFlow::MethodCallNode methodCall |
methodCall.getMethodName() in ["get", "setdefault"]
).asCfgNode() = call
}
}

View File

@@ -0,0 +1,71 @@
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname((__file__)))) # $ unresolved_call=sys.path.append(..)
from testlib import expects
# These are defined so that we can evaluate the test code.
NONSOURCE = "not a source"
SOURCE = "source"
def is_source(x):
return x == "source" or x == b"source" or x == 42 or x == 42.0 or x == 42j
def SINK(x):
if is_source(x):
print("OK")
else:
print("Unexpected flow", x)
def SINK_F(x):
if is_source(x):
print("Unexpected flow", x)
else:
print("OK")
# ------------------------------------------------------------------------------
# Actual tests
# ------------------------------------------------------------------------------
@expects(2) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
def test_dict_literal():
d = {"key": SOURCE}
SINK(d["key"]) # $ flow="SOURCE, l:-1 -> d['key']"
SINK(d.get("key")) # $ flow="SOURCE, l:-2 -> d.get(..)"
@expects(2) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
def test_dict_update():
d = {}
d["key"] = SOURCE
SINK(d["key"]) # $ flow="SOURCE, l:-1 -> d['key']"
SINK(d.get("key")) # $ flow="SOURCE, l:-2 -> d.get(..)"
@expects(3) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
def test_dict_setdefault():
d = {}
x = d.setdefault("key", SOURCE)
SINK(x) # $ flow="SOURCE, l:-1 -> x"
SINK(d["key"]) # $ flow="SOURCE, l:-2 -> d['key']"
SINK(d.setdefault("key", NONSOURCE)) # $ flow="SOURCE, l:-3 -> d.setdefault(..)"
@expects(2) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
def test_dict_override():
d = {}
d["key"] = SOURCE
SINK(d["key"]) # $ flow="SOURCE, l:-1 -> d['key']"
d["key"] = NONSOURCE
SINK_F(d["key"])
@expects(3) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
def test_dict_nonstring_key():
d = {}
d[42] = SOURCE
SINK(d[42]) # $ MISSING: flow
SINK(d.get(42)) # $ MISSING: flow
SINK(d.setdefault(42, NONSOURCE)) # $ MISSING: flow

View File

@@ -1,4 +1,5 @@
| file://:0:0:0:0 | [summary] read: argument position 0.List element in builtins.reversed | file://:0:0:0:0 | [summary] to write: return (return).List element in builtins.reversed |
| file://:0:0:0:0 | parameter position 1 of dict.setdefault | file://:0:0:0:0 | [summary] to write: return (return) in dict.setdefault |
| test.py:3:1:3:7 | GSSA Variable tainted | test.py:4:6:4:12 | ControlFlowNode for tainted |
| test.py:3:11:3:16 | ControlFlowNode for SOURCE | test.py:3:1:3:7 | GSSA Variable tainted |
| test.py:6:1:6:11 | ControlFlowNode for FunctionExpr | test.py:6:5:6:8 | GSSA Variable func |

View File

@@ -72,6 +72,7 @@ if __name__ == "__main__":
check_tests_valid("variable-capture.collections")
check_tests_valid("module-initialization.multiphase")
check_tests_valid("fieldflow.test")
check_tests_valid("fieldflow.test_dict")
check_tests_valid_after_version("match.test", (3, 10))
check_tests_valid("exceptions.test")
check_tests_valid_after_version("exceptions.test_group", (3, 11))

View File

@@ -3,7 +3,8 @@ edges
| UnsafeUnpack.py:5:26:5:32 | ControlFlowNode for ImportMember | UnsafeUnpack.py:5:26:5:32 | GSSA Variable request |
| UnsafeUnpack.py:5:26:5:32 | GSSA Variable request | UnsafeUnpack.py:0:0:0:0 | ModuleVariableNode for UnsafeUnpack.request |
| UnsafeUnpack.py:11:18:11:24 | ControlFlowNode for request | UnsafeUnpack.py:11:18:11:29 | ControlFlowNode for Attribute |
| UnsafeUnpack.py:11:18:11:29 | ControlFlowNode for Attribute | UnsafeUnpack.py:17:27:17:38 | ControlFlowNode for Attribute |
| UnsafeUnpack.py:11:18:11:29 | ControlFlowNode for Attribute | UnsafeUnpack.py:11:18:11:49 | ControlFlowNode for Attribute() |
| UnsafeUnpack.py:11:18:11:49 | ControlFlowNode for Attribute() | UnsafeUnpack.py:17:27:17:38 | ControlFlowNode for Attribute |
| UnsafeUnpack.py:17:27:17:38 | ControlFlowNode for Attribute | UnsafeUnpack.py:19:35:19:41 | ControlFlowNode for tarpath |
| UnsafeUnpack.py:33:50:33:65 | ControlFlowNode for local_ziped_path | UnsafeUnpack.py:34:23:34:38 | ControlFlowNode for local_ziped_path |
| UnsafeUnpack.py:47:20:47:34 | ControlFlowNode for compressed_file | UnsafeUnpack.py:48:23:48:37 | ControlFlowNode for compressed_file |
@@ -32,6 +33,7 @@ nodes
| UnsafeUnpack.py:5:26:5:32 | GSSA Variable request | semmle.label | GSSA Variable request |
| UnsafeUnpack.py:11:18:11:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
| UnsafeUnpack.py:11:18:11:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| UnsafeUnpack.py:11:18:11:49 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
| UnsafeUnpack.py:17:27:17:38 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
| UnsafeUnpack.py:19:35:19:41 | ControlFlowNode for tarpath | semmle.label | ControlFlowNode for tarpath |
| UnsafeUnpack.py:33:50:33:65 | ControlFlowNode for local_ziped_path | semmle.label | ControlFlowNode for local_ziped_path |

View File

@@ -0,0 +1 @@
semmle-extractor-options: --lang=3 --max-import-depth=1

View File

@@ -0,0 +1,16 @@
edges
| paramiko.py:15:21:15:23 | ControlFlowNode for cmd | paramiko.py:16:62:16:64 | ControlFlowNode for cmd |
| paramiko.py:20:21:20:23 | ControlFlowNode for cmd | paramiko.py:21:70:21:72 | ControlFlowNode for cmd |
| paramiko.py:25:21:25:23 | ControlFlowNode for cmd | paramiko.py:26:114:26:139 | ControlFlowNode for Attribute() |
nodes
| paramiko.py:15:21:15:23 | ControlFlowNode for cmd | semmle.label | ControlFlowNode for cmd |
| paramiko.py:16:62:16:64 | ControlFlowNode for cmd | semmle.label | ControlFlowNode for cmd |
| paramiko.py:20:21:20:23 | ControlFlowNode for cmd | semmle.label | ControlFlowNode for cmd |
| paramiko.py:21:70:21:72 | ControlFlowNode for cmd | semmle.label | ControlFlowNode for cmd |
| paramiko.py:25:21:25:23 | ControlFlowNode for cmd | semmle.label | ControlFlowNode for cmd |
| paramiko.py:26:114:26:139 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
subpaths
#select
| paramiko.py:16:62:16:64 | ControlFlowNode for cmd | paramiko.py:15:21:15:23 | ControlFlowNode for cmd | paramiko.py:16:62:16:64 | ControlFlowNode for cmd | This code execution depends on a $@. | paramiko.py:15:21:15:23 | ControlFlowNode for cmd | a user-provided value |
| paramiko.py:21:70:21:72 | ControlFlowNode for cmd | paramiko.py:20:21:20:23 | ControlFlowNode for cmd | paramiko.py:21:70:21:72 | ControlFlowNode for cmd | This code execution depends on a $@. | paramiko.py:20:21:20:23 | ControlFlowNode for cmd | a user-provided value |
| paramiko.py:26:114:26:139 | ControlFlowNode for Attribute() | paramiko.py:25:21:25:23 | ControlFlowNode for cmd | paramiko.py:26:114:26:139 | ControlFlowNode for Attribute() | This code execution depends on a $@. | paramiko.py:25:21:25:23 | ControlFlowNode for cmd | a user-provided value |

View File

@@ -0,0 +1,27 @@
#!/usr/bin/env python
from fastapi import FastAPI
import paramiko
from paramiko import SSHClient
paramiko_ssh_client = SSHClient()
paramiko_ssh_client.load_system_host_keys()
paramiko_ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
paramiko_ssh_client.connect(hostname="127.0.0.1", port="22", username="ssh_user_name", pkey="k", timeout=11, banner_timeout=200)
app = FastAPI()
@app.get("/bad1")
async def read_item(cmd: str):
stdin, stdout, stderr = paramiko_ssh_client.exec_command(cmd)
return {"success": stdout}
@app.get("/bad2")
async def read_item(cmd: str):
stdin, stdout, stderr = paramiko_ssh_client.exec_command(command=cmd)
return {"success": "OK"}
@app.get("/bad3")
async def read_item(cmd: str):
stdin, stdout, stderr = paramiko_ssh_client.connect('hostname', username='user',password='yourpassword',sock=paramiko.ProxyCommand(cmd))
return {"success": "OK"}

View File

@@ -0,0 +1 @@
experimental/Security/CWE-074/paramiko/paramiko.ql

File diff suppressed because it is too large Load Diff

View File

@@ -815,24 +815,20 @@ private module Cached {
)
}
private predicate store(
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
exists(ContentSet cs |
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
)
}
/**
* Holds if data can flow from `node1` to `node2` via a direct assignment to
* `f`.
* `c`.
*
* This includes reverse steps through reads when the result of the read has
* been stored into, in order to handle cases like `x.f1.f2 = y`.
*/
cached
predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) {
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
predicate store(
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
) {
exists(ContentSet cs |
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
)
}
/**
@@ -932,36 +928,15 @@ private module Cached {
TReturnCtxNoFlowThrough() or
TReturnCtxMaybeFlowThrough(ReturnPosition pos)
cached
newtype TTypedContentApprox =
MkTypedContentApprox(ContentApprox c, DataFlowType t) {
exists(Content cont |
c = getContentApprox(cont) and
store(_, cont, _, _, t)
)
}
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
cached
TypedContent getATypedContent(TypedContentApprox c) {
exists(ContentApprox cls, DataFlowType t, Content cont |
c = MkTypedContentApprox(cls, pragma[only_bind_into](t)) and
result = MkTypedContent(cont, pragma[only_bind_into](t)) and
cls = getContentApprox(cont)
)
}
cached
newtype TAccessPathFront =
TFrontNil(DataFlowType t) or
TFrontHead(TypedContent tc)
TFrontNil() or
TFrontHead(Content c)
cached
newtype TApproxAccessPathFront =
TApproxFrontNil(DataFlowType t) or
TApproxFrontHead(TypedContentApprox tc)
TApproxFrontNil() or
TApproxFrontHead(ContentApprox c)
cached
newtype TAccessPathFrontOption =
@@ -986,8 +961,16 @@ predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
/**
* A `Node` at which a cast can occur such that the type should be checked.
*/
class CastingNode extends Node {
class CastingNode instanceof Node {
CastingNode() { castingNode(this) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
private predicate readStepWithTypes(
@@ -1135,9 +1118,17 @@ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable)
* The value of a parameter at function entry, viewed as a node in a data
* flow graph.
*/
class ParamNode extends Node {
class ParamNode instanceof Node {
ParamNode() { parameterNode(this, _, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/**
* Holds if this node is the parameter of callable `c` at the specified
* position.
@@ -1146,9 +1137,17 @@ class ParamNode extends Node {
}
/** A data-flow node that represents a call argument. */
class ArgNode extends Node {
class ArgNode instanceof Node {
ArgNode() { argumentNode(this, _, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Holds if this argument occurs at the given position in the given call. */
final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
argumentNode(this, call, pos)
@@ -1159,9 +1158,17 @@ class ArgNode extends Node {
* A node from which flow can return to the caller. This is either a regular
* `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter.
*/
class ReturnNodeExt extends Node {
class ReturnNodeExt instanceof Node {
ReturnNodeExt() { returnNodeExt(this, _) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the kind of this returned value. */
ReturnKindExt getKind() { returnNodeExt(this, result) }
}
@@ -1170,8 +1177,16 @@ class ReturnNodeExt extends Node {
* A node to which data can flow from a call. Either an ordinary out node
* or a post-update node associated with a call argument.
*/
class OutNodeExt extends Node {
class OutNodeExt instanceof Node {
OutNodeExt() { outNodeExt(this) }
string toString() { result = super.toString() }
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
/**
@@ -1387,67 +1402,37 @@ class ReturnCtx extends TReturnCtx {
}
}
/** An approximated `Content` tagged with the type of a containing object. */
class TypedContentApprox extends MkTypedContentApprox {
private ContentApprox c;
private DataFlowType t;
TypedContentApprox() { this = MkTypedContentApprox(c, t) }
/** Gets a typed content approximated by this value. */
TypedContent getATypedContent() { result = getATypedContent(this) }
/** Gets the content. */
ContentApprox getContent() { result = c }
/** Gets the container type. */
DataFlowType getContainerType() { result = t }
/** Gets a textual representation of this approximated content. */
string toString() { result = c.toString() }
}
/**
* The front of an approximated access path. This is either a head or a nil.
*/
abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
abstract string toString();
abstract DataFlowType getType();
abstract boolean toBoolNonEmpty();
TypedContentApprox getHead() { this = TApproxFrontHead(result) }
ContentApprox getHead() { this = TApproxFrontHead(result) }
pragma[nomagic]
TypedContent getAHead() {
exists(TypedContentApprox cont |
Content getAHead() {
exists(ContentApprox cont |
this = TApproxFrontHead(cont) and
result = cont.getATypedContent()
cont = getContentApprox(result)
)
}
}
class ApproxAccessPathFrontNil extends ApproxAccessPathFront, TApproxFrontNil {
private DataFlowType t;
ApproxAccessPathFrontNil() { this = TApproxFrontNil(t) }
override string toString() { result = ppReprType(t) }
override DataFlowType getType() { result = t }
override string toString() { result = "nil" }
override boolean toBoolNonEmpty() { result = false }
}
class ApproxAccessPathFrontHead extends ApproxAccessPathFront, TApproxFrontHead {
private TypedContentApprox tc;
private ContentApprox c;
ApproxAccessPathFrontHead() { this = TApproxFrontHead(tc) }
ApproxAccessPathFrontHead() { this = TApproxFrontHead(c) }
override string toString() { result = tc.toString() }
override DataFlowType getType() { result = tc.getContainerType() }
override string toString() { result = c.toString() }
override boolean toBoolNonEmpty() { result = true }
}
@@ -1461,65 +1446,31 @@ class ApproxAccessPathFrontOption extends TApproxAccessPathFrontOption {
}
}
/** A `Content` tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;
private DataFlowType t;
TypedContent() { this = MkTypedContent(c, t) }
/** Gets the content. */
Content getContent() { result = c }
/** Gets the container type. */
DataFlowType getContainerType() { result = t }
/** Gets a textual representation of this content. */
string toString() { result = c.toString() }
/**
* Holds if access paths with this `TypedContent` at their head always should
* be tracked at high precision. This disables adaptive access path precision
* for such access paths.
*/
predicate forceHighPrecision() { forceHighPrecision(c) }
}
/**
* The front of an access path. This is either a head or a nil.
*/
abstract class AccessPathFront extends TAccessPathFront {
abstract string toString();
abstract DataFlowType getType();
abstract ApproxAccessPathFront toApprox();
TypedContent getHead() { this = TFrontHead(result) }
Content getHead() { this = TFrontHead(result) }
}
class AccessPathFrontNil extends AccessPathFront, TFrontNil {
private DataFlowType t;
override string toString() { result = "nil" }
AccessPathFrontNil() { this = TFrontNil(t) }
override string toString() { result = ppReprType(t) }
override DataFlowType getType() { result = t }
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil(t) }
override ApproxAccessPathFront toApprox() { result = TApproxFrontNil() }
}
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
private TypedContent tc;
private Content c;
AccessPathFrontHead() { this = TFrontHead(tc) }
AccessPathFrontHead() { this = TFrontHead(c) }
override string toString() { result = tc.toString() }
override string toString() { result = c.toString() }
override DataFlowType getType() { result = tc.getContainerType() }
override ApproxAccessPathFront toApprox() { result.getAHead() = tc }
override ApproxAccessPathFront toApprox() { result.getAHead() = c }
}
/** An optional access path front. */

View File

@@ -400,3 +400,19 @@ private class AccessLocalsKeySummary extends SummarizedCallable {
preservesValue = true
}
}
/** A call to `render inline: foo`, considered as a ERB template rendering. */
private class RailsTemplateRendering extends TemplateRendering::Range, DataFlow::CallNode {
private DataFlow::Node template;
RailsTemplateRendering() {
(
this.asExpr().getExpr() instanceof Rails::RenderCall
or
this.asExpr().getExpr() instanceof Rails::RenderToCall
) and
template = this.getKeywordArgument("inline")
}
override DataFlow::Node getTemplate() { result = template }
}

View File

@@ -9,6 +9,7 @@ class BadERBController < ActionController::Base
<h2>Hello %s </h2></body></html>
" % name
template = ERB.new(html_text).result(binding)
render inline: html_text
end
end

View File

@@ -9,6 +9,7 @@ class GoodController < ActionController::Base
<h2>Hello <%= name %> </h2></body></html>
"
template = ERB.new(html_text).result(binding)
render inline: html_text
end
end

View File

@@ -8,6 +8,7 @@ private import codeql.ruby.security.PathInjectionCustomizations
private import codeql.ruby.security.ServerSideRequestForgeryCustomizations
private import codeql.ruby.security.UnsafeDeserializationCustomizations
private import codeql.ruby.security.UrlRedirectCustomizations
private import codeql.ruby.security.SqlInjectionCustomizations
class RelevantFile extends File {
RelevantFile() { not getRelativePath().regexpMatch(".*/test(case)?s?/.*") }
@@ -34,6 +35,8 @@ DataFlow::Node relevantTaintSink(string kind) {
kind = "UnsafeDeserialization" and result instanceof UnsafeDeserialization::Sink
or
kind = "UrlRedirect" and result instanceof UrlRedirect::Sink
or
kind = "SqlInjection" and result instanceof SqlInjection::Sink
) and
// the sink is not a string literal
not exists(Ast::StringLiteral str |

View File

@@ -14,6 +14,10 @@ class FooController < ActionController::Base
# where name is unsanitized
template = ERB.new(bad_text).result(binding)
# BAD: user input is evaluated
# where name is unsanitized
render inline: bad_text
# Template with the source
good_text = "
<!DOCTYPE html><html><body>
@@ -22,6 +26,9 @@ class FooController < ActionController::Base
# GOOD: user input is not evaluated
template2 = ERB.new(good_text).result(binding)
# GOOD: user input is not evaluated
render inline: good_text
end
end

Some files were not shown because too many files have changed in this diff Show More