Merge branch 'main' into mathiasvp/read-step-without-memory-operands

This commit is contained in:
Mathias Vorreiter Pedersen
2020-09-02 14:21:23 +02:00
94 changed files with 3693 additions and 163 deletions

View File

@@ -73,8 +73,20 @@ private predicate addressTakenVariable(StackVariable var) {
)
}
/**
* Holds if `v` is a stack-allocated reference-typed local variable. We don't
* build SSA for such variables since they are likely to change values even
* when not syntactically mentioned. For the same reason,
* `addressTakenVariable` is used to prevent tracking variables that may be
* aliased by such a reference.
*
* Reference-typed parameters are treated as if they weren't references.
* That's because it's in practice highly unlikely that they alias other data
* accessible from the function body.
*/
private predicate isReferenceVar(StackVariable v) {
v.getUnspecifiedType() instanceof ReferenceType
v.getUnspecifiedType() instanceof ReferenceType and
not v instanceof Parameter
}
/**

View File

@@ -525,6 +525,19 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
inner = nodeTo.(InnerPartialDefinitionNode).getPreUpdateNode().asExpr() and
outer = nodeFrom.(PartialDefinitionNode).getPreUpdateNode().asExpr()
)
or
// Reverse flow: data that flows from the post-update node of a reference
// returned by a function call, back into the qualifier of that function.
// This allows data to flow 'in' through references returned by a modeled
// function such as `operator[]`.
exists(DataFlowFunction f, Call call, FunctionInput inModel, FunctionOutput outModel |
call.getTarget() = f and
inModel.isReturnValueDeref() and
outModel.isQualifierObject() and
f.hasDataFlow(inModel, outModel) and
nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr() = call and
nodeTo.asDefiningArgument() = call.getQualifier()
)
}
/**

View File

@@ -4,6 +4,7 @@ private import implementations.Fread
private import implementations.Gets
private import implementations.IdentityFunction
private import implementations.Inet
private import implementations.Iterator
private import implementations.MemberFunction
private import implementations.Memcpy
private import implementations.Memset

View File

@@ -0,0 +1,273 @@
/**
* Provides implementation classes modeling C++ iterators, including
* `std::iterator`, `std::iterator_traits`, and types meeting the
* `LegacyIterator` named requirement. See `semmle.code.cpp.models.Models` for
* usage information.
*/
import cpp
import semmle.code.cpp.models.interfaces.Taint
import semmle.code.cpp.models.interfaces.DataFlow
/**
* An instantiation of the `std::iterator_traits` template.
*/
class IteratorTraits extends Class {
IteratorTraits() {
this.hasQualifiedName("std", "iterator_traits") and
not this instanceof TemplateClass and
exists(TypedefType t |
this.getAMember() = t and
t.getName() = "iterator_category"
)
}
Type getIteratorType() { result = this.getTemplateArgument(0) }
}
/**
* A type which has the typedefs expected for an iterator.
*/
class IteratorByTypedefs extends Class {
IteratorByTypedefs() {
this.getAMember().(TypedefType).hasName("difference_type") and
this.getAMember().(TypedefType).hasName("value_type") and
this.getAMember().(TypedefType).hasName("pointer") and
this.getAMember().(TypedefType).hasName("reference") and
this.getAMember().(TypedefType).hasName("iterator_category") and
not this.hasQualifiedName("std", "iterator_traits")
}
}
/**
* The `std::iterator` class.
*/
class StdIterator extends Class {
StdIterator() { this.hasQualifiedName("std", "iterator") }
}
/**
* A type which can be used as an iterator
*/
class Iterator extends Type {
Iterator() {
this instanceof IteratorByTypedefs or
exists(IteratorTraits it | it.getIteratorType() = this) or
this instanceof StdIterator
}
}
private FunctionInput getIteratorArgumentInput(Operator op, int index) {
exists(Type t |
t =
op
.getACallToThisFunction()
.getArgument(index)
.getExplicitlyConverted()
.getType()
.stripTopLevelSpecifiers()
|
(
t instanceof Iterator or
t.(ReferenceType).getBaseType() instanceof Iterator
) and
if op.getParameter(index).getUnspecifiedType() instanceof ReferenceType
then result.isParameterDeref(index)
else result.isParameter(index)
)
}
/**
* A non-member prefix `operator*` function for an iterator type.
*/
class IteratorPointerDereferenceOperator extends Operator, TaintFunction {
FunctionInput iteratorInput;
IteratorPointerDereferenceOperator() {
this.hasName("operator*") and
iteratorInput = getIteratorArgumentInput(this, 0)
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input = iteratorInput and
output.isReturnValue()
}
}
/**
* A non-member `operator++` or `operator--` function for an iterator type.
*/
class IteratorCrementOperator extends Operator, DataFlowFunction {
FunctionInput iteratorInput;
IteratorCrementOperator() {
this.hasName(["operator++", "operator--"]) and
iteratorInput = getIteratorArgumentInput(this, 0)
}
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
input = iteratorInput and
output.isReturnValue()
}
}
/**
* A non-member `operator+` function for an iterator type.
*/
class IteratorAddOperator extends Operator, TaintFunction {
FunctionInput iteratorInput;
IteratorAddOperator() {
this.hasName("operator+") and
iteratorInput = getIteratorArgumentInput(this, [0, 1])
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input = iteratorInput and
output.isReturnValue()
}
}
/**
* A non-member `operator-` function that takes a pointer difference type as its second argument.
*/
class IteratorSubOperator extends Operator, TaintFunction {
FunctionInput iteratorInput;
IteratorSubOperator() {
this.hasName("operator-") and
iteratorInput = getIteratorArgumentInput(this, 0) and
this.getParameter(1).getUnspecifiedType() instanceof IntegralType // not an iterator difference
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input = iteratorInput and
output.isReturnValue()
}
}
/**
* A non-member `operator+=` or `operator-=` function for an iterator type.
*/
class IteratorAssignArithmeticOperator extends Operator, DataFlowFunction, TaintFunction {
IteratorAssignArithmeticOperator() {
this.hasName(["operator+=", "operator-="]) and
this.getDeclaringType() instanceof Iterator
}
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
input.isParameter(0) and
output.isReturnValue()
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isParameterDeref(1) and
output.isParameterDeref(0)
}
}
/**
* A prefix `operator*` member function for an iterator type.
*/
class IteratorPointerDereferenceMemberOperator extends MemberFunction, TaintFunction {
IteratorPointerDereferenceMemberOperator() {
this.hasName("operator*") and
this.getDeclaringType() instanceof Iterator
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierObject() and
output.isReturnValue()
}
}
/**
* An `operator++` or `operator--` member function for an iterator type.
*/
class IteratorCrementMemberOperator extends MemberFunction, DataFlowFunction, TaintFunction {
IteratorCrementMemberOperator() {
this.hasName(["operator++", "operator--"]) and
this.getDeclaringType() instanceof Iterator
}
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierAddress() and
output.isReturnValue()
or
input.isReturnValueDeref() and
output.isQualifierObject()
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierObject() and
output.isReturnValueDeref()
}
}
/**
* A member `operator->` function for an iterator type.
*/
class IteratorFieldMemberOperator extends Operator, TaintFunction {
IteratorFieldMemberOperator() {
this.hasName("operator->") and
this.getDeclaringType() instanceof Iterator
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierObject() and
output.isReturnValue()
}
}
/**
* An `operator+` or `operator-` member function of an iterator class.
*/
class IteratorBinaryArithmeticMemberOperator extends MemberFunction, TaintFunction {
IteratorBinaryArithmeticMemberOperator() {
this.hasName(["operator+", "operator-"]) and
this.getDeclaringType() instanceof Iterator
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierObject() and
output.isReturnValue()
}
}
/**
* An `operator+=` or `operator-=` member function of an iterator class.
*/
class IteratorAssignArithmeticMemberOperator extends MemberFunction, DataFlowFunction, TaintFunction {
IteratorAssignArithmeticMemberOperator() {
this.hasName(["operator+=", "operator-="]) and
this.getDeclaringType() instanceof Iterator
}
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierAddress() and
output.isReturnValue()
or
input.isReturnValueDeref() and
output.isQualifierObject()
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierObject() and
output.isReturnValueDeref()
}
}
/**
* An `operator[]` member function of an iterator class.
*/
class IteratorArrayMemberOperator extends MemberFunction, TaintFunction {
IteratorArrayMemberOperator() {
this.hasName("operator[]") and
this.getDeclaringType() instanceof Iterator
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierObject() and
output.isReturnValue()
}
}

View File

@@ -1,4 +1,11 @@
/**
* Provides implementation classes modeling `std::string` and other
* instantiations of`std::basic_string`. See `semmle.code.cpp.models.Models`
* for usage information.
*/
import semmle.code.cpp.models.interfaces.Taint
import semmle.code.cpp.models.implementations.Iterator
/**
* The `std::basic_string` template class.
@@ -78,11 +85,17 @@ class StdStringAppend extends TaintFunction {
getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. `std::basic_string::CharT`
}
/**
* Gets the index of a parameter to this function that is an iterator.
*/
int getAnIteratorParameterIndex() { getParameter(result).getType() instanceof Iterator }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from string and parameter to string (qualifier) and return value
(
input.isQualifierObject() or
input.isParameterDeref(getAStringParameterIndex())
input.isParameterDeref(getAStringParameterIndex()) or
input.isParameter(getAnIteratorParameterIndex())
) and
(
output.isQualifierObject() or
@@ -118,6 +131,28 @@ class StdStringAssign extends TaintFunction {
}
}
/**
* The standard functions `std::string.begin` and `std::string.end` and their
* variants.
*/
class StdStringBeginEnd extends TaintFunction {
StdStringBeginEnd() {
this.hasQualifiedName("std", "basic_string", "begin") or
this.hasQualifiedName("std", "basic_string", "cbegin") or
this.hasQualifiedName("std", "basic_string", "rbegin") or
this.hasQualifiedName("std", "basic_string", "crbegin") or
this.hasQualifiedName("std", "basic_string", "end") or
this.hasQualifiedName("std", "basic_string", "cend") or
this.hasQualifiedName("std", "basic_string", "rend") or
this.hasQualifiedName("std", "basic_string", "crend")
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierObject() and
output.isReturnValue()
}
}
/**
* The standard function `std::string.copy`.
*/

View File

@@ -191,6 +191,8 @@ private predicate linearAccessImpl(Expr expr, VariableAccess v, float p, float q
// Base case
expr = v and p = 1.0 and q = 0.0
or
expr.(ReferenceDereferenceExpr).getExpr() = v and p = 1.0 and q = 0.0
or
// a+(p*v+b) == p*v + (a+b)
exists(AddExpr addExpr, float a, float b |
addExpr.getLeftOperand().isConstant() and
@@ -349,13 +351,20 @@ private predicate typeBounds(ArithmeticType t, float lb, float ub) {
t instanceof FloatingPointType and lb = -(1.0 / 0.0) and ub = 1.0 / 0.0
}
private Type stripReference(Type t) {
if t instanceof ReferenceType then result = t.(ReferenceType).getBaseType() else result = t
}
/** Gets the type used by range analysis for the given `StackVariable`. */
Type getVariableRangeType(StackVariable v) { result = stripReference(v.getUnspecifiedType()) }
/**
* Gets the lower bound for the unspecified type `t`.
*
* For example, if `t` is a signed 32-bit type then the result is
* `-2^31`.
*/
float typeLowerBound(ArithmeticType t) { typeBounds(t, result, _) }
float typeLowerBound(Type t) { typeBounds(stripReference(t), result, _) }
/**
* Gets the upper bound for the unspecified type `t`.
@@ -363,7 +372,7 @@ float typeLowerBound(ArithmeticType t) { typeBounds(t, result, _) }
* For example, if `t` is a signed 32-bit type then the result is
* `2^31 - 1`.
*/
float typeUpperBound(ArithmeticType t) { typeBounds(t, _, result) }
float typeUpperBound(Type t) { typeBounds(stripReference(t), _, result) }
/**
* Gets the minimum value that this expression could represent, based on

View File

@@ -485,7 +485,7 @@ private predicate isRecursiveDef(RangeSsaDefinition def, StackVariable v) {
* This predicate finds all the definitions in the first set.
*/
private predicate assignmentDef(RangeSsaDefinition def, StackVariable v, Expr expr) {
v.getUnspecifiedType() instanceof ArithmeticType and
getVariableRangeType(v) instanceof ArithmeticType and
(
def = v.getInitializer().getExpr() and def = expr
or
@@ -1329,7 +1329,7 @@ private float getDefLowerBounds(RangeSsaDefinition def, StackVariable v) {
// recursion from exploding.
result =
max(float widenLB |
widenLB = wideningLowerBounds(v.getUnspecifiedType()) and
widenLB = wideningLowerBounds(getVariableRangeType(v)) and
not widenLB > truncatedLB
|
widenLB
@@ -1359,7 +1359,7 @@ private float getDefUpperBounds(RangeSsaDefinition def, StackVariable v) {
// from exploding.
result =
min(float widenUB |
widenUB = wideningUpperBounds(v.getUnspecifiedType()) and
widenUB = wideningUpperBounds(getVariableRangeType(v)) and
not widenUB < truncatedUB
|
widenUB
@@ -1391,9 +1391,10 @@ private predicate unanalyzableDefBounds(RangeSsaDefinition def, StackVariable v,
*/
bindingset[guard, v, branch]
predicate nonNanGuardedVariable(ComparisonOperation guard, VariableAccess v, boolean branch) {
v.getUnspecifiedType() instanceof IntegralType
getVariableRangeType(v.getTarget()) instanceof IntegralType
or
v.getUnspecifiedType() instanceof FloatingPointType and v instanceof NonNanVariableAccess
getVariableRangeType(v.getTarget()) instanceof FloatingPointType and
v instanceof NonNanVariableAccess
or
// The reason the following case is here is to ensure that when we say
// `if (x > 5) { ...then... } else { ...else... }`
@@ -1418,7 +1419,7 @@ private predicate lowerBoundFromGuard(
then
if
strictness = Nonstrict() or
not v.getUnspecifiedType() instanceof IntegralType
not getVariableRangeType(v.getTarget()) instanceof IntegralType
then lb = childLB
else lb = childLB + 1
else lb = varMinVal(v.getTarget())
@@ -1440,7 +1441,7 @@ private predicate upperBoundFromGuard(
then
if
strictness = Nonstrict() or
not v.getUnspecifiedType() instanceof IntegralType
not getVariableRangeType(v.getTarget()) instanceof IntegralType
then ub = childUB
else ub = childUB - 1
else ub = varMaxVal(v.getTarget())

View File

@@ -437,12 +437,34 @@
| movableclass.cpp:65:13:65:18 | call to source | movableclass.cpp:65:13:65:20 | call to MyMovableClass | TAINT |
| movableclass.cpp:65:13:65:20 | call to MyMovableClass | movableclass.cpp:65:8:65:9 | ref arg s3 | TAINT |
| movableclass.cpp:65:13:65:20 | call to MyMovableClass | movableclass.cpp:65:11:65:11 | call to operator= | TAINT |
| stl.h:139:30:139:40 | call to allocator | stl.h:139:21:139:41 | noexcept(...) | TAINT |
| stl.h:139:30:139:40 | call to allocator | stl.h:139:21:139:41 | noexcept(...) | TAINT |
| stl.h:139:30:139:40 | call to allocator | stl.h:139:21:139:41 | noexcept(...) | TAINT |
| stl.h:139:30:139:40 | call to allocator | stl.h:139:21:139:41 | noexcept(...) | TAINT |
| stl.h:139:30:139:40 | call to allocator | stl.h:139:21:139:41 | noexcept(...) | TAINT |
| stl.h:139:53:139:63 | 0 | stl.h:139:46:139:64 | (no string representation) | TAINT |
| standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:40:11:40:17 | source1 | |
| standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:41:12:41:18 | source1 | |
| standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:42:14:42:20 | source1 | |
| standalone_iterators.cpp:40:11:40:17 | source1 | standalone_iterators.cpp:40:10:40:10 | call to operator* | TAINT |
| standalone_iterators.cpp:41:12:41:18 | ref arg source1 | standalone_iterators.cpp:42:14:42:20 | source1 | |
| standalone_iterators.cpp:41:12:41:18 | source1 | standalone_iterators.cpp:41:19:41:19 | call to operator++ | TAINT |
| standalone_iterators.cpp:41:19:41:19 | call to operator++ | standalone_iterators.cpp:41:10:41:10 | call to operator* | TAINT |
| standalone_iterators.cpp:42:12:42:12 | call to operator++ | standalone_iterators.cpp:42:10:42:10 | call to operator* | TAINT |
| standalone_iterators.cpp:42:14:42:20 | source1 | standalone_iterators.cpp:42:12:42:12 | call to operator++ | TAINT |
| standalone_iterators.cpp:45:39:45:45 | source1 | standalone_iterators.cpp:46:11:46:17 | source1 | |
| standalone_iterators.cpp:45:39:45:45 | source1 | standalone_iterators.cpp:47:12:47:18 | source1 | |
| standalone_iterators.cpp:45:39:45:45 | source1 | standalone_iterators.cpp:48:14:48:20 | source1 | |
| standalone_iterators.cpp:46:11:46:17 | source1 | standalone_iterators.cpp:46:10:46:10 | call to operator* | TAINT |
| standalone_iterators.cpp:47:12:47:18 | ref arg source1 | standalone_iterators.cpp:48:14:48:20 | source1 | |
| standalone_iterators.cpp:47:12:47:18 | source1 | standalone_iterators.cpp:47:19:47:19 | call to operator++ | TAINT |
| standalone_iterators.cpp:47:19:47:19 | call to operator++ | standalone_iterators.cpp:47:10:47:10 | call to operator* | TAINT |
| standalone_iterators.cpp:48:12:48:12 | call to operator++ | standalone_iterators.cpp:48:10:48:10 | call to operator* | TAINT |
| standalone_iterators.cpp:48:14:48:20 | source1 | standalone_iterators.cpp:48:12:48:12 | call to operator++ | TAINT |
| standalone_iterators.cpp:51:37:51:43 | source1 | standalone_iterators.cpp:52:11:52:17 | source1 | |
| standalone_iterators.cpp:51:37:51:43 | source1 | standalone_iterators.cpp:53:12:53:18 | source1 | |
| standalone_iterators.cpp:51:37:51:43 | source1 | standalone_iterators.cpp:54:14:54:20 | source1 | |
| standalone_iterators.cpp:53:12:53:18 | ref arg source1 | standalone_iterators.cpp:54:14:54:20 | source1 | |
| stl.h:156:30:156:40 | call to allocator | stl.h:156:21:156:41 | noexcept(...) | TAINT |
| stl.h:156:30:156:40 | call to allocator | stl.h:156:21:156:41 | noexcept(...) | TAINT |
| stl.h:156:30:156:40 | call to allocator | stl.h:156:21:156:41 | noexcept(...) | TAINT |
| stl.h:156:30:156:40 | call to allocator | stl.h:156:21:156:41 | noexcept(...) | TAINT |
| stl.h:156:30:156:40 | call to allocator | stl.h:156:21:156:41 | noexcept(...) | TAINT |
| stl.h:156:53:156:63 | 0 | stl.h:156:46:156:64 | (no string representation) | TAINT |
| string.cpp:24:12:24:17 | call to source | string.cpp:28:7:28:7 | a | |
| string.cpp:25:16:25:20 | 123 | string.cpp:25:16:25:21 | call to basic_string | TAINT |
| string.cpp:25:16:25:21 | call to basic_string | string.cpp:29:7:29:7 | b | |
@@ -503,6 +525,10 @@
| string.cpp:119:16:119:24 | call to basic_string | string.cpp:124:33:124:33 | s | |
| string.cpp:119:16:119:24 | call to basic_string | string.cpp:124:50:124:50 | s | |
| string.cpp:119:16:119:24 | call to basic_string | string.cpp:128:16:128:16 | s | |
| string.cpp:120:15:120:15 | (__begin) | string.cpp:120:15:120:15 | call to operator* | TAINT |
| string.cpp:120:15:120:15 | (__begin) | string.cpp:120:15:120:15 | call to operator++ | TAINT |
| string.cpp:120:15:120:15 | (__range) | string.cpp:120:15:120:15 | call to begin | TAINT |
| string.cpp:120:15:120:15 | (__range) | string.cpp:120:15:120:15 | call to end | TAINT |
| string.cpp:120:15:120:15 | call to begin | string.cpp:120:15:120:15 | (__begin) | |
| string.cpp:120:15:120:15 | call to begin | string.cpp:120:15:120:15 | (__begin) | |
| string.cpp:120:15:120:15 | call to begin | string.cpp:120:15:120:15 | (__begin) | |
@@ -517,14 +543,22 @@
| string.cpp:120:15:120:15 | s | string.cpp:120:15:120:15 | call to operator* | TAINT |
| string.cpp:124:33:124:33 | ref arg s | string.cpp:124:50:124:50 | s | |
| string.cpp:124:33:124:33 | ref arg s | string.cpp:128:16:128:16 | s | |
| string.cpp:124:33:124:33 | s | string.cpp:124:35:124:39 | call to begin | TAINT |
| string.cpp:124:35:124:39 | call to begin | string.cpp:124:44:124:45 | it | |
| string.cpp:124:35:124:39 | call to begin | string.cpp:124:61:124:62 | it | |
| string.cpp:124:35:124:39 | call to begin | string.cpp:125:9:125:10 | it | |
| string.cpp:124:50:124:50 | ref arg s | string.cpp:124:50:124:50 | s | |
| string.cpp:124:50:124:50 | ref arg s | string.cpp:128:16:128:16 | s | |
| string.cpp:124:50:124:50 | s | string.cpp:124:52:124:54 | call to end | TAINT |
| string.cpp:124:61:124:62 | it | string.cpp:124:59:124:59 | call to operator++ | TAINT |
| string.cpp:124:61:124:62 | ref arg it | string.cpp:124:44:124:45 | it | |
| string.cpp:124:61:124:62 | ref arg it | string.cpp:124:61:124:62 | it | |
| string.cpp:124:61:124:62 | ref arg it | string.cpp:125:9:125:10 | it | |
| string.cpp:125:9:125:10 | it | string.cpp:125:8:125:8 | call to operator* | TAINT |
| string.cpp:128:16:128:16 | (__begin) | string.cpp:128:16:128:16 | call to operator* | TAINT |
| string.cpp:128:16:128:16 | (__begin) | string.cpp:128:16:128:16 | call to operator++ | TAINT |
| string.cpp:128:16:128:16 | (__range) | string.cpp:128:16:128:16 | call to begin | TAINT |
| string.cpp:128:16:128:16 | (__range) | string.cpp:128:16:128:16 | call to end | TAINT |
| string.cpp:128:16:128:16 | call to begin | string.cpp:128:16:128:16 | (__begin) | |
| string.cpp:128:16:128:16 | call to begin | string.cpp:128:16:128:16 | (__begin) | |
| string.cpp:128:16:128:16 | call to begin | string.cpp:128:16:128:16 | (__begin) | |
@@ -539,6 +573,10 @@
| string.cpp:128:16:128:16 | s | string.cpp:128:16:128:16 | call to operator* | TAINT |
| string.cpp:132:28:132:33 | call to source | string.cpp:132:28:132:36 | call to basic_string | TAINT |
| string.cpp:132:28:132:36 | call to basic_string | string.cpp:133:22:133:28 | const_s | |
| string.cpp:133:22:133:22 | (__begin) | string.cpp:133:22:133:22 | call to operator* | TAINT |
| string.cpp:133:22:133:22 | (__begin) | string.cpp:133:22:133:22 | call to operator++ | TAINT |
| string.cpp:133:22:133:22 | (__range) | string.cpp:133:22:133:22 | call to begin | TAINT |
| string.cpp:133:22:133:22 | (__range) | string.cpp:133:22:133:22 | call to end | TAINT |
| string.cpp:133:22:133:22 | call to begin | string.cpp:133:22:133:22 | (__begin) | |
| string.cpp:133:22:133:22 | call to begin | string.cpp:133:22:133:22 | (__begin) | |
| string.cpp:133:22:133:22 | call to begin | string.cpp:133:22:133:22 | (__begin) | |
@@ -857,6 +895,122 @@
| string.cpp:348:13:348:13 | 1 | string.cpp:348:2:348:14 | access to array | TAINT |
| string.cpp:348:18:348:32 | call to source | string.cpp:348:2:348:34 | ... = ... | |
| string.cpp:350:7:350:9 | str | string.cpp:350:11:350:14 | call to data | TAINT |
| string.cpp:355:18:355:24 | hello | string.cpp:355:18:355:25 | call to basic_string | TAINT |
| string.cpp:355:18:355:25 | call to basic_string | string.cpp:360:8:360:9 | s1 | |
| string.cpp:355:18:355:25 | call to basic_string | string.cpp:361:8:361:9 | s1 | |
| string.cpp:355:18:355:25 | call to basic_string | string.cpp:362:8:362:9 | s1 | |
| string.cpp:356:18:356:23 | call to source | string.cpp:356:18:356:26 | call to basic_string | TAINT |
| string.cpp:356:18:356:26 | call to basic_string | string.cpp:361:18:361:19 | s2 | |
| string.cpp:356:18:356:26 | call to basic_string | string.cpp:361:30:361:31 | s2 | |
| string.cpp:357:18:357:24 | hello | string.cpp:357:18:357:25 | call to basic_string | TAINT |
| string.cpp:357:18:357:25 | call to basic_string | string.cpp:364:8:364:9 | s3 | |
| string.cpp:357:18:357:25 | call to basic_string | string.cpp:365:8:365:9 | s3 | |
| string.cpp:357:18:357:25 | call to basic_string | string.cpp:366:8:366:9 | s3 | |
| string.cpp:358:18:358:24 | world | string.cpp:358:18:358:25 | call to basic_string | TAINT |
| string.cpp:358:18:358:25 | call to basic_string | string.cpp:365:18:365:19 | s4 | |
| string.cpp:358:18:358:25 | call to basic_string | string.cpp:365:30:365:31 | s4 | |
| string.cpp:361:8:361:9 | ref arg s1 | string.cpp:362:8:362:9 | s1 | |
| string.cpp:361:8:361:9 | s1 | string.cpp:361:11:361:16 | call to append | TAINT |
| string.cpp:361:18:361:19 | ref arg s2 | string.cpp:361:30:361:31 | s2 | |
| string.cpp:361:18:361:19 | s2 | string.cpp:361:21:361:25 | call to begin | TAINT |
| string.cpp:361:21:361:25 | call to begin | string.cpp:361:8:361:9 | ref arg s1 | TAINT |
| string.cpp:361:21:361:25 | call to begin | string.cpp:361:11:361:16 | call to append | TAINT |
| string.cpp:361:30:361:31 | s2 | string.cpp:361:33:361:35 | call to end | TAINT |
| string.cpp:361:33:361:35 | call to end | string.cpp:361:8:361:9 | ref arg s1 | TAINT |
| string.cpp:361:33:361:35 | call to end | string.cpp:361:11:361:16 | call to append | TAINT |
| string.cpp:365:8:365:9 | ref arg s3 | string.cpp:366:8:366:9 | s3 | |
| string.cpp:365:8:365:9 | s3 | string.cpp:365:11:365:16 | call to append | TAINT |
| string.cpp:365:18:365:19 | ref arg s4 | string.cpp:365:30:365:31 | s4 | |
| string.cpp:365:18:365:19 | s4 | string.cpp:365:21:365:25 | call to begin | TAINT |
| string.cpp:365:21:365:25 | call to begin | string.cpp:365:8:365:9 | ref arg s3 | TAINT |
| string.cpp:365:21:365:25 | call to begin | string.cpp:365:11:365:16 | call to append | TAINT |
| string.cpp:365:30:365:31 | s4 | string.cpp:365:33:365:35 | call to end | TAINT |
| string.cpp:365:33:365:35 | call to end | string.cpp:365:8:365:9 | ref arg s3 | TAINT |
| string.cpp:365:33:365:35 | call to end | string.cpp:365:11:365:16 | call to append | TAINT |
| string.cpp:371:18:371:24 | hello | string.cpp:371:18:371:25 | call to basic_string | TAINT |
| string.cpp:371:18:371:25 | call to basic_string | string.cpp:374:28:374:29 | s1 | |
| string.cpp:372:18:372:23 | call to source | string.cpp:372:18:372:26 | call to basic_string | TAINT |
| string.cpp:372:18:372:26 | call to basic_string | string.cpp:378:28:378:29 | s2 | |
| string.cpp:374:28:374:29 | s1 | string.cpp:374:31:374:35 | call to begin | TAINT |
| string.cpp:374:31:374:35 | call to begin | string.cpp:376:9:376:13 | iter1 | |
| string.cpp:374:31:374:35 | call to begin | string.cpp:377:8:377:12 | iter1 | |
| string.cpp:376:9:376:13 | iter1 | string.cpp:376:8:376:8 | call to operator* | TAINT |
| string.cpp:377:8:377:12 | iter1 | string.cpp:377:13:377:13 | call to operator[] | TAINT |
| string.cpp:378:28:378:29 | s2 | string.cpp:378:31:378:35 | call to begin | TAINT |
| string.cpp:378:31:378:35 | call to begin | string.cpp:380:9:380:13 | iter2 | |
| string.cpp:378:31:378:35 | call to begin | string.cpp:381:8:381:12 | iter2 | |
| string.cpp:380:9:380:13 | iter2 | string.cpp:380:8:380:8 | call to operator* | TAINT |
| string.cpp:381:8:381:12 | iter2 | string.cpp:381:13:381:13 | call to operator[] | TAINT |
| string.cpp:386:18:386:24 | hello | string.cpp:386:18:386:25 | call to basic_string | TAINT |
| string.cpp:386:18:386:25 | call to basic_string | string.cpp:389:25:389:26 | s1 | |
| string.cpp:387:18:387:23 | call to source | string.cpp:387:18:387:26 | call to basic_string | TAINT |
| string.cpp:387:18:387:26 | call to basic_string | string.cpp:391:25:391:26 | s2 | |
| string.cpp:387:18:387:26 | call to basic_string | string.cpp:411:8:411:9 | s2 | |
| string.cpp:389:25:389:26 | s1 | string.cpp:389:28:389:32 | call to begin | TAINT |
| string.cpp:391:25:391:26 | ref arg s2 | string.cpp:411:8:411:9 | s2 | |
| string.cpp:391:25:391:26 | s2 | string.cpp:391:28:391:32 | call to begin | TAINT |
| string.cpp:391:28:391:32 | call to begin | string.cpp:394:10:394:11 | i2 | |
| string.cpp:391:28:391:32 | call to begin | string.cpp:395:10:395:11 | i2 | |
| string.cpp:391:28:391:32 | call to begin | string.cpp:396:8:396:9 | i2 | |
| string.cpp:391:28:391:32 | call to begin | string.cpp:398:8:398:9 | i2 | |
| string.cpp:391:28:391:32 | call to begin | string.cpp:400:8:400:9 | i2 | |
| string.cpp:391:28:391:32 | call to begin | string.cpp:403:8:403:9 | i2 | |
| string.cpp:391:28:391:32 | call to begin | string.cpp:406:8:406:9 | i2 | |
| string.cpp:391:28:391:32 | call to begin | string.cpp:408:8:408:9 | i2 | |
| string.cpp:394:10:394:11 | i2 | string.cpp:394:12:394:12 | call to operator+ | TAINT |
| string.cpp:394:10:394:11 | ref arg i2 | string.cpp:395:10:395:11 | i2 | |
| string.cpp:394:10:394:11 | ref arg i2 | string.cpp:396:8:396:9 | i2 | |
| string.cpp:394:10:394:11 | ref arg i2 | string.cpp:398:8:398:9 | i2 | |
| string.cpp:394:10:394:11 | ref arg i2 | string.cpp:400:8:400:9 | i2 | |
| string.cpp:394:10:394:11 | ref arg i2 | string.cpp:403:8:403:9 | i2 | |
| string.cpp:394:10:394:11 | ref arg i2 | string.cpp:406:8:406:9 | i2 | |
| string.cpp:394:10:394:11 | ref arg i2 | string.cpp:408:8:408:9 | i2 | |
| string.cpp:394:12:394:12 | call to operator+ | string.cpp:394:8:394:8 | call to operator* | TAINT |
| string.cpp:395:10:395:11 | i2 | string.cpp:395:12:395:12 | call to operator- | TAINT |
| string.cpp:395:10:395:11 | ref arg i2 | string.cpp:396:8:396:9 | i2 | |
| string.cpp:395:10:395:11 | ref arg i2 | string.cpp:398:8:398:9 | i2 | |
| string.cpp:395:10:395:11 | ref arg i2 | string.cpp:400:8:400:9 | i2 | |
| string.cpp:395:10:395:11 | ref arg i2 | string.cpp:403:8:403:9 | i2 | |
| string.cpp:395:10:395:11 | ref arg i2 | string.cpp:406:8:406:9 | i2 | |
| string.cpp:395:10:395:11 | ref arg i2 | string.cpp:408:8:408:9 | i2 | |
| string.cpp:395:12:395:12 | call to operator- | string.cpp:395:8:395:8 | call to operator* | TAINT |
| string.cpp:396:8:396:9 | i2 | string.cpp:396:3:396:9 | ... = ... | |
| string.cpp:396:8:396:9 | i2 | string.cpp:397:12:397:13 | i3 | |
| string.cpp:397:10:397:10 | call to operator++ | string.cpp:397:8:397:8 | call to operator* | TAINT |
| string.cpp:397:12:397:13 | i3 | string.cpp:397:10:397:10 | call to operator++ | TAINT |
| string.cpp:398:8:398:9 | i2 | string.cpp:398:3:398:9 | ... = ... | |
| string.cpp:398:8:398:9 | i2 | string.cpp:399:12:399:13 | i4 | |
| string.cpp:399:10:399:10 | call to operator-- | string.cpp:399:8:399:8 | call to operator* | TAINT |
| string.cpp:399:12:399:13 | i4 | string.cpp:399:10:399:10 | call to operator-- | TAINT |
| string.cpp:400:8:400:9 | i2 | string.cpp:400:3:400:9 | ... = ... | |
| string.cpp:400:8:400:9 | i2 | string.cpp:401:3:401:4 | i5 | |
| string.cpp:400:8:400:9 | i2 | string.cpp:402:9:402:10 | i5 | |
| string.cpp:401:3:401:4 | i5 | string.cpp:401:5:401:5 | call to operator++ | TAINT |
| string.cpp:401:3:401:4 | ref arg i5 | string.cpp:402:9:402:10 | i5 | |
| string.cpp:402:9:402:10 | i5 | string.cpp:402:8:402:8 | call to operator* | TAINT |
| string.cpp:403:8:403:9 | i2 | string.cpp:403:3:403:9 | ... = ... | |
| string.cpp:403:8:403:9 | i2 | string.cpp:404:3:404:4 | i6 | |
| string.cpp:403:8:403:9 | i2 | string.cpp:405:9:405:10 | i6 | |
| string.cpp:404:3:404:4 | i6 | string.cpp:404:5:404:5 | call to operator-- | TAINT |
| string.cpp:404:3:404:4 | ref arg i6 | string.cpp:405:9:405:10 | i6 | |
| string.cpp:405:9:405:10 | i6 | string.cpp:405:8:405:8 | call to operator* | TAINT |
| string.cpp:406:8:406:9 | i2 | string.cpp:406:3:406:9 | ... = ... | |
| string.cpp:406:8:406:9 | i2 | string.cpp:407:10:407:11 | i7 | |
| string.cpp:407:10:407:11 | i7 | string.cpp:407:12:407:12 | call to operator+= | TAINT |
| string.cpp:407:12:407:12 | call to operator+= | string.cpp:407:8:407:8 | call to operator* | TAINT |
| string.cpp:407:14:407:14 | 1 | string.cpp:407:12:407:12 | call to operator+= | |
| string.cpp:408:8:408:9 | i2 | string.cpp:408:3:408:9 | ... = ... | |
| string.cpp:408:8:408:9 | i2 | string.cpp:409:10:409:11 | i8 | |
| string.cpp:409:10:409:11 | i8 | string.cpp:409:12:409:12 | call to operator-= | TAINT |
| string.cpp:409:12:409:12 | call to operator-= | string.cpp:409:8:409:8 | call to operator* | TAINT |
| string.cpp:409:14:409:14 | 1 | string.cpp:409:12:409:12 | call to operator-= | |
| string.cpp:411:8:411:9 | s2 | string.cpp:411:11:411:13 | call to end | TAINT |
| string.cpp:411:11:411:13 | call to end | string.cpp:411:3:411:15 | ... = ... | |
| string.cpp:411:11:411:13 | call to end | string.cpp:412:5:412:6 | i9 | |
| string.cpp:411:11:411:13 | call to end | string.cpp:413:9:413:10 | i9 | |
| string.cpp:412:5:412:6 | i9 | string.cpp:412:3:412:3 | call to operator-- | TAINT |
| string.cpp:412:5:412:6 | ref arg i9 | string.cpp:413:9:413:10 | i9 | |
| string.cpp:413:9:413:10 | i9 | string.cpp:413:8:413:8 | call to operator* | TAINT |
| stringstream.cpp:13:20:13:22 | call to basic_stringstream | stringstream.cpp:16:2:16:4 | ss1 | |
| stringstream.cpp:13:20:13:22 | call to basic_stringstream | stringstream.cpp:22:7:22:9 | ss1 | |
| stringstream.cpp:13:20:13:22 | call to basic_stringstream | stringstream.cpp:27:7:27:9 | ss1 | |
@@ -1718,6 +1872,8 @@
| vector.cpp:17:21:17:33 | call to vector | vector.cpp:27:15:27:15 | v | |
| vector.cpp:17:21:17:33 | call to vector | vector.cpp:35:1:35:1 | v | |
| vector.cpp:17:26:17:32 | source1 | vector.cpp:17:21:17:33 | call to vector | TAINT |
| vector.cpp:19:14:19:14 | (__begin) | vector.cpp:19:14:19:14 | call to operator* | TAINT |
| vector.cpp:19:14:19:14 | (__begin) | vector.cpp:19:14:19:14 | call to operator++ | TAINT |
| vector.cpp:19:14:19:14 | call to begin | vector.cpp:19:14:19:14 | (__begin) | |
| vector.cpp:19:14:19:14 | call to begin | vector.cpp:19:14:19:14 | (__begin) | |
| vector.cpp:19:14:19:14 | call to begin | vector.cpp:19:14:19:14 | (__begin) | |
@@ -1739,9 +1895,13 @@
| vector.cpp:23:55:23:55 | ref arg v | vector.cpp:23:55:23:55 | v | |
| vector.cpp:23:55:23:55 | ref arg v | vector.cpp:27:15:27:15 | v | |
| vector.cpp:23:55:23:55 | ref arg v | vector.cpp:35:1:35:1 | v | |
| vector.cpp:23:66:23:67 | it | vector.cpp:23:64:23:64 | call to operator++ | TAINT |
| vector.cpp:23:66:23:67 | ref arg it | vector.cpp:23:49:23:50 | it | |
| vector.cpp:23:66:23:67 | ref arg it | vector.cpp:23:66:23:67 | it | |
| vector.cpp:23:66:23:67 | ref arg it | vector.cpp:24:9:24:10 | it | |
| vector.cpp:24:9:24:10 | it | vector.cpp:24:8:24:8 | call to operator* | TAINT |
| vector.cpp:27:15:27:15 | (__begin) | vector.cpp:27:15:27:15 | call to operator* | TAINT |
| vector.cpp:27:15:27:15 | (__begin) | vector.cpp:27:15:27:15 | call to operator++ | TAINT |
| vector.cpp:27:15:27:15 | call to begin | vector.cpp:27:15:27:15 | (__begin) | |
| vector.cpp:27:15:27:15 | call to begin | vector.cpp:27:15:27:15 | (__begin) | |
| vector.cpp:27:15:27:15 | call to begin | vector.cpp:27:15:27:15 | (__begin) | |
@@ -1757,6 +1917,8 @@
| vector.cpp:31:33:31:45 | call to vector | vector.cpp:32:21:32:27 | const_v | |
| vector.cpp:31:33:31:45 | call to vector | vector.cpp:35:1:35:1 | const_v | |
| vector.cpp:31:38:31:44 | source1 | vector.cpp:31:33:31:45 | call to vector | TAINT |
| vector.cpp:32:21:32:21 | (__begin) | vector.cpp:32:21:32:21 | call to operator* | TAINT |
| vector.cpp:32:21:32:21 | (__begin) | vector.cpp:32:21:32:21 | call to operator++ | TAINT |
| vector.cpp:32:21:32:21 | call to begin | vector.cpp:32:21:32:21 | (__begin) | |
| vector.cpp:32:21:32:21 | call to begin | vector.cpp:32:21:32:21 | (__begin) | |
| vector.cpp:32:21:32:21 | call to begin | vector.cpp:32:21:32:21 | (__begin) | |
@@ -2415,6 +2577,7 @@
| vector.cpp:251:11:251:15 | call to begin | vector.cpp:253:8:253:9 | i1 | |
| vector.cpp:251:11:251:15 | call to begin | vector.cpp:255:13:255:14 | i1 | |
| vector.cpp:251:11:251:15 | call to begin | vector.cpp:259:8:259:9 | i1 | |
| vector.cpp:252:3:252:4 | i1 | vector.cpp:252:5:252:5 | call to operator++ | TAINT |
| vector.cpp:252:3:252:4 | ref arg i1 | vector.cpp:253:8:253:9 | i1 | |
| vector.cpp:252:3:252:4 | ref arg i1 | vector.cpp:255:13:255:14 | i1 | |
| vector.cpp:252:3:252:4 | ref arg i1 | vector.cpp:259:8:259:9 | i1 | |
@@ -2422,6 +2585,7 @@
| vector.cpp:253:8:253:9 | i1 | vector.cpp:254:3:254:4 | i2 | |
| vector.cpp:253:8:253:9 | i1 | vector.cpp:255:17:255:18 | i2 | |
| vector.cpp:253:8:253:9 | i1 | vector.cpp:260:8:260:9 | i2 | |
| vector.cpp:254:3:254:4 | i2 | vector.cpp:254:5:254:5 | call to operator++ | TAINT |
| vector.cpp:254:3:254:4 | ref arg i2 | vector.cpp:255:17:255:18 | i2 | |
| vector.cpp:254:3:254:4 | ref arg i2 | vector.cpp:260:8:260:9 | i2 | |
| vector.cpp:255:3:255:4 | ref arg v6 | vector.cpp:261:8:261:9 | v6 | |

View File

@@ -0,0 +1,55 @@
#include "stl.h"
using namespace std;
void sink(int);
class int_iterator_by_typedefs {
public:
typedef input_iterator_tag iterator_category;
typedef int value_type;
typedef size_t difference_type;
typedef int * pointer;
typedef int & reference;
int_iterator_by_typedefs &operator++();
int_iterator_by_typedefs operator++(int);
int operator*() const;
};
class int_iterator_by_trait {
public:
int_iterator_by_trait &operator++();
int_iterator_by_trait operator++(int);
int operator*() const;
};
template<>
struct std::iterator_traits<int_iterator_by_trait> {
typedef input_iterator_tag iterator_category;
};
class non_iterator {
public:
non_iterator &operator++();
non_iterator operator++(int);
int operator*() const;
};
void test_typedefs(int_iterator_by_typedefs source1) {
sink(*source1);
sink(*(source1++));
sink(*(++source1));
}
void test_trait(int_iterator_by_trait source1) {
sink(*source1);
sink(*(source1++));
sink(*(++source1));
}
void test_non_iterator(non_iterator source1) {
sink(*source1);
sink(*(source1++));
sink(*(++source1));
}

View File

@@ -1,16 +1,13 @@
typedef unsigned long size_t;
// --- string ---
namespace std
{
template<class charT> struct char_traits;
typedef size_t streamsize;
// --- iterator ---
namespace std {
struct ptrdiff_t;
template<class I> struct iterator_traits;
template <class Category,
class value_type,
class difference_type = ptrdiff_t,
@@ -21,15 +18,33 @@ namespace std
iterator &operator++();
iterator operator++(int);
iterator &operator--();
iterator operator--(int);
bool operator==(iterator other) const;
bool operator!=(iterator other) const;
reference_type operator*() const;
iterator operator+(int);
iterator operator-(int);
iterator &operator+=(int);
iterator &operator-=(int);
int operator-(iterator);
reference_type operator[](int);
};
struct input_iterator_tag {};
struct forward_iterator_tag : public input_iterator_tag {};
struct bidirectional_iterator_tag : public forward_iterator_tag {};
struct random_access_iterator_tag : public bidirectional_iterator_tag {};
}
// --- string ---
namespace std
{
template<class charT> struct char_traits;
typedef size_t streamsize;
template <class T> class allocator {
public:
@@ -72,6 +87,8 @@ namespace std
basic_string& append(const basic_string& str);
basic_string& append(const charT* s);
basic_string& append(size_type n, charT c);
template<class InputIterator>
/* constexpr */ basic_string& append(InputIterator first, InputIterator last);
basic_string& assign(const basic_string& str);
basic_string& assign(size_type n, charT c);
basic_string& insert(size_type pos, const basic_string& str);

View File

@@ -122,7 +122,7 @@ void test_range_based_for_loop_string() {
}
for(std::string::iterator it = s.begin(); it != s.end(); ++it) {
sink(*it); // tainted [NOT DETECTED]
sink(*it); // tainted [NOT DETECTED by IR]
}
for(char& c : s) {
@@ -349,3 +349,67 @@ void test_string_data_more()
sink(str); // tainted
sink(str.data()); // tainted
}
void test_string_iterators() {
// string append
{
std::string s1("hello");
std::string s2(source());
std::string s3("hello");
std::string s4("world");
sink(s1);
sink(s1.append(s2.begin(), s2.end())); // tainted
sink(s1); // tainted
sink(s3);
sink(s3.append(s4.begin(), s4.end()));
sink(s3);
}
// dereference
{
std::string s1("hello");
std::string s2(source());
string::iterator iter1 = s1.begin();
sink(*iter1);
sink(iter1[1]);
string::iterator iter2 = s2.begin();
sink(*iter2); // tainted
sink(iter2[1]); // tainted
}
// arithmetic operators
{
std::string s1("hello");
std::string s2(source());
string::iterator i1 = s1.begin();
string::iterator i2 = s2.begin();
string::iterator i3, i4, i5, i6, i7, i8, i9;
sink(*(i2+1)); //tainted
sink(*(i2-1)); // tainted
i3 = i2;
sink(*(++i3)); // tainted
i4 = i2;
sink(*(--i4)); // tainted
i5 = i2;
i5++;
sink(*i5); // tainted
i6 = i2;
i6--;
sink(*i6); // tainted
i7 = i2;
sink(*(i7+=1)); // tainted
i8 = i2;
sink(*(i8-=1)); // tainted
i9 = s2.end();
--i9;
sink(*i9); // tainted
}
}

View File

@@ -37,6 +37,12 @@
| movableclass.cpp:55:8:55:9 | s2 | movableclass.cpp:52:23:52:28 | call to source |
| movableclass.cpp:64:8:64:9 | s2 | movableclass.cpp:23:55:23:60 | call to source |
| movableclass.cpp:65:11:65:11 | call to operator= | movableclass.cpp:65:13:65:18 | call to source |
| standalone_iterators.cpp:40:10:40:10 | call to operator* | standalone_iterators.cpp:39:45:39:51 | source1 |
| standalone_iterators.cpp:41:10:41:10 | call to operator* | standalone_iterators.cpp:39:45:39:51 | source1 |
| standalone_iterators.cpp:42:10:42:10 | call to operator* | standalone_iterators.cpp:39:45:39:51 | source1 |
| standalone_iterators.cpp:46:10:46:10 | call to operator* | standalone_iterators.cpp:45:39:45:45 | source1 |
| standalone_iterators.cpp:47:10:47:10 | call to operator* | standalone_iterators.cpp:45:39:45:45 | source1 |
| standalone_iterators.cpp:48:10:48:10 | call to operator* | standalone_iterators.cpp:45:39:45:45 | source1 |
| string.cpp:28:7:28:7 | a | string.cpp:24:12:24:17 | call to source |
| string.cpp:30:7:30:7 | c | string.cpp:26:16:26:21 | call to source |
| string.cpp:32:9:32:13 | call to c_str | string.cpp:26:16:26:21 | call to source |
@@ -53,6 +59,7 @@
| string.cpp:113:8:113:9 | s1 | string.cpp:109:32:109:37 | call to source |
| string.cpp:114:8:114:9 | s2 | string.cpp:111:20:111:25 | call to source |
| string.cpp:121:8:121:8 | c | string.cpp:119:16:119:21 | call to source |
| string.cpp:125:8:125:8 | call to operator* | string.cpp:119:16:119:21 | call to source |
| string.cpp:129:8:129:8 | c | string.cpp:119:16:119:21 | call to source |
| string.cpp:134:8:134:8 | c | string.cpp:132:28:132:33 | call to source |
| string.cpp:144:11:144:11 | call to operator+ | string.cpp:141:18:141:23 | call to source |
@@ -101,6 +108,19 @@
| string.cpp:341:7:341:7 | c | string.cpp:335:9:335:23 | call to source |
| string.cpp:349:7:349:9 | str | string.cpp:348:18:348:32 | call to source |
| string.cpp:350:11:350:14 | call to data | string.cpp:348:18:348:32 | call to source |
| string.cpp:361:11:361:16 | call to append | string.cpp:356:18:356:23 | call to source |
| string.cpp:362:8:362:9 | s1 | string.cpp:356:18:356:23 | call to source |
| string.cpp:380:8:380:8 | call to operator* | string.cpp:372:18:372:23 | call to source |
| string.cpp:381:13:381:13 | call to operator[] | string.cpp:372:18:372:23 | call to source |
| string.cpp:394:8:394:8 | call to operator* | string.cpp:387:18:387:23 | call to source |
| string.cpp:395:8:395:8 | call to operator* | string.cpp:387:18:387:23 | call to source |
| string.cpp:397:8:397:8 | call to operator* | string.cpp:387:18:387:23 | call to source |
| string.cpp:399:8:399:8 | call to operator* | string.cpp:387:18:387:23 | call to source |
| string.cpp:402:8:402:8 | call to operator* | string.cpp:387:18:387:23 | call to source |
| string.cpp:405:8:405:8 | call to operator* | string.cpp:387:18:387:23 | call to source |
| string.cpp:407:8:407:8 | call to operator* | string.cpp:387:18:387:23 | call to source |
| string.cpp:409:8:409:8 | call to operator* | string.cpp:387:18:387:23 | call to source |
| string.cpp:413:8:413:8 | call to operator* | string.cpp:387:18:387:23 | call to source |
| structlikeclass.cpp:35:8:35:9 | s1 | structlikeclass.cpp:29:22:29:27 | call to source |
| structlikeclass.cpp:36:8:36:9 | s2 | structlikeclass.cpp:30:24:30:29 | call to source |
| structlikeclass.cpp:37:8:37:9 | s3 | structlikeclass.cpp:29:22:29:27 | call to source |

View File

@@ -38,6 +38,12 @@
| format.cpp:115:8:115:13 | format.cpp:114:37:114:50 | AST only |
| movableclass.cpp:65:11:65:11 | movableclass.cpp:65:13:65:18 | AST only |
| movableclass.cpp:65:11:65:21 | movableclass.cpp:65:13:65:18 | IR only |
| standalone_iterators.cpp:40:10:40:10 | standalone_iterators.cpp:39:45:39:51 | AST only |
| standalone_iterators.cpp:41:10:41:10 | standalone_iterators.cpp:39:45:39:51 | AST only |
| standalone_iterators.cpp:42:10:42:10 | standalone_iterators.cpp:39:45:39:51 | AST only |
| standalone_iterators.cpp:46:10:46:10 | standalone_iterators.cpp:45:39:45:45 | AST only |
| standalone_iterators.cpp:47:10:47:10 | standalone_iterators.cpp:45:39:45:45 | AST only |
| standalone_iterators.cpp:48:10:48:10 | standalone_iterators.cpp:45:39:45:45 | AST only |
| string.cpp:30:7:30:7 | string.cpp:26:16:26:21 | AST only |
| string.cpp:32:9:32:13 | string.cpp:26:16:26:21 | AST only |
| string.cpp:38:13:38:17 | string.cpp:14:10:14:15 | AST only |
@@ -53,6 +59,7 @@
| string.cpp:113:8:113:9 | string.cpp:109:32:109:37 | AST only |
| string.cpp:114:8:114:9 | string.cpp:111:20:111:25 | AST only |
| string.cpp:121:8:121:8 | string.cpp:119:16:119:21 | AST only |
| string.cpp:125:8:125:8 | string.cpp:119:16:119:21 | AST only |
| string.cpp:129:8:129:8 | string.cpp:119:16:119:21 | AST only |
| string.cpp:134:8:134:8 | string.cpp:132:28:132:33 | AST only |
| string.cpp:144:11:144:11 | string.cpp:141:18:141:23 | AST only |
@@ -101,6 +108,19 @@
| string.cpp:341:7:341:7 | string.cpp:335:9:335:23 | AST only |
| string.cpp:349:7:349:9 | string.cpp:348:18:348:32 | AST only |
| string.cpp:350:11:350:14 | string.cpp:348:18:348:32 | AST only |
| string.cpp:361:11:361:16 | string.cpp:356:18:356:23 | AST only |
| string.cpp:362:8:362:9 | string.cpp:356:18:356:23 | AST only |
| string.cpp:380:8:380:8 | string.cpp:372:18:372:23 | AST only |
| string.cpp:381:13:381:13 | string.cpp:372:18:372:23 | AST only |
| string.cpp:394:8:394:8 | string.cpp:387:18:387:23 | AST only |
| string.cpp:395:8:395:8 | string.cpp:387:18:387:23 | AST only |
| string.cpp:397:8:397:8 | string.cpp:387:18:387:23 | AST only |
| string.cpp:399:8:399:8 | string.cpp:387:18:387:23 | AST only |
| string.cpp:402:8:402:8 | string.cpp:387:18:387:23 | AST only |
| string.cpp:405:8:405:8 | string.cpp:387:18:387:23 | AST only |
| string.cpp:407:8:407:8 | string.cpp:387:18:387:23 | AST only |
| string.cpp:409:8:409:8 | string.cpp:387:18:387:23 | AST only |
| string.cpp:413:8:413:8 | string.cpp:387:18:387:23 | AST only |
| swap1.cpp:78:12:78:16 | swap1.cpp:69:23:69:23 | AST only |
| swap1.cpp:87:13:87:17 | swap1.cpp:82:16:82:21 | AST only |
| swap1.cpp:88:13:88:17 | swap1.cpp:81:27:81:28 | AST only |

View File

@@ -573,3 +573,15 @@
| test.cpp:75:22:75:30 | c_times_2 | 0 |
| test.cpp:77:5:77:13 | c_times_2 | 0 |
| test.cpp:79:3:79:11 | c_times_2 | 0 |
| test.cpp:83:16:83:22 | aliased | -2147483648 |
| test.cpp:85:7:85:7 | i | -2147483648 |
| test.cpp:86:12:86:12 | i | 2 |
| test.cpp:88:7:88:8 | ci | -2147483648 |
| test.cpp:89:12:89:13 | ci | 2 |
| test.cpp:91:7:91:13 | aliased | -2147483648 |
| test.cpp:92:12:92:18 | aliased | -2147483648 |
| test.cpp:94:7:94:11 | alias | -2147483648 |
| test.cpp:95:12:95:16 | alias | -2147483648 |
| test.cpp:97:10:97:10 | i | -2147483648 |
| test.cpp:97:22:97:22 | i | -2147483648 |
| test.cpp:98:5:98:5 | i | -2147483648 |

View File

@@ -78,3 +78,25 @@ void use_after_cast(unsigned char c)
}
c_times_2;
}
int ref_to_number(int &i, const int &ci, int &aliased) {
int &alias = aliased; // no range analysis for either of the two aliased variables
if (i >= 2)
return i;
if (ci >= 2)
return ci;
if (aliased >= 2)
return aliased;
if (alias >= 2)
return alias;
for (; i <= 12345; i++) { // test that widening works for references
i;
}
return 0;
}

View File

@@ -573,3 +573,15 @@
| test.cpp:75:22:75:30 | c_times_2 | 510 |
| test.cpp:77:5:77:13 | c_times_2 | 510 |
| test.cpp:79:3:79:11 | c_times_2 | 510 |
| test.cpp:83:16:83:22 | aliased | 2147483647 |
| test.cpp:85:7:85:7 | i | 2147483647 |
| test.cpp:86:12:86:12 | i | 2147483647 |
| test.cpp:88:7:88:8 | ci | 2147483647 |
| test.cpp:89:12:89:13 | ci | 2147483647 |
| test.cpp:91:7:91:13 | aliased | 2147483647 |
| test.cpp:92:12:92:18 | aliased | 2147483647 |
| test.cpp:94:7:94:11 | alias | 2147483647 |
| test.cpp:95:12:95:16 | alias | 2147483647 |
| test.cpp:97:10:97:10 | i | 65535 |
| test.cpp:97:22:97:22 | i | 32767 |
| test.cpp:98:5:98:5 | i | 32767 |

View File

@@ -4,6 +4,22 @@ import java
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.security.QueryInjection
/** A sink for MongoDB injection vulnerabilities. */
class MongoDbInjectionSink extends QueryInjectionSink {
MongoDbInjectionSink() {
exists(MethodAccess call |
call.getMethod().getDeclaringType().hasQualifiedName("com.mongodb", "BasicDBObject") and
call.getMethod().hasName("parse") and
this.asExpr() = call.getArgument(0)
)
or
exists(CastExpr c |
c.getExpr() = this.asExpr() and
c.getTypeExpr().getType().(RefType).hasQualifiedName("com.mongodb", "DBObject")
)
}
}
private class QueryInjectionFlowConfig extends TaintTracking::Configuration {
QueryInjectionFlowConfig() { this = "SqlInjectionLib::QueryInjectionFlowConfig" }
@@ -16,6 +32,10 @@ private class QueryInjectionFlowConfig extends TaintTracking::Configuration {
node.getType() instanceof BoxedType or
node.getType() instanceof NumberType
}
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
mongoJsonStep(node1, node2)
}
}
/**
@@ -27,3 +47,12 @@ predicate queryTaintedBy(
) {
exists(QueryInjectionFlowConfig conf | conf.hasFlowPath(source, sink) and sink.getNode() = query)
}
predicate mongoJsonStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(MethodAccess ma |
ma.getMethod().getDeclaringType().hasQualifiedName("com.mongodb.util", "JSON") and
ma.getMethod().hasName("parse") and
ma.getArgument(0) = node1.asExpr() and
ma = node2.asExpr()
)
}

View File

@@ -25,6 +25,10 @@ class LocalUserInputToQueryInjectionFlowConfig extends TaintTracking::Configurat
override predicate isSanitizer(DataFlow::Node node) {
node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType
}
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
mongoJsonStep(node1, node2)
}
}
from

View File

@@ -0,0 +1,18 @@
import javax.xml.XMLConstants;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
public void transform(Socket socket, String inputXml) throws Exception {
StreamSource xslt = new StreamSource(socket.getInputStream());
StreamSource xml = new StreamSource(new StringReader(inputXml));
StringWriter result = new StringWriter();
TransformerFactory factory = TransformerFactory.newInstance();
// BAD: User provided XSLT stylesheet is processed
factory.newTransformer(xslt).transform(xml, new StreamResult(result));
// GOOD: The secure processing mode is enabled
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
factory.newTransformer(xslt).transform(xml, new StreamResult(result));
}

View File

@@ -0,0 +1,32 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>XSLT (Extensible Stylesheet Language Transformations) is a language for transforming XML
documents into other XML documents or other formats. Processing of unvalidated XSLT stylesheet can
let attacker to read arbitrary files from the filesystem or to execute arbitrary code.</p>
</overview>
<recommendation>
<p>The general recommendation is to not process untrusted XSLT stylesheets. If user provided
stylesheets must be processed, enable the secure processing mode.</p>
</recommendation>
<example>
<p>In the following examples, the code accepts an XSLT stylesheet from the user and processes it.
</p>
<p>In the first example, the user provided XSLT stylesheet is parsed and processed.</p>
<p>In the second example, secure processing mode is enabled.</p>
<sample src="XsltInjection.java" />
</example>
<references>
<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/XSLT">XSLT</a>.</li>
<li>Java Tutorial: <a href="https://docs.oracle.com/javase/tutorial/jaxp/xslt/transformingXML.html">Transforming XML Data with XSLT</a>.</li>
<li><a href="https://blog.hunniccyber.com/ektron-cms-remote-code-execution-xslt-transform-injection-java/">XSLT Injection Basics</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,21 @@
/**
* @name XSLT transformation with user-controlled stylesheet
* @description Doing an XSLT transformation with user-controlled stylesheet can lead to
* information disclosure or execution of arbitrary code.
* @kind path-problem
* @problem.severity error
* @precision high
* @id java/xslt-injection
* @tags security
* external/cwe/cwe-074
*/
import java
import semmle.code.java.dataflow.FlowSources
import XsltInjectionLib
import DataFlow::PathGraph
from DataFlow::PathNode source, DataFlow::PathNode sink, XsltInjectionFlowConfig conf
where conf.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "XSLT transformation might include stylesheet from $@.",
source.getNode(), "this user input"

View File

@@ -0,0 +1,288 @@
import java
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.security.XmlParsers
import DataFlow
/**
* A taint-tracking configuration for unvalidated user input that is used in XSLT transformation.
*/
class XsltInjectionFlowConfig extends TaintTracking::Configuration {
XsltInjectionFlowConfig() { this = "XsltInjectionFlowConfig" }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) { sink instanceof XsltInjectionSink }
override predicate isSanitizer(DataFlow::Node node) {
node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType
}
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
xmlStreamReaderStep(node1, node2) or
xmlEventReaderStep(node1, node2) or
staxSourceStep(node1, node2) or
documentBuilderStep(node1, node2) or
domSourceStep(node1, node2) or
newTransformerOrTemplatesStep(node1, node2) or
newTransformerFromTemplatesStep(node1, node2) or
xsltCompilerStep(node1, node2) or
xsltExecutableStep(node1, node2) or
xsltPackageStep(node1, node2)
}
}
/** The class `javax.xml.transform.stax.StAXSource`. */
class TypeStAXSource extends Class {
TypeStAXSource() { this.hasQualifiedName("javax.xml.transform.stax", "StAXSource") }
}
/** The class `javax.xml.transform.dom.DOMSource`. */
class TypeDOMSource extends Class {
TypeDOMSource() { this.hasQualifiedName("javax.xml.transform.dom", "DOMSource") }
}
/** The interface `javax.xml.transform.Templates`. */
class TypeTemplates extends Interface {
TypeTemplates() { this.hasQualifiedName("javax.xml.transform", "Templates") }
}
/** The method `net.sf.saxon.s9api.XsltTransformer.transform`. */
class XsltTransformerTransformMethod extends Method {
XsltTransformerTransformMethod() {
this.getDeclaringType().hasQualifiedName("net.sf.saxon.s9api", "XsltTransformer") and
this.hasName("transform")
}
}
/** The method `net.sf.saxon.s9api.Xslt30Transformer.transform`. */
class Xslt30TransformerTransformMethod extends Method {
Xslt30TransformerTransformMethod() {
this.getDeclaringType().hasQualifiedName("net.sf.saxon.s9api", "Xslt30Transformer") and
this.hasName("transform")
}
}
/** The method `net.sf.saxon.s9api.Xslt30Transformer.applyTemplates`. */
class Xslt30TransformerApplyTemplatesMethod extends Method {
Xslt30TransformerApplyTemplatesMethod() {
this.getDeclaringType().hasQualifiedName("net.sf.saxon.s9api", "Xslt30Transformer") and
this.hasName("applyTemplates")
}
}
/** The method `net.sf.saxon.s9api.Xslt30Transformer.callFunction`. */
class Xslt30TransformerCallFunctionMethod extends Method {
Xslt30TransformerCallFunctionMethod() {
this.getDeclaringType().hasQualifiedName("net.sf.saxon.s9api", "Xslt30Transformer") and
this.hasName("callFunction")
}
}
/** The method `net.sf.saxon.s9api.Xslt30Transformer.callTemplate`. */
class Xslt30TransformerCallTemplateMethod extends Method {
Xslt30TransformerCallTemplateMethod() {
this.getDeclaringType().hasQualifiedName("net.sf.saxon.s9api", "Xslt30Transformer") and
this.hasName("callTemplate")
}
}
/** The class `net.sf.saxon.s9api.XsltCompiler`. */
class TypeXsltCompiler extends Class {
TypeXsltCompiler() { this.hasQualifiedName("net.sf.saxon.s9api", "XsltCompiler") }
}
/** The class `net.sf.saxon.s9api.XsltExecutable`. */
class TypeXsltExecutable extends Class {
TypeXsltExecutable() { this.hasQualifiedName("net.sf.saxon.s9api", "XsltExecutable") }
}
/** The class `net.sf.saxon.s9api.XsltPackage`. */
class TypeXsltPackage extends Class {
TypeXsltPackage() { this.hasQualifiedName("net.sf.saxon.s9api", "XsltPackage") }
}
/** A data flow sink for unvalidated user input that is used in XSLT transformation. */
class XsltInjectionSink extends DataFlow::ExprNode {
XsltInjectionSink() {
exists(MethodAccess ma, Method m | m = ma.getMethod() and ma.getQualifier() = this.getExpr() |
ma instanceof TransformerTransform or
m instanceof XsltTransformerTransformMethod or
m instanceof Xslt30TransformerTransformMethod or
m instanceof Xslt30TransformerApplyTemplatesMethod or
m instanceof Xslt30TransformerCallFunctionMethod or
m instanceof Xslt30TransformerCallTemplateMethod
)
}
}
/**
* Holds if `n1` to `n2` is a dataflow step that converts between `InputStream` or `Reader` and
* `XMLStreamReader`, i.e. `XMLInputFactory.createXMLStreamReader(tainted)`.
*/
predicate xmlStreamReaderStep(ExprNode n1, ExprNode n2) {
exists(XmlInputFactoryStreamReader xmlStreamReader |
n1.asExpr() = xmlStreamReader.getSink() and
n2.asExpr() = xmlStreamReader
)
}
/**
* Holds if `n1` to `n2` is a dataflow step that converts between `InputStream` or `Reader` and
* `XMLEventReader`, i.e. `XMLInputFactory.createXMLEventReader(tainted)`.
*/
predicate xmlEventReaderStep(ExprNode n1, ExprNode n2) {
exists(XmlInputFactoryEventReader xmlEventReader |
n1.asExpr() = xmlEventReader.getSink() and
n2.asExpr() = xmlEventReader
)
}
/**
* Holds if `n1` to `n2` is a dataflow step that converts between `XMLStreamReader` or
* `XMLEventReader` and `StAXSource`, i.e. `new StAXSource(tainted)`.
*/
predicate staxSourceStep(ExprNode n1, ExprNode n2) {
exists(ConstructorCall cc | cc.getConstructedType() instanceof TypeStAXSource |
n1.asExpr() = cc.getAnArgument() and
n2.asExpr() = cc
)
}
/**
* Holds if `n1` to `n2` is a dataflow step that converts between `InputStream` and `Document`,
* i.e. `DocumentBuilder.parse(tainted)`.
*/
predicate documentBuilderStep(ExprNode n1, ExprNode n2) {
exists(DocumentBuilderParse documentBuilder |
n1.asExpr() = documentBuilder.getSink() and
n2.asExpr() = documentBuilder
)
}
/**
* Holds if `n1` to `n2` is a dataflow step that converts between `Document` and `DOMSource`, i.e.
* `new DOMSource(tainted)`.
*/
predicate domSourceStep(ExprNode n1, ExprNode n2) {
exists(ConstructorCall cc | cc.getConstructedType() instanceof TypeDOMSource |
n1.asExpr() = cc.getAnArgument() and
n2.asExpr() = cc
)
}
/**
* A data flow configuration for secure processing feature that is enabled on `TransformerFactory`.
*/
private class TransformerFactoryWithSecureProcessingFeatureFlowConfig extends DataFlow2::Configuration {
TransformerFactoryWithSecureProcessingFeatureFlowConfig() {
this = "TransformerFactoryWithSecureProcessingFeatureFlowConfig"
}
override predicate isSource(DataFlow::Node src) {
exists(Variable v | v = src.asExpr().(VarAccess).getVariable() |
exists(TransformerFactoryFeatureConfig config | config.getQualifier() = v.getAnAccess() |
config.enables(configSecureProcessing())
)
)
}
override predicate isSink(DataFlow::Node sink) {
exists(MethodAccess ma |
sink.asExpr() = ma.getQualifier() and
ma.getMethod().getDeclaringType() instanceof TransformerFactory
)
}
override int fieldFlowBranchLimit() { result = 0 }
}
/** A `ParserConfig` specific to `TransformerFactory`. */
private class TransformerFactoryFeatureConfig extends ParserConfig {
TransformerFactoryFeatureConfig() {
exists(Method m |
m = this.getMethod() and
m.getDeclaringType() instanceof TransformerFactory and
m.hasName("setFeature")
)
}
}
/**
* Holds if `n1` to `n2` is a dataflow step that converts between `Source` and `Transformer` or
* `Templates`, i.e. `TransformerFactory.newTransformer(tainted)` or
* `TransformerFactory.newTemplates(tainted)`.
*/
predicate newTransformerOrTemplatesStep(ExprNode n1, ExprNode n2) {
exists(MethodAccess ma, Method m | ma.getMethod() = m |
n1.asExpr() = ma.getAnArgument() and
n2.asExpr() = ma and
(
m.getDeclaringType() instanceof TransformerFactory and m.hasName("newTransformer")
or
m.getDeclaringType() instanceof TransformerFactory and m.hasName("newTemplates")
) and
not exists(TransformerFactoryWithSecureProcessingFeatureFlowConfig conf |
conf.hasFlowToExpr(ma.getQualifier())
)
)
}
/**
* Holds if `n1` to `n2` is a dataflow step that converts between `Templates` and `Transformer`,
* i.e. `tainted.newTransformer()`.
*/
predicate newTransformerFromTemplatesStep(ExprNode n1, ExprNode n2) {
exists(MethodAccess ma, Method m | ma.getMethod() = m |
n1.asExpr() = ma.getQualifier() and
n2.asExpr() = ma and
m.getDeclaringType() instanceof TypeTemplates and
m.hasName("newTransformer")
)
}
/**
* Holds if `n1` to `n2` is a dataflow step that converts between `Source` or `URI` and
* `XsltExecutable` or `XsltPackage`, i.e. `XsltCompiler.compile(tainted)` or
* `XsltCompiler.loadExecutablePackage(tainted)` or `XsltCompiler.compilePackage(tainted)` or
* `XsltCompiler.loadLibraryPackage(tainted)`.
*/
predicate xsltCompilerStep(ExprNode n1, ExprNode n2) {
exists(MethodAccess ma, Method m | ma.getMethod() = m |
n1.asExpr() = ma.getArgument(0) and
n2.asExpr() = ma and
m.getDeclaringType() instanceof TypeXsltCompiler and
(
m.hasName("compile") or
m.hasName("loadExecutablePackage") or
m.hasName("compilePackage") or
m.hasName("loadLibraryPackage")
)
)
}
/**
* Holds if `n1` to `n2` is a dataflow step that converts between `XsltExecutable` and
* `XsltTransformer` or `Xslt30Transformer`, i.e. `XsltExecutable.load()` or
* `XsltExecutable.load30()`.
*/
predicate xsltExecutableStep(ExprNode n1, ExprNode n2) {
exists(MethodAccess ma, Method m | ma.getMethod() = m |
n1.asExpr() = ma.getQualifier() and
n2.asExpr() = ma and
m.getDeclaringType() instanceof TypeXsltExecutable and
(m.hasName("load") or m.hasName("load30"))
)
}
/**
* Holds if `n1` to `n2` is a dataflow step that converts between `XsltPackage` and
* `XsltExecutable`, i.e. `XsltPackage.link()`.
*/
predicate xsltPackageStep(ExprNode n1, ExprNode n2) {
exists(MethodAccess ma, Method m | ma.getMethod() = m |
n1.asExpr() = ma.getQualifier() and
n2.asExpr() = ma and
m.getDeclaringType() instanceof TypeXsltPackage and
m.hasName("link")
)
}

View File

@@ -1,5 +1,7 @@
private import java
private import DataFlowPrivate
private import DataFlowUtil
private import semmle.code.java.dataflow.InstanceAccess
import semmle.code.java.dispatch.VirtualDispatch
private module DispatchImpl {
@@ -17,6 +19,13 @@ private module DispatchImpl {
not p.isVarargs() and
c = ma.getEnclosingCallable()
)
or
exists(OwnInstanceAccess ia |
2 <= strictcount(viableImpl(ma)) and
(ia.isExplicit(ma.getQualifier()) or ia.isImplicitMethodQualifier(ma)) and
i = -1 and
c = ma.getEnclosingCallable()
)
}
/**
@@ -37,18 +46,32 @@ private module DispatchImpl {
* relevant call context.
*/
private predicate contextArgHasType(Call ctx, int i, RefType t, boolean exact) {
exists(Expr arg, Expr src |
relevantContext(ctx, i) and
ctx.getArgument(i) = arg and
src = variableTrack(arg) and
exists(RefType srctype | srctype = src.getType() |
exists(TypeVariable v | v = srctype |
t = v.getUpperBoundType+() and not t instanceof TypeVariable
)
relevantContext(ctx, i) and
exists(RefType srctype |
exists(Expr arg, Expr src |
i = -1 and
ctx.getQualifier() = arg
or
t = srctype and not srctype instanceof TypeVariable
) and
if src instanceof ClassInstanceExpr then exact = true else exact = false
ctx.getArgument(i) = arg
|
src = variableTrack(arg) and
srctype = src.getType() and
if src instanceof ClassInstanceExpr then exact = true else exact = false
)
or
exists(Node arg |
i = -1 and
not exists(ctx.getQualifier()) and
getInstanceArgument(ctx) = arg and
arg.getTypeBound() = srctype and
if ctx instanceof ClassInstanceExpr then exact = true else exact = false
)
|
exists(TypeVariable v | v = srctype |
t = v.getUpperBoundType+() and not t instanceof TypeVariable
)
or
t = srctype and not srctype instanceof TypeVariable
)
}

View File

@@ -193,6 +193,18 @@ predicate readStep(Node node1, Content f, Node node2) {
fr.getField() = f.(FieldContent).getField() and
fr = node2.asExpr()
)
or
exists(Record r, Method getter, Field recf, MethodAccess get |
getter.getDeclaringType() = r and
recf.getDeclaringType() = r and
getter.getNumberOfParameters() = 0 and
getter.getName() = recf.getName() and
not exists(getter.getBody()) and
recf = f.(FieldContent).getField() and
get.getMethod() = getter and
node1.asExpr() = get.getQualifier() and
node2.asExpr() = get
)
}
/**

View File

@@ -1172,3 +1172,15 @@ class SimpleXMLFormatterCall extends XmlParserCall {
override predicate isSafe() { none() }
}
/** A configuration for secure processing. */
Expr configSecureProcessing() {
result.(ConstantStringExpr).getStringValue() =
"http://javax.xml.XMLConstants/feature/secure-processing"
or
exists(Field f |
result = f.getAnAccess() and
f.hasName("FEATURE_SECURE_PROCESSING") and
f.getDeclaringType() instanceof XmlConstants
)
}

View File

@@ -0,0 +1,85 @@
edges
| XsltInjection.java:30:44:30:66 | getInputStream(...) : InputStream | XsltInjection.java:31:5:31:59 | newTransformer(...) |
| XsltInjection.java:35:66:35:88 | getInputStream(...) : InputStream | XsltInjection.java:36:5:36:74 | newTransformer(...) |
| XsltInjection.java:40:45:40:70 | param : String | XsltInjection.java:43:5:43:59 | newTransformer(...) |
| XsltInjection.java:47:54:47:76 | getInputStream(...) : InputStream | XsltInjection.java:48:5:48:74 | newTransformer(...) |
| XsltInjection.java:52:82:52:104 | getInputStream(...) : InputStream | XsltInjection.java:53:5:53:59 | newTransformer(...) |
| XsltInjection.java:57:91:57:113 | getInputStream(...) : InputStream | XsltInjection.java:58:5:58:59 | newTransformer(...) |
| XsltInjection.java:62:120:62:142 | getInputStream(...) : InputStream | XsltInjection.java:63:5:63:74 | newTransformer(...) |
| XsltInjection.java:67:102:67:124 | getInputStream(...) : InputStream | XsltInjection.java:68:5:68:59 | newTransformer(...) |
| XsltInjection.java:72:44:72:66 | getInputStream(...) : InputStream | XsltInjection.java:76:5:76:34 | newTransformer(...) |
| XsltInjection.java:80:44:80:66 | getInputStream(...) : InputStream | XsltInjection.java:83:5:83:34 | newTransformer(...) |
| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:90:5:90:35 | load(...) |
| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:91:5:91:37 | load30(...) |
| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:92:5:92:37 | load30(...) |
| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:93:5:93:37 | load30(...) |
| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:94:5:94:37 | load30(...) |
| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:95:5:95:37 | load30(...) |
| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:96:5:96:37 | load30(...) |
| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:97:5:97:37 | load30(...) |
| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:98:5:98:37 | load30(...) |
| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:99:5:99:37 | load30(...) |
| XsltInjection.java:103:36:103:61 | param : String | XsltInjection.java:108:5:108:46 | load(...) |
| XsltInjection.java:103:36:103:61 | param : String | XsltInjection.java:110:5:110:50 | load(...) |
| XsltInjection.java:105:44:105:66 | getInputStream(...) : InputStream | XsltInjection.java:109:5:109:49 | load(...) |
nodes
| XsltInjection.java:30:44:30:66 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
| XsltInjection.java:31:5:31:59 | newTransformer(...) | semmle.label | newTransformer(...) |
| XsltInjection.java:35:66:35:88 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
| XsltInjection.java:36:5:36:74 | newTransformer(...) | semmle.label | newTransformer(...) |
| XsltInjection.java:40:45:40:70 | param : String | semmle.label | param : String |
| XsltInjection.java:43:5:43:59 | newTransformer(...) | semmle.label | newTransformer(...) |
| XsltInjection.java:47:54:47:76 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
| XsltInjection.java:48:5:48:74 | newTransformer(...) | semmle.label | newTransformer(...) |
| XsltInjection.java:52:82:52:104 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
| XsltInjection.java:53:5:53:59 | newTransformer(...) | semmle.label | newTransformer(...) |
| XsltInjection.java:57:91:57:113 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
| XsltInjection.java:58:5:58:59 | newTransformer(...) | semmle.label | newTransformer(...) |
| XsltInjection.java:62:120:62:142 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
| XsltInjection.java:63:5:63:74 | newTransformer(...) | semmle.label | newTransformer(...) |
| XsltInjection.java:67:102:67:124 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
| XsltInjection.java:68:5:68:59 | newTransformer(...) | semmle.label | newTransformer(...) |
| XsltInjection.java:72:44:72:66 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
| XsltInjection.java:76:5:76:34 | newTransformer(...) | semmle.label | newTransformer(...) |
| XsltInjection.java:80:44:80:66 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
| XsltInjection.java:83:5:83:34 | newTransformer(...) | semmle.label | newTransformer(...) |
| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
| XsltInjection.java:90:5:90:35 | load(...) | semmle.label | load(...) |
| XsltInjection.java:91:5:91:37 | load30(...) | semmle.label | load30(...) |
| XsltInjection.java:92:5:92:37 | load30(...) | semmle.label | load30(...) |
| XsltInjection.java:93:5:93:37 | load30(...) | semmle.label | load30(...) |
| XsltInjection.java:94:5:94:37 | load30(...) | semmle.label | load30(...) |
| XsltInjection.java:95:5:95:37 | load30(...) | semmle.label | load30(...) |
| XsltInjection.java:96:5:96:37 | load30(...) | semmle.label | load30(...) |
| XsltInjection.java:97:5:97:37 | load30(...) | semmle.label | load30(...) |
| XsltInjection.java:98:5:98:37 | load30(...) | semmle.label | load30(...) |
| XsltInjection.java:99:5:99:37 | load30(...) | semmle.label | load30(...) |
| XsltInjection.java:103:36:103:61 | param : String | semmle.label | param : String |
| XsltInjection.java:105:44:105:66 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
| XsltInjection.java:108:5:108:46 | load(...) | semmle.label | load(...) |
| XsltInjection.java:109:5:109:49 | load(...) | semmle.label | load(...) |
| XsltInjection.java:110:5:110:50 | load(...) | semmle.label | load(...) |
#select
| XsltInjection.java:31:5:31:59 | newTransformer(...) | XsltInjection.java:30:44:30:66 | getInputStream(...) : InputStream | XsltInjection.java:31:5:31:59 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:30:44:30:66 | getInputStream(...) | this user input |
| XsltInjection.java:36:5:36:74 | newTransformer(...) | XsltInjection.java:35:66:35:88 | getInputStream(...) : InputStream | XsltInjection.java:36:5:36:74 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:35:66:35:88 | getInputStream(...) | this user input |
| XsltInjection.java:43:5:43:59 | newTransformer(...) | XsltInjection.java:40:45:40:70 | param : String | XsltInjection.java:43:5:43:59 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:40:45:40:70 | param | this user input |
| XsltInjection.java:48:5:48:74 | newTransformer(...) | XsltInjection.java:47:54:47:76 | getInputStream(...) : InputStream | XsltInjection.java:48:5:48:74 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:47:54:47:76 | getInputStream(...) | this user input |
| XsltInjection.java:53:5:53:59 | newTransformer(...) | XsltInjection.java:52:82:52:104 | getInputStream(...) : InputStream | XsltInjection.java:53:5:53:59 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:52:82:52:104 | getInputStream(...) | this user input |
| XsltInjection.java:58:5:58:59 | newTransformer(...) | XsltInjection.java:57:91:57:113 | getInputStream(...) : InputStream | XsltInjection.java:58:5:58:59 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:57:91:57:113 | getInputStream(...) | this user input |
| XsltInjection.java:63:5:63:74 | newTransformer(...) | XsltInjection.java:62:120:62:142 | getInputStream(...) : InputStream | XsltInjection.java:63:5:63:74 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:62:120:62:142 | getInputStream(...) | this user input |
| XsltInjection.java:68:5:68:59 | newTransformer(...) | XsltInjection.java:67:102:67:124 | getInputStream(...) : InputStream | XsltInjection.java:68:5:68:59 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:67:102:67:124 | getInputStream(...) | this user input |
| XsltInjection.java:76:5:76:34 | newTransformer(...) | XsltInjection.java:72:44:72:66 | getInputStream(...) : InputStream | XsltInjection.java:76:5:76:34 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:72:44:72:66 | getInputStream(...) | this user input |
| XsltInjection.java:83:5:83:34 | newTransformer(...) | XsltInjection.java:80:44:80:66 | getInputStream(...) : InputStream | XsltInjection.java:83:5:83:34 | newTransformer(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:80:44:80:66 | getInputStream(...) | this user input |
| XsltInjection.java:90:5:90:35 | load(...) | XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:90:5:90:35 | load(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:87:44:87:66 | getInputStream(...) | this user input |
| XsltInjection.java:91:5:91:37 | load30(...) | XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:91:5:91:37 | load30(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:87:44:87:66 | getInputStream(...) | this user input |
| XsltInjection.java:92:5:92:37 | load30(...) | XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:92:5:92:37 | load30(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:87:44:87:66 | getInputStream(...) | this user input |
| XsltInjection.java:93:5:93:37 | load30(...) | XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:93:5:93:37 | load30(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:87:44:87:66 | getInputStream(...) | this user input |
| XsltInjection.java:94:5:94:37 | load30(...) | XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:94:5:94:37 | load30(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:87:44:87:66 | getInputStream(...) | this user input |
| XsltInjection.java:95:5:95:37 | load30(...) | XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:95:5:95:37 | load30(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:87:44:87:66 | getInputStream(...) | this user input |
| XsltInjection.java:96:5:96:37 | load30(...) | XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:96:5:96:37 | load30(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:87:44:87:66 | getInputStream(...) | this user input |
| XsltInjection.java:97:5:97:37 | load30(...) | XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:97:5:97:37 | load30(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:87:44:87:66 | getInputStream(...) | this user input |
| XsltInjection.java:98:5:98:37 | load30(...) | XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:98:5:98:37 | load30(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:87:44:87:66 | getInputStream(...) | this user input |
| XsltInjection.java:99:5:99:37 | load30(...) | XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:99:5:99:37 | load30(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:87:44:87:66 | getInputStream(...) | this user input |
| XsltInjection.java:108:5:108:46 | load(...) | XsltInjection.java:103:36:103:61 | param : String | XsltInjection.java:108:5:108:46 | load(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:103:36:103:61 | param | this user input |
| XsltInjection.java:109:5:109:49 | load(...) | XsltInjection.java:105:44:105:66 | getInputStream(...) : InputStream | XsltInjection.java:109:5:109:49 | load(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:105:44:105:66 | getInputStream(...) | this user input |
| XsltInjection.java:110:5:110:50 | load(...) | XsltInjection.java:103:36:103:61 | param : String | XsltInjection.java:110:5:110:50 | load(...) | XSLT transformation might include stylesheet from $@. | XsltInjection.java:103:36:103:61 | param | this user input |

View File

@@ -0,0 +1,127 @@
import java.io.InputStreamReader;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.Socket;
import java.net.URI;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.stream.XMLInputFactory;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stax.StAXSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.xml.sax.InputSource;
import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.XdmValue;
import net.sf.saxon.s9api.XsltCompiler;
@Controller
public class XsltInjection {
public void testStreamSourceInputStream(Socket socket) throws Exception {
StreamSource source = new StreamSource(socket.getInputStream());
TransformerFactory.newInstance().newTransformer(source).transform(null, null);
}
public void testStreamSourceReader(Socket socket) throws Exception {
StreamSource source = new StreamSource(new InputStreamReader(socket.getInputStream()));
TransformerFactory.newInstance().newTemplates(source).newTransformer().transform(null, null);
}
@RequestMapping
public void testStreamSourceInjectedParam(@RequestParam String param) throws Exception {
String xslt = "<xsl:stylesheet [...]" + param + "</xsl:stylesheet>";
StreamSource source = new StreamSource(new StringReader(xslt));
TransformerFactory.newInstance().newTransformer(source).transform(null, null);
}
public void testSAXSourceInputStream(Socket socket) throws Exception {
SAXSource source = new SAXSource(new InputSource(socket.getInputStream()));
TransformerFactory.newInstance().newTemplates(source).newTransformer().transform(null, null);
}
public void testSAXSourceReader(Socket socket) throws Exception {
SAXSource source = new SAXSource(null, new InputSource(new InputStreamReader(socket.getInputStream())));
TransformerFactory.newInstance().newTransformer(source).transform(null, null);
}
public void testStAXSourceEventReader(Socket socket) throws Exception {
StAXSource source = new StAXSource(XMLInputFactory.newInstance().createXMLEventReader(socket.getInputStream()));
TransformerFactory.newInstance().newTransformer(source).transform(null, null);
}
public void testStAXSourceEventStream(Socket socket) throws Exception {
StAXSource source = new StAXSource(XMLInputFactory.newInstance().createXMLStreamReader(null, new InputStreamReader(socket.getInputStream())));
TransformerFactory.newInstance().newTemplates(source).newTransformer().transform(null, null);
}
public void testDOMSource(Socket socket) throws Exception {
DOMSource source = new DOMSource(DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(socket.getInputStream()));
TransformerFactory.newInstance().newTransformer(source).transform(null, null);
}
public void testDisabledXXE(Socket socket) throws Exception {
StreamSource source = new StreamSource(socket.getInputStream());
TransformerFactory factory = TransformerFactory.newInstance();
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
factory.newTransformer(source).transform(null, null);
}
public void testFeatureSecureProcessingDisabled(Socket socket) throws Exception {
StreamSource source = new StreamSource(socket.getInputStream());
TransformerFactory factory = TransformerFactory.newInstance();
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, false);
factory.newTransformer(source).transform(null, null);
}
public void testSaxon(Socket socket) throws Exception {
StreamSource source = new StreamSource(socket.getInputStream());
XsltCompiler compiler = new Processor(true).newXsltCompiler();
compiler.compile(source).load().transform();
compiler.compile(source).load30().transform(null, null);
compiler.compile(source).load30().applyTemplates((Source) null);
compiler.compile(source).load30().applyTemplates((Source) null, null);
compiler.compile(source).load30().applyTemplates((XdmValue) null);
compiler.compile(source).load30().applyTemplates((XdmValue) null, null);
compiler.compile(source).load30().callFunction(null, null);
compiler.compile(source).load30().callFunction(null, null, null);
compiler.compile(source).load30().callTemplate(null);
compiler.compile(source).load30().callTemplate(null, null);
}
@RequestMapping
public void testSaxonXsltPackage(@RequestParam String param, Socket socket) throws Exception {
URI uri = new URI(param);
StreamSource source = new StreamSource(socket.getInputStream());
XsltCompiler compiler = new Processor(true).newXsltCompiler();
compiler.loadExecutablePackage(uri).load().transform();
compiler.compilePackage(source).link().load().transform();
compiler.loadLibraryPackage(uri).link().load().transform();
}
public void testOkFeatureSecureProcessing(Socket socket) throws Exception {
StreamSource source = new StreamSource(socket.getInputStream());
TransformerFactory factory = TransformerFactory.newInstance();
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
factory.newTransformer(source).transform(null, null);
}
public void testOkSaxon(Socket socket) throws Exception {
StreamSource source = new StreamSource(socket.getInputStream());
XsltCompiler compiler = new Processor(true).newXsltCompiler();
compiler.compile(source).load().close();
compiler.compile((Source) new Object()).load().transform();
}
}

View File

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

View File

@@ -0,0 +1 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../stubs/Saxon-HE-9.9.1-7

View File

@@ -0,0 +1,8 @@
package net.sf.saxon;
import net.sf.saxon.lib.*;
import net.sf.saxon.om.*;
public class Configuration implements SourceResolver, NotationSet {
public interface ApiProvider {}
}

View File

@@ -0,0 +1,3 @@
package net.sf.saxon.lib;
public interface SourceResolver { }

View File

@@ -0,0 +1,3 @@
package net.sf.saxon.om;
public interface NotationSet { }

View File

@@ -0,0 +1,3 @@
package net.sf.saxon.s9api;
abstract class AbstractXsltTransformer { }

View File

@@ -0,0 +1,3 @@
package net.sf.saxon.s9api;
public interface Destination { }

View File

@@ -0,0 +1,9 @@
package net.sf.saxon.s9api;
import net.sf.saxon.Configuration;
public class Processor implements Configuration.ApiProvider {
public Processor(boolean licensedEdition) {}
public XsltCompiler newXsltCompiler() { return null; }
}

View File

@@ -0,0 +1,3 @@
package net.sf.saxon.s9api;
public class QName { }

View File

@@ -0,0 +1,3 @@
package net.sf.saxon.s9api;
public class SaxonApiException extends Exception { }

View File

@@ -0,0 +1,3 @@
package net.sf.saxon.s9api;
public class SaxonApiUncheckedException extends RuntimeException {}

View File

@@ -0,0 +1,3 @@
package net.sf.saxon.s9api;
public abstract class XdmItem extends XdmValue { }

View File

@@ -0,0 +1,8 @@
package net.sf.saxon.s9api;
import java.lang.Iterable;
import java.util.Iterator;
public class XdmValue implements Iterable<XdmItem> {
public Iterator<XdmItem> iterator() throws SaxonApiUncheckedException { return null; }
}

View File

@@ -0,0 +1,15 @@
package net.sf.saxon.s9api;
import javax.xml.transform.Source;
public class Xslt30Transformer extends AbstractXsltTransformer {
public void transform(Source source, Destination destination) throws SaxonApiException {}
public void applyTemplates(Source source, Destination destination) throws SaxonApiException {}
public XdmValue applyTemplates(Source source) throws SaxonApiException { return null; }
public void applyTemplates(XdmValue selection, Destination destination) throws SaxonApiException {}
public XdmValue applyTemplates(XdmValue selection) throws SaxonApiException { return null; }
public XdmValue callFunction(QName function, XdmValue[] arguments) throws SaxonApiException { return null; }
public void callFunction(QName function, XdmValue[] arguments, Destination destination) throws SaxonApiException {}
public XdmValue callTemplate(QName templateName) throws SaxonApiException { return null; }
public void callTemplate(QName templateName, Destination destination) throws SaxonApiException {}
}

View File

@@ -0,0 +1,11 @@
package net.sf.saxon.s9api;
import javax.xml.transform.Source;
import java.net.URI;
public class XsltCompiler {
public XsltExecutable compile(Source source) throws SaxonApiException { return null; }
public XsltExecutable loadExecutablePackage(URI location) throws SaxonApiException { return null; }
public XsltPackage compilePackage(Source source) throws SaxonApiException { return null; }
public XsltPackage loadLibraryPackage(URI location) throws SaxonApiException { return null; }
}

View File

@@ -0,0 +1,6 @@
package net.sf.saxon.s9api;
public class XsltExecutable {
public XsltTransformer load() { return null; }
public Xslt30Transformer load30() { return null; }
}

View File

@@ -0,0 +1,5 @@
package net.sf.saxon.s9api;
public class XsltPackage {
public XsltExecutable link() throws SaxonApiException { return null; }
}

View File

@@ -0,0 +1,6 @@
package net.sf.saxon.s9api;
public class XsltTransformer extends AbstractXsltTransformer implements Destination {
public void transform() throws SaxonApiException {}
public void close() {}
}

View File

@@ -0,0 +1,63 @@
public class A {
static void sink(Object x) { }
static Object source() { return null; }
static class C1 {
C1() { }
C1(Object x) {
foo(x);
}
void wrapFoo1(Object x) {
foo(x);
}
void wrapFoo2(Object x) {
this.foo(x);
}
void foo(Object x) {
Object c1 = x;
sink(c1);
}
}
static class C2 extends C1 {
C2() { }
C2(Object x) {
super(x);
}
void foo(Object x) {
Object c2 = x;
sink(c2);
}
void callWrapFoo2() {
wrapFoo2(source());
}
}
static void wrapFoo3(C1 c1, Object x) {
c1.foo(x);
}
void test(C1 c) {
c.wrapFoo1(source());
c.wrapFoo2(source());
wrapFoo3(c, source());
new C1(source());
new C1().wrapFoo1(source());
new C1().wrapFoo2(source());
wrapFoo3(new C1(), source());
new C2(source());
new C2().wrapFoo1(source());
new C2().wrapFoo2(source());
wrapFoo3(new C2(), source());
}
}

View File

@@ -0,0 +1,15 @@
| A.java:40:16:40:23 | source(...) | A.java:36:12:36:13 | c2 |
| A.java:49:16:49:23 | source(...) | A.java:23:12:23:13 | c1 |
| A.java:49:16:49:23 | source(...) | A.java:36:12:36:13 | c2 |
| A.java:50:16:50:23 | source(...) | A.java:23:12:23:13 | c1 |
| A.java:50:16:50:23 | source(...) | A.java:36:12:36:13 | c2 |
| A.java:51:17:51:24 | source(...) | A.java:23:12:23:13 | c1 |
| A.java:51:17:51:24 | source(...) | A.java:36:12:36:13 | c2 |
| A.java:53:12:53:19 | source(...) | A.java:23:12:23:13 | c1 |
| A.java:54:23:54:30 | source(...) | A.java:23:12:23:13 | c1 |
| A.java:55:23:55:30 | source(...) | A.java:23:12:23:13 | c1 |
| A.java:56:24:56:31 | source(...) | A.java:23:12:23:13 | c1 |
| A.java:58:12:58:19 | source(...) | A.java:36:12:36:13 | c2 |
| A.java:59:23:59:30 | source(...) | A.java:36:12:36:13 | c2 |
| A.java:60:23:60:30 | source(...) | A.java:36:12:36:13 | c2 |
| A.java:61:24:61:31 | source(...) | A.java:36:12:36:13 | c2 |

View File

@@ -0,0 +1,15 @@
import java
import semmle.code.java.dataflow.DataFlow
import DataFlow
class Conf extends Configuration {
Conf() { this = "qqconf" }
override predicate isSource(Node n) { n.asExpr().(MethodAccess).getMethod().hasName("source") }
override predicate isSink(Node n) { n.asExpr().(Argument).getCall().getCallee().hasName("sink") }
}
from Node src, Node sink, Conf c
where c.hasFlow(src, sink)
select src, sink

View File

@@ -0,0 +1,65 @@
public class A {
record Pair(Object x, Object y) { }
static Object source() { return null; }
void sink(Object o) { }
void foo() {
Pair p1 = new Pair(source(), null);
Pair p2 = new Pair(new Object(), source());
bar(p1, p2);
}
void bar(Pair p1, Pair p2) {
sink(p1.x);
sink(p1.y);
sink(p2.x);
sink(p2.y);
Object p1x = p1.x();
Object p1y = p1.y();
Object p2x = p2.x();
Object p2y = p2.y();
sink(p1x);
sink(p1y);
sink(p2x);
sink(p2y);
}
record RecWithGetter(Object f) {
public Object f() {
return this.f;
}
}
record RecWithWeirdGetter1(Object f) {
public Object f() {
return new Object();
}
}
record RecWithWeirdGetter2(Object f) {
public Object f() {
return source();
}
}
void testExplicitGetter1() {
RecWithGetter r1 = new RecWithGetter(source());
RecWithWeirdGetter1 r2 = new RecWithWeirdGetter1(source());
RecWithWeirdGetter2 r3 = new RecWithWeirdGetter2(source());
testExplicitGetter2(r1, r2, r3);
}
void testExplicitGetter2(RecWithGetter r1, RecWithWeirdGetter1 r2, RecWithWeirdGetter2 r3) {
sink(r1.f);
sink(r2.f);
sink(r3.f);
Object r1f = r1.f();
Object r2f = r2.f();
Object r3f = r3.f();
sink(r1f);
sink(r2f);
sink(r3f);
}
}

View File

@@ -0,0 +1 @@
//semmle-extractor-options: --javac-args --enable-preview -source 14 -target 14

View File

@@ -0,0 +1,9 @@
| A.java:9:24:9:31 | source(...) | A.java:15:10:15:13 | p1.x |
| A.java:9:24:9:31 | source(...) | A.java:23:10:23:12 | p1x |
| A.java:10:38:10:45 | source(...) | A.java:18:10:18:13 | p2.y |
| A.java:10:38:10:45 | source(...) | A.java:26:10:26:12 | p2y |
| A.java:43:14:43:21 | source(...) | A.java:63:10:63:12 | r3f |
| A.java:48:42:48:49 | source(...) | A.java:55:10:55:13 | r1.f |
| A.java:48:42:48:49 | source(...) | A.java:61:10:61:12 | r1f |
| A.java:49:54:49:61 | source(...) | A.java:56:10:56:13 | r2.f |
| A.java:50:54:50:61 | source(...) | A.java:57:10:57:13 | r3.f |

View File

@@ -0,0 +1,15 @@
import java
import semmle.code.java.dataflow.DataFlow
import DataFlow
class Conf extends Configuration {
Conf() { this = "qqconf" }
override predicate isSource(Node n) { n.asExpr().(MethodAccess).getMethod().hasName("source") }
override predicate isSink(Node n) { n.asExpr().(Argument).getCall().getCallee().hasName("sink") }
}
from Conf conf, Node src, Node sink
where conf.hasFlow(src, sink)
select src, sink

View File

@@ -0,0 +1,25 @@
import com.mongodb.MongoClient;
import com.mongodb.DBObject;
import com.mongodb.util.*;
import com.mongodb.ServerAddress;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.*;
public class Mongo {
public static void main(String[] args) {
MongoClient mongoClient = new MongoClient(new ServerAddress("localhost", 27017));
DB db = mongoClient.getDB("mydb");
DBCollection collection = db.getCollection("test");
String name = args[1];
String stringQuery = "{ 'name' : '" + name + "'}";
DBObject databaseQuery = (DBObject) JSON.parse(stringQuery);
DBCursor result = collection.find(databaseQuery);
String json = args[1];
BasicDBObject bdb = BasicDBObject.parse(json);
DBCursor result2 = collection.find(bdb);
}
}

View File

@@ -1,4 +1,6 @@
edges
| Mongo.java:10:29:10:41 | args : String[] | Mongo.java:17:45:17:67 | parse(...) |
| Mongo.java:10:29:10:41 | args : String[] | Mongo.java:21:49:21:52 | json |
| Test.java:29:30:29:42 | args : String[] | Test.java:36:47:36:52 | query1 |
| Test.java:29:30:29:42 | args : String[] | Test.java:42:57:42:62 | query2 |
| Test.java:29:30:29:42 | args : String[] | Test.java:50:62:50:67 | query3 |
@@ -11,6 +13,9 @@ edges
| Test.java:214:11:214:14 | args : String[] | Test.java:29:30:29:42 | args : String[] |
| Test.java:218:14:218:17 | args : String[] | Test.java:183:33:183:45 | args : String[] |
nodes
| Mongo.java:10:29:10:41 | args : String[] | semmle.label | args : String[] |
| Mongo.java:17:45:17:67 | parse(...) | semmle.label | parse(...) |
| Mongo.java:21:49:21:52 | json | semmle.label | json |
| Test.java:29:30:29:42 | args : String[] | semmle.label | args : String[] |
| Test.java:36:47:36:52 | query1 | semmle.label | query1 |
| Test.java:42:57:42:62 | query2 | semmle.label | query2 |
@@ -24,6 +29,8 @@ nodes
| Test.java:214:11:214:14 | args : String[] | semmle.label | args : String[] |
| Test.java:218:14:218:17 | args : String[] | semmle.label | args : String[] |
#select
| Mongo.java:17:45:17:67 | parse(...) | Mongo.java:10:29:10:41 | args : String[] | Mongo.java:17:45:17:67 | parse(...) | Query might include code from $@. | Mongo.java:10:29:10:41 | args | this user input |
| Mongo.java:21:49:21:52 | json | Mongo.java:10:29:10:41 | args : String[] | Mongo.java:21:49:21:52 | json | Query might include code from $@. | Mongo.java:10:29:10:41 | args | this user input |
| Test.java:36:47:36:52 | query1 | Test.java:213:26:213:38 | args : String[] | Test.java:36:47:36:52 | query1 | Query might include code from $@. | Test.java:213:26:213:38 | args | this user input |
| Test.java:42:57:42:62 | query2 | Test.java:213:26:213:38 | args : String[] | Test.java:42:57:42:62 | query2 | Query might include code from $@. | Test.java:213:26:213:38 | args | this user input |
| Test.java:50:62:50:67 | query3 | Test.java:213:26:213:38 | args : String[] | Test.java:50:62:50:67 | query3 | Query might include code from $@. | Test.java:213:26:213:38 | args | this user input |

View File

@@ -5,5 +5,6 @@ where
controlledString(controlled) and
method = controlled.getEnclosingCallable() and
line = controlled.getLocation().getStartLine() - method.getLocation().getStartLine() and
controlled.getCompilationUnit().fromSource()
controlled.getCompilationUnit().fromSource() and
controlled.getFile().getStem() = ["Test", "Validation"]
select method.getName(), line, controlled

View File

@@ -1,6 +1,9 @@
import semmle.code.java.security.ControlledString
from Expr precedes, Method method
where endsInQuote(precedes) and precedes.getEnclosingCallable() = method
where
endsInQuote(precedes) and
precedes.getEnclosingCallable() = method and
precedes.getFile().getStem() = "Test"
select method.getName(),
precedes.getLocation().getStartLine() - method.getLocation().getStartLine(), precedes

View File

@@ -0,0 +1 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/mongodbClient

View File

@@ -9,5 +9,8 @@ class Conf extends TaintTracking::Configuration {
}
from Conf conf, Expr tainted, Method method
where conf.hasFlowToExpr(tainted) and tainted.getEnclosingCallable() = method
where
conf.hasFlowToExpr(tainted) and
tainted.getEnclosingCallable() = method and
tainted.getFile().getStem() = ["Test", "Validation"]
select method, tainted.getLocation().getStartLine() - method.getLocation().getStartLine(), tainted

View File

@@ -0,0 +1,7 @@
package com.mongodb;
public class BasicDBObject implements com.mongodb.DBObject {
public static com.mongodb.BasicDBObject parse(java.lang.String json) {
return null;
}
}

View File

@@ -0,0 +1,8 @@
package com.mongodb;
public class DB {
public com.mongodb.DBCollection getCollection(java.lang.String name) {
return null;
}
}

View File

@@ -0,0 +1,7 @@
package com.mongodb;
public class DBCollection {
public com.mongodb.DBCursor find(com.mongodb.DBObject query) {
return null;
}
}

View File

@@ -0,0 +1,4 @@
package com.mongodb;
public class DBCursor {
}

View File

@@ -0,0 +1,4 @@
package com.mongodb;
public abstract interface DBObject {
}

View File

@@ -0,0 +1,10 @@
package com.mongodb;
public class Mongo {
public com.mongodb.DB getDB(java.lang.String dbName) {
return null;
}
}

View File

@@ -0,0 +1,6 @@
package com.mongodb;
public class MongoClient extends com.mongodb.Mongo {
public MongoClient(com.mongodb.ServerAddress addr) {
}
}

View File

@@ -0,0 +1,64 @@
// Failed to get sources. Instead, stub sources have been generated by the disassembler.
// Implementation of methods is unavailable.
package com.mongodb;
public class ServerAddress implements java.io.Serializable {
public ServerAddress() {
}
public ServerAddress(java.lang.String host) {
}
public ServerAddress(java.net.InetAddress inetAddress) {
}
public ServerAddress(java.net.InetAddress inetAddress, int port) {
}
public ServerAddress(java.net.InetSocketAddress inetSocketAddress) {
}
public ServerAddress(java.lang.String host, int port) {
}
public boolean equals(java.lang.Object o) {
return false;
}
public int hashCode() {
return 0;
}
public java.lang.String getHost() {
return null;
}
public int getPort() {
return 0;
}
public java.net.InetSocketAddress getSocketAddress() {
return null;
}
public java.util.List<java.net.InetSocketAddress> getSocketAddresses() {
return null;
}
public java.lang.String toString() {
return null;
}
public static java.lang.String defaultHost() {
return null;
}
public static int defaultPort() {
return 0;
}
public boolean sameHost(java.lang.String hostName) {
return false;
}
}

View File

@@ -0,0 +1,8 @@
package com.mongodb.util;
public class JSON {
public static java.lang.Object parse(java.lang.String jsonString) {
return null;
}
}

View File

@@ -0,0 +1,22 @@
package org.bson;
public abstract interface BSONObject {
public abstract java.lang.Object put(java.lang.String arg0, java.lang.Object arg1);
public abstract void putAll(org.bson.BSONObject arg0);
public abstract void putAll(java.util.Map arg0);
public abstract java.lang.Object get(java.lang.String arg0);
public abstract java.util.Map toMap();
public abstract java.lang.Object removeField(java.lang.String arg0);
public abstract boolean containsKey(java.lang.String arg0);
public abstract boolean containsField(java.lang.String arg0);
public abstract java.util.Set<java.lang.String> keySet();
}

View File

@@ -912,7 +912,7 @@ module DataFlow {
function.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
override BasicBlock getBasicBlock() { result = function.(ExprOrStmt).getBasicBlock() }
override BasicBlock getBasicBlock() { result = function.getExit().getBasicBlock() }
/**
* Gets the function corresponding to this exceptional return node.
@@ -938,7 +938,7 @@ module DataFlow {
function.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
override BasicBlock getBasicBlock() { result = function.(ExprOrStmt).getBasicBlock() }
override BasicBlock getBasicBlock() { result = function.getExit().getBasicBlock() }
/**
* Gets the function corresponding to this return node.

View File

@@ -0,0 +1,543 @@
| arguments.js:1:1:1:0 | this | arguments.js:1:1:1:0 | entry node of <toplevel> |
| arguments.js:1:1:12:2 | (functi ... 3);\\n}) | arguments.js:1:1:1:0 | entry node of <toplevel> |
| arguments.js:1:1:12:4 | (functi ... );\\n})() | arguments.js:1:1:1:0 | entry node of <toplevel> |
| arguments.js:1:1:12:4 | exceptional return of (functi ... );\\n})() | arguments.js:1:1:1:0 | entry node of <toplevel> |
| arguments.js:1:2:1:1 | this | arguments.js:1:2:1:1 | entry node of functio ... , 3);\\n} |
| arguments.js:1:2:12:1 | exceptional return of anonymous function | arguments.js:1:2:1:1 | entry node of functio ... , 3);\\n} |
| arguments.js:1:2:12:1 | functio ... , 3);\\n} | arguments.js:1:1:1:0 | entry node of <toplevel> |
| arguments.js:1:2:12:1 | return of anonymous function | arguments.js:1:2:1:1 | entry node of functio ... , 3);\\n} |
| arguments.js:2:5:2:4 | this | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:2:5:2:5 | arguments | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:2:5:10:5 | exceptional return of function f | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:2:5:10:5 | functio ... ;\\n } | arguments.js:1:2:1:1 | entry node of functio ... , 3);\\n} |
| arguments.js:2:5:10:5 | return of function f | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:2:14:2:14 | f | arguments.js:1:2:1:1 | entry node of functio ... , 3);\\n} |
| arguments.js:2:14:2:14 | f | arguments.js:1:2:1:1 | entry node of functio ... , 3);\\n} |
| arguments.js:2:16:2:16 | x | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:2:16:2:16 | x | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:3:13:3:20 | firstArg | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:3:13:3:24 | firstArg = x | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:3:24:3:24 | x | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:4:13:4:24 | alsoFirstArg | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:4:13:4:39 | alsoFir ... ents[0] | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:4:28:4:36 | arguments | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:4:28:4:39 | arguments[0] | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:4:38:4:38 | 0 | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:5:13:5:21 | secondArg | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:5:13:5:36 | secondA ... ents[1] | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:5:25:5:33 | arguments | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:5:25:5:36 | arguments[1] | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:5:35:5:35 | 1 | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:6:13:6:16 | args | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:6:13:6:28 | args | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:6:13:6:28 | args = arguments | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:6:20:6:28 | arguments | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:7:13:7:20 | thirdArg | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:7:13:7:30 | thirdArg = args[2] | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:7:24:7:27 | args | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:7:24:7:30 | args[2] | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:7:29:7:29 | 2 | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:8:9:8:17 | arguments | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:8:9:8:22 | arguments | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:8:9:8:22 | arguments = {} | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:8:21:8:22 | {} | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:9:13:9:23 | notFirstArg | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:9:13:9:38 | notFirs ... ents[0] | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:9:27:9:35 | arguments | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:9:27:9:38 | arguments[0] | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:9:37:9:37 | 0 | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } |
| arguments.js:11:5:11:5 | f | arguments.js:1:2:1:1 | entry node of functio ... , 3);\\n} |
| arguments.js:11:5:11:14 | exceptional return of f(1, 2, 3) | arguments.js:1:2:1:1 | entry node of functio ... , 3);\\n} |
| arguments.js:11:5:11:14 | f(1, 2, 3) | arguments.js:1:2:1:1 | entry node of functio ... , 3);\\n} |
| arguments.js:11:7:11:7 | 1 | arguments.js:1:2:1:1 | entry node of functio ... , 3);\\n} |
| arguments.js:11:10:11:10 | 2 | arguments.js:1:2:1:1 | entry node of functio ... , 3);\\n} |
| arguments.js:11:13:11:13 | 3 | arguments.js:1:2:1:1 | entry node of functio ... , 3);\\n} |
| eval.js:1:1:1:0 | this | eval.js:1:1:1:0 | entry node of <toplevel> |
| eval.js:1:1:1:0 | this | eval.js:1:1:1:0 | entry node of functio ... eval`\\n} |
| eval.js:1:1:5:1 | exceptional return of function k | eval.js:1:1:1:0 | entry node of functio ... eval`\\n} |
| eval.js:1:1:5:1 | functio ... eval`\\n} | eval.js:1:1:1:0 | entry node of <toplevel> |
| eval.js:1:1:5:1 | return of function k | eval.js:1:1:1:0 | entry node of functio ... eval`\\n} |
| eval.js:1:10:1:10 | k | eval.js:1:1:1:0 | entry node of <toplevel> |
| eval.js:2:7:2:7 | x | eval.js:1:1:1:0 | entry node of functio ... eval`\\n} |
| eval.js:2:7:2:12 | x | eval.js:1:1:1:0 | entry node of functio ... eval`\\n} |
| eval.js:2:7:2:12 | x = 42 | eval.js:1:1:1:0 | entry node of functio ... eval`\\n} |
| eval.js:2:11:2:12 | 42 | eval.js:1:1:1:0 | entry node of functio ... eval`\\n} |
| eval.js:3:3:3:6 | eval | eval.js:1:1:1:0 | entry node of functio ... eval`\\n} |
| eval.js:3:3:3:16 | eval("x = 23") | eval.js:1:1:1:0 | entry node of functio ... eval`\\n} |
| eval.js:3:3:3:16 | exceptional return of eval("x = 23") | eval.js:1:1:1:0 | entry node of functio ... eval`\\n} |
| eval.js:3:8:3:15 | "x = 23" | eval.js:1:1:1:0 | entry node of functio ... eval`\\n} |
| eval.js:4:3:4:3 | x | eval.js:1:1:1:0 | entry node of functio ... eval`\\n} |
| sources.js:1:1:1:0 | this | sources.js:1:1:1:0 | entry node of <toplevel> |
| sources.js:1:1:1:12 | exceptional return of new (x => x) | sources.js:1:1:1:0 | entry node of <toplevel> |
| sources.js:1:1:1:12 | new (x => x) | sources.js:1:1:1:0 | entry node of <toplevel> |
| sources.js:1:5:1:12 | (x => x) | sources.js:1:1:1:0 | entry node of <toplevel> |
| sources.js:1:6:1:6 | x | sources.js:1:6:1:5 | entry node of x => x |
| sources.js:1:6:1:6 | x | sources.js:1:6:1:5 | entry node of x => x |
| sources.js:1:6:1:11 | exceptional return of anonymous function | sources.js:1:6:1:5 | entry node of x => x |
| sources.js:1:6:1:11 | return of anonymous function | sources.js:1:6:1:5 | entry node of x => x |
| sources.js:1:6:1:11 | x => x | sources.js:1:1:1:0 | entry node of <toplevel> |
| sources.js:1:11:1:11 | x | sources.js:1:6:1:5 | entry node of x => x |
| sources.js:3:1:5:2 | (functi ... +19;\\n}) | sources.js:1:1:1:0 | entry node of <toplevel> |
| sources.js:3:1:5:6 | (functi ... \\n})(23) | sources.js:1:1:1:0 | entry node of <toplevel> |
| sources.js:3:1:5:6 | exceptional return of (functi ... \\n})(23) | sources.js:1:1:1:0 | entry node of <toplevel> |
| sources.js:3:2:3:1 | this | sources.js:3:2:3:1 | entry node of functio ... x+19;\\n} |
| sources.js:3:2:5:1 | exceptional return of anonymous function | sources.js:3:2:3:1 | entry node of functio ... x+19;\\n} |
| sources.js:3:2:5:1 | functio ... x+19;\\n} | sources.js:1:1:1:0 | entry node of <toplevel> |
| sources.js:3:2:5:1 | return of anonymous function | sources.js:3:2:3:1 | entry node of functio ... x+19;\\n} |
| sources.js:3:11:3:11 | x | sources.js:3:2:3:1 | entry node of functio ... x+19;\\n} |
| sources.js:3:11:3:11 | x | sources.js:3:2:3:1 | entry node of functio ... x+19;\\n} |
| sources.js:4:10:4:10 | x | sources.js:3:2:3:1 | entry node of functio ... x+19;\\n} |
| sources.js:4:10:4:13 | x+19 | sources.js:3:2:3:1 | entry node of functio ... x+19;\\n} |
| sources.js:4:12:4:13 | 19 | sources.js:3:2:3:1 | entry node of functio ... x+19;\\n} |
| sources.js:5:4:5:5 | 23 | sources.js:1:1:1:0 | entry node of <toplevel> |
| sources.js:7:1:7:3 | /x/ | sources.js:1:1:1:0 | entry node of <toplevel> |
| sources.js:9:1:9:0 | this | sources.js:9:1:9:0 | entry node of functio ... ey; }\\n} |
| sources.js:9:1:12:1 | exceptional return of function foo | sources.js:12:2:12:1 | exit node of functio ... ey; }\\n} |
| sources.js:9:1:12:1 | functio ... ey; }\\n} | sources.js:1:1:1:0 | entry node of <toplevel> |
| sources.js:9:1:12:1 | return of function foo | sources.js:12:2:12:1 | exit node of functio ... ey; }\\n} |
| sources.js:9:10:9:12 | foo | sources.js:1:1:1:0 | entry node of <toplevel> |
| sources.js:9:14:9:18 | array | sources.js:9:1:9:0 | entry node of functio ... ey; }\\n} |
| sources.js:9:14:9:18 | array | sources.js:9:1:9:0 | entry node of functio ... ey; }\\n} |
| sources.js:10:12:10:14 | key | sources.js:10:8:10:14 | let key |
| sources.js:10:12:10:14 | key | sources.js:10:8:10:14 | let key |
| sources.js:10:12:10:14 | key | sources.js:10:8:10:14 | let key |
| sources.js:10:19:10:23 | array | sources.js:9:1:9:0 | entry node of functio ... ey; }\\n} |
| sources.js:10:28:10:30 | key | sources.js:10:8:10:14 | let key |
| sources.js:11:12:11:18 | key | sources.js:11:8:11:18 | let { key } |
| sources.js:11:12:11:18 | { key } | sources.js:11:8:11:18 | let { key } |
| sources.js:11:12:11:18 | { key } | sources.js:11:8:11:18 | let { key } |
| sources.js:11:14:11:16 | key | sources.js:11:8:11:18 | let { key } |
| sources.js:11:14:11:16 | key | sources.js:11:8:11:18 | let { key } |
| sources.js:11:14:11:16 | key | sources.js:11:8:11:18 | let { key } |
| sources.js:11:14:11:16 | key | sources.js:11:8:11:18 | let { key } |
| sources.js:11:23:11:27 | array | sources.js:11:23:11:27 | array |
| sources.js:11:32:11:34 | key | sources.js:11:8:11:18 | let { key } |
| tst2.ts:1:1:1:0 | this | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:1:1:1:1 | A | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:1:8:5:1 | A | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:1:8:5:1 | namespa ... lysed\\n} | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:1:18:1:18 | A | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:1:18:1:18 | A | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:2:14:2:14 | x | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:2:14:2:19 | x | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:2:14:2:19 | x = 42 | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:2:18:2:19 | 42 | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:3:3:3:6 | setX | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:3:3:3:8 | exceptional return of setX() | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:3:3:3:8 | setX() | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:4:3:4:3 | x | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:7:1:7:0 | A | tst2.ts:7:1:7:0 | entry node of functio ... = 23;\\n} |
| tst2.ts:7:1:7:0 | this | tst2.ts:7:1:7:0 | entry node of functio ... = 23;\\n} |
| tst2.ts:7:1:9:1 | exceptional return of function setX | tst2.ts:7:1:7:0 | entry node of functio ... = 23;\\n} |
| tst2.ts:7:1:9:1 | functio ... = 23;\\n} | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:7:1:9:1 | return of function setX | tst2.ts:7:1:7:0 | entry node of functio ... = 23;\\n} |
| tst2.ts:7:10:7:13 | setX | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:7:10:7:13 | setX | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:8:3:8:3 | A | tst2.ts:7:1:7:0 | entry node of functio ... = 23;\\n} |
| tst2.ts:8:3:8:5 | A.x | tst2.ts:7:1:7:0 | entry node of functio ... = 23;\\n} |
| tst2.ts:8:3:8:10 | A.x = 23 | tst2.ts:7:1:7:0 | entry node of functio ... = 23;\\n} |
| tst2.ts:8:5:8:5 | x | tst2.ts:7:1:7:0 | entry node of functio ... = 23;\\n} |
| tst2.ts:8:9:8:10 | 23 | tst2.ts:7:1:7:0 | entry node of functio ... = 23;\\n} |
| tst2.ts:11:5:11:7 | nd2 | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:11:5:11:23 | nd2 = A.x as number | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:11:11:11:11 | A | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:11:11:11:13 | A.x | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:11:11:11:23 | A.x as number | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:11:13:11:13 | x | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:13:1:13:40 | class S ... ing> {} | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:13:7:13:16 | StringList | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:13:26:13:29 | List | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:13:26:13:37 | List<string> | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:13:39:13:38 | (...arg ... rgs); } | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:13:39:13:38 | ...args | tst2.ts:13:39:13:38 | entry node of (...arg ... rgs); } |
| tst2.ts:13:39:13:38 | args | tst2.ts:13:39:13:38 | entry node of (...arg ... rgs); } |
| tst2.ts:13:39:13:38 | args | tst2.ts:13:39:13:38 | entry node of (...arg ... rgs); } |
| tst2.ts:13:39:13:38 | args | tst2.ts:13:39:13:38 | entry node of (...arg ... rgs); } |
| tst2.ts:13:39:13:38 | constru ... rgs); } | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:13:39:13:38 | constru ... rgs); } | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:13:39:13:38 | constructor | tst2.ts:1:1:1:0 | entry node of <toplevel> |
| tst2.ts:13:39:13:38 | exceptional return of default constructor of class StringList | tst2.ts:13:39:13:38 | entry node of (...arg ... rgs); } |
| tst2.ts:13:39:13:38 | exceptional return of super(...args) | tst2.ts:13:39:13:38 | entry node of (...arg ... rgs); } |
| tst2.ts:13:39:13:38 | return of default constructor of class StringList | tst2.ts:13:39:13:38 | entry node of (...arg ... rgs); } |
| tst2.ts:13:39:13:38 | super | tst2.ts:13:39:13:38 | entry node of (...arg ... rgs); } |
| tst2.ts:13:39:13:38 | super(...args) | tst2.ts:13:39:13:38 | entry node of (...arg ... rgs); } |
| tst2.ts:13:39:13:38 | this | tst2.ts:13:39:13:38 | entry node of (...arg ... rgs); } |
| tst.js:1:1:1:0 | this | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:1:1:1:1 | x | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:1:1:1:24 | import ... m 'fs'; | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:1:10:1:11 | fs | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:1:10:1:11 | fs | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:3:5:3:5 | x | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:3:5:3:5 | x | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:3:5:3:10 | x | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:3:5:3:10 | x = 42 | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:3:9:3:10 | 42 | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:4:5:4:5 | y | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:4:5:4:12 | y | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:4:5:4:12 | y = "hi" | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:4:9:4:12 | "hi" | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:5:5:5:5 | z | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:5:5:5:5 | z | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:7:1:7:2 | fs | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:8:1:8:1 | x | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:9:1:9:3 | (x) | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:9:2:9:2 | x | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:10:1:10:1 | x | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:10:1:10:4 | x, y | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:10:4:10:4 | y | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:11:1:11:1 | x | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:11:1:11:1 | x | tst.js:11:1:11:1 | guard: x is false |
| tst.js:11:1:11:1 | x | tst.js:11:1:11:1 | guard: x is true |
| tst.js:11:1:11:6 | x && y | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:11:6:11:6 | y | tst.js:11:1:11:1 | guard: x is true |
| tst.js:12:1:12:1 | x | tst.js:12:1:12:1 | guard: x is false |
| tst.js:12:1:12:1 | x | tst.js:12:1:12:1 | guard: x is true |
| tst.js:12:1:12:1 | x | tst.js:12:1:12:7 | x \|\| y; |
| tst.js:12:1:12:1 | x | tst.js:12:1:12:7 | x \|\| y; |
| tst.js:12:1:12:6 | x \|\| y | tst.js:12:1:12:7 | x \|\| y; |
| tst.js:12:6:12:6 | y | tst.js:12:1:12:1 | guard: x is false |
| tst.js:13:1:13:1 | x | tst.js:13:1:13:6 | z = y; |
| tst.js:13:1:13:1 | z | tst.js:13:1:13:6 | z = y; |
| tst.js:13:1:13:5 | z | tst.js:13:1:13:6 | z = y; |
| tst.js:13:1:13:5 | z = y | tst.js:13:1:13:6 | z = y; |
| tst.js:13:5:13:5 | y | tst.js:13:1:13:6 | z = y; |
| tst.js:14:1:14:1 | z | tst.js:13:1:13:6 | z = y; |
| tst.js:14:1:14:9 | z ? x : y | tst.js:13:1:13:6 | z = y; |
| tst.js:14:5:14:5 | x | tst.js:14:1:14:1 | guard: z is true |
| tst.js:14:9:14:9 | y | tst.js:14:1:14:1 | guard: z is false |
| tst.js:16:1:20:2 | (functi ... "";\\n}) | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:16:1:20:9 | (functi ... ("arg") | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:16:1:20:9 | exceptional return of (functi ... ("arg") | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:16:2:16:1 | this | tst.js:16:2:16:1 | entry node of functio ... n "";\\n} |
| tst.js:16:2:20:1 | exceptional return of function f | tst.js:20:2:20:1 | exit node of functio ... n "";\\n} |
| tst.js:16:2:20:1 | functio ... n "";\\n} | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:16:2:20:1 | return of function f | tst.js:20:2:20:1 | exit node of functio ... n "";\\n} |
| tst.js:16:11:16:11 | f | tst.js:16:2:16:1 | entry node of functio ... n "";\\n} |
| tst.js:16:13:16:13 | a | tst.js:16:2:16:1 | entry node of functio ... n "";\\n} |
| tst.js:16:13:16:13 | a | tst.js:16:2:16:1 | entry node of functio ... n "";\\n} |
| tst.js:17:7:17:10 | Math | tst.js:16:2:16:1 | entry node of functio ... n "";\\n} |
| tst.js:17:7:17:17 | Math.random | tst.js:16:2:16:1 | entry node of functio ... n "";\\n} |
| tst.js:17:7:17:19 | Math.random() | tst.js:16:2:16:1 | entry node of functio ... n "";\\n} |
| tst.js:17:7:17:19 | exceptional return of Math.random() | tst.js:16:2:16:1 | entry node of functio ... n "";\\n} |
| tst.js:17:7:17:25 | Math.random() > 0.5 | tst.js:16:2:16:1 | entry node of functio ... n "";\\n} |
| tst.js:17:12:17:17 | random | tst.js:16:2:16:1 | entry node of functio ... n "";\\n} |
| tst.js:17:23:17:25 | 0.5 | tst.js:16:2:16:1 | entry node of functio ... n "";\\n} |
| tst.js:18:12:18:12 | a | tst.js:17:7:17:25 | guard: Math.random() > 0.5 is true |
| tst.js:19:10:19:11 | "" | tst.js:17:7:17:25 | guard: Math.random() > 0.5 is false |
| tst.js:20:4:20:8 | "arg" | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:22:5:22:20 | { readFileSync } | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:22:5:22:25 | readFileSync | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:22:5:22:25 | { readF ... } = fs | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:22:7:22:18 | readFileSync | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:22:7:22:18 | readFileSync | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:22:7:22:18 | readFileSync | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:22:7:22:18 | readFileSync | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:22:24:22:25 | fs | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:23:1:23:12 | readFileSync | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:25:1:25:3 | ++x | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:25:1:25:3 | x | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:25:3:25:3 | x | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:26:1:26:1 | x | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:28:1:30:1 | (() =>\\n ... ables\\n) | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:28:1:30:3 | (() =>\\n ... les\\n)() | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:28:1:30:3 | exceptional return of (() =>\\n ... les\\n)() | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:28:2:28:1 | x | tst.js:28:2:28:1 | entry node of () =>\\n x |
| tst.js:28:2:29:3 | () =>\\n x | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:28:2:29:3 | exceptional return of anonymous function | tst.js:28:2:28:1 | entry node of () =>\\n x |
| tst.js:28:2:29:3 | return of anonymous function | tst.js:28:2:28:1 | entry node of () =>\\n x |
| tst.js:29:3:29:3 | x | tst.js:28:2:28:1 | entry node of () =>\\n x |
| tst.js:32:1:32:0 | this | tst.js:32:1:32:0 | entry node of functio ... ables\\n} |
| tst.js:32:1:32:0 | x | tst.js:32:1:32:0 | entry node of functio ... ables\\n} |
| tst.js:32:1:34:1 | exceptional return of function g | tst.js:32:1:32:0 | entry node of functio ... ables\\n} |
| tst.js:32:1:34:1 | functio ... ables\\n} | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:32:1:34:1 | return of function g | tst.js:32:1:32:0 | entry node of functio ... ables\\n} |
| tst.js:32:10:32:10 | g | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:32:10:32:10 | g | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:32:12:32:12 | b | tst.js:32:1:32:0 | entry node of functio ... ables\\n} |
| tst.js:33:10:33:10 | x | tst.js:32:1:32:0 | entry node of functio ... ables\\n} |
| tst.js:35:1:35:1 | g | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:35:1:35:7 | exceptional return of g(true) | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:35:1:35:7 | g(true) | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:35:3:35:6 | true | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:37:5:37:5 | o | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:37:5:42:1 | o | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:37:5:42:1 | o = {\\n ... ;\\n }\\n} | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:37:9:42:1 | {\\n x: ... ;\\n }\\n} | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:38:3:38:3 | x | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:38:3:38:9 | x: null | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:38:3:38:9 | x: null | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:38:6:38:9 | null | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:39:3:39:3 | m | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:39:3:41:3 | m() {\\n this;\\n } | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:39:3:41:3 | m() {\\n this;\\n } | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:39:4:39:3 | this | tst.js:39:4:39:3 | entry node of () {\\n this;\\n } |
| tst.js:39:4:41:3 | () {\\n this;\\n } | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:39:4:41:3 | exceptional return of method m | tst.js:39:4:39:3 | entry node of () {\\n this;\\n } |
| tst.js:39:4:41:3 | return of method m | tst.js:39:4:39:3 | entry node of () {\\n this;\\n } |
| tst.js:40:5:40:8 | this | tst.js:39:4:39:3 | entry node of () {\\n this;\\n } |
| tst.js:43:1:43:1 | o | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:43:1:43:3 | o.x | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:43:3:43:3 | x | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:44:1:44:1 | o | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:44:1:44:3 | o.m | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:44:1:44:5 | exceptional return of o.m() | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:44:1:44:5 | o.m() | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:44:3:44:3 | m | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:46:1:46:6 | global | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:46:1:46:11 | global = "" | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:46:10:46:11 | "" | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:47:1:47:6 | global | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:49:1:54:1 | A | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:49:1:54:1 | class A ... `\\n }\\n} | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:49:7:49:7 | A | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:49:17:49:17 | B | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:50:3:50:13 | constructor | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:50:3:53:3 | constru ... et`\\n } | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:50:3:53:3 | constru ... et`\\n } | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:50:14:50:13 | this | tst.js:50:14:50:13 | entry node of () {\\n ... et`\\n } |
| tst.js:50:14:53:3 | () {\\n ... et`\\n } | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:50:14:53:3 | exceptional return of constructor of class A | tst.js:50:14:50:13 | entry node of () {\\n ... et`\\n } |
| tst.js:50:14:53:3 | return of constructor of class A | tst.js:50:14:50:13 | entry node of () {\\n ... et`\\n } |
| tst.js:51:5:51:9 | super | tst.js:50:14:50:13 | entry node of () {\\n ... et`\\n } |
| tst.js:51:5:51:13 | exceptional return of super(42) | tst.js:50:14:50:13 | entry node of () {\\n ... et`\\n } |
| tst.js:51:5:51:13 | super(42) | tst.js:50:14:50:13 | entry node of () {\\n ... et`\\n } |
| tst.js:51:11:51:12 | 42 | tst.js:50:14:50:13 | entry node of () {\\n ... et`\\n } |
| tst.js:52:5:52:14 | new.target | tst.js:50:14:50:13 | entry node of () {\\n ... et`\\n } |
| tst.js:55:1:55:1 | A | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:57:1:57:9 | `x: ${x}` | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:57:2:57:4 | x: | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:57:7:57:7 | x | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:58:1:58:3 | tag | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:58:1:58:13 | tag `x: ${x}` | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:58:5:58:13 | `x: ${x}` | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:58:6:58:8 | x: | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:58:11:58:11 | x | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:60:1:60:1 | g | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:61:1:61:5 | ::o.m | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:61:3:61:3 | o | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:61:3:61:5 | o.m | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:61:5:61:5 | m | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:62:1:62:1 | o | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:62:1:62:4 | o::g | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:62:4:62:4 | g | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:64:1:64:0 | this | tst.js:64:1:64:0 | entry node of functio ... lysed\\n} |
| tst.js:64:1:67:1 | exceptional return of function h | tst.js:64:1:64:0 | entry node of functio ... lysed\\n} |
| tst.js:64:1:67:1 | functio ... lysed\\n} | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:64:1:67:1 | return of function h | tst.js:64:1:64:0 | entry node of functio ... lysed\\n} |
| tst.js:64:11:64:11 | h | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:64:11:64:11 | h | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:65:3:65:10 | yield 42 | tst.js:64:1:64:0 | entry node of functio ... lysed\\n} |
| tst.js:65:9:65:10 | 42 | tst.js:64:1:64:0 | entry node of functio ... lysed\\n} |
| tst.js:66:7:66:9 | tmp | tst.js:64:1:64:0 | entry node of functio ... lysed\\n} |
| tst.js:66:7:66:25 | tmp = function.sent | tst.js:64:1:64:0 | entry node of functio ... lysed\\n} |
| tst.js:66:13:66:25 | function.sent | tst.js:64:1:64:0 | entry node of functio ... lysed\\n} |
| tst.js:68:5:68:8 | iter | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:68:5:68:14 | iter | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:68:5:68:14 | iter = h() | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:68:12:68:12 | h | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:68:12:68:14 | exceptional return of h() | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:68:12:68:14 | h() | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:69:1:69:4 | iter | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:69:1:69:9 | iter.next | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:69:1:69:13 | exceptional return of iter.next(23) | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:69:1:69:13 | iter.next(23) | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:69:6:69:9 | next | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:69:11:69:12 | 23 | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:71:1:71:0 | this | tst.js:71:1:71:0 | entry node of async f ... lysed\\n} |
| tst.js:71:1:73:1 | async f ... lysed\\n} | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:71:1:73:1 | exceptional return of function k | tst.js:71:1:71:0 | entry node of async f ... lysed\\n} |
| tst.js:71:1:73:1 | return of function k | tst.js:71:1:71:0 | entry node of async f ... lysed\\n} |
| tst.js:71:16:71:16 | k | tst.js:1:1:1:0 | entry node of <toplevel> |
| tst.js:72:3:72:11 | await p() | tst.js:71:1:71:0 | entry node of async f ... lysed\\n} |
| tst.js:72:9:72:9 | p | tst.js:71:1:71:0 | entry node of async f ... lysed\\n} |
| tst.js:72:9:72:11 | exceptional return of p() | tst.js:71:1:71:0 | entry node of async f ... lysed\\n} |
| tst.js:72:9:72:11 | p() | tst.js:71:1:71:0 | entry node of async f ... lysed\\n} |
| tst.js:75:5:75:5 | m | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:75:5:75:21 | m = import('foo') | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:75:9:75:21 | import('foo') | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:75:16:75:20 | 'foo' | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:77:10:77:10 | i | tst.js:77:6:77:10 | let i |
| tst.js:77:10:77:10 | i | tst.js:77:6:77:10 | let i |
| tst.js:77:10:77:10 | i | tst.js:77:6:77:10 | let i |
| tst.js:77:15:77:15 | o | tst.js:16:1:20:10 | (functi ... "arg"); |
| tst.js:78:3:78:3 | i | tst.js:77:6:77:10 | let i |
| tst.js:80:10:80:10 | v | tst.js:80:6:80:10 | let v |
| tst.js:80:10:80:10 | v | tst.js:80:6:80:10 | let v |
| tst.js:80:10:80:10 | v | tst.js:80:6:80:10 | let v |
| tst.js:80:15:80:15 | o | tst.js:80:15:80:15 | o |
| tst.js:81:3:81:3 | v | tst.js:80:6:80:10 | let v |
| tst.js:83:5:83:7 | vs1 | tst.js:83:1:83:29 | var vs1 ... o) v ]; |
| tst.js:83:5:83:28 | vs1 = [ ... o) v ] | tst.js:83:5:83:28 | vs1 = [ ... o) v ] |
| tst.js:83:11:83:28 | [ for (v of o) v ] | tst.js:83:1:83:29 | var vs1 ... o) v ]; |
| tst.js:83:13:83:24 | for (v of o) | tst.js:83:1:83:29 | var vs1 ... o) v ]; |
| tst.js:83:18:83:18 | v | tst.js:83:18:83:18 | v |
| tst.js:83:18:83:18 | v | tst.js:83:18:83:18 | v |
| tst.js:83:23:83:23 | o | tst.js:83:1:83:29 | var vs1 ... o) v ]; |
| tst.js:83:26:83:26 | v | tst.js:83:18:83:18 | v |
| tst.js:85:5:85:7 | vs2 | tst.js:83:5:83:28 | vs1 = [ ... o) v ] |
| tst.js:85:5:85:28 | vs2 = ( ... o) v ) | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:85:11:85:28 | ( for (v of o) v ) | tst.js:83:5:83:28 | vs1 = [ ... o) v ] |
| tst.js:85:13:85:24 | for (v of o) | tst.js:83:5:83:28 | vs1 = [ ... o) v ] |
| tst.js:85:18:85:18 | v | tst.js:85:18:85:18 | v |
| tst.js:85:18:85:18 | v | tst.js:85:18:85:18 | v |
| tst.js:85:23:85:23 | o | tst.js:83:5:83:28 | vs1 = [ ... o) v ] |
| tst.js:85:26:85:26 | v | tst.js:85:18:85:18 | v |
| tst.js:87:1:92:2 | (functi ... + z;\\n}) | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:87:1:96:2 | (functi ... r: 0\\n}) | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:87:1:96:2 | exceptional return of (functi ... r: 0\\n}) | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:87:2:87:1 | this | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:87:2:92:1 | exceptional return of anonymous function | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:87:2:92:1 | functio ... + z;\\n} | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:87:2:92:1 | return of anonymous function | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:87:11:87:24 | o | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:87:11:87:24 | x | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:87:11:87:24 | { p: x, ...o } | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:87:13:87:13 | p | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:87:13:87:16 | p: x | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:87:13:87:16 | p: x | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:87:16:87:16 | x | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:87:22:87:22 | ...o | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:87:22:87:22 | o | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:88:7:88:14 | { q: y } | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:88:7:88:18 | y | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:88:7:88:18 | { q: y } = o | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:88:9:88:9 | q | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:88:9:88:12 | q: y | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:88:9:88:12 | q: y | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:88:12:88:12 | y | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:88:18:88:18 | o | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:89:7:89:7 | z | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:89:7:89:7 | z | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:90:3:90:16 | ({ r: z } = o) | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:90:4:90:11 | { r: z } | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:90:4:90:15 | z | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:90:4:90:15 | { r: z } = o | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:90:6:90:6 | r | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:90:6:90:9 | r: z | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:90:6:90:9 | r: z | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:90:9:90:9 | z | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:90:15:90:15 | o | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:91:10:91:10 | x | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:91:10:91:14 | x + y | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:91:10:91:18 | x + y + z | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:91:14:91:14 | y | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:91:18:91:18 | z | tst.js:87:2:87:1 | entry node of functio ... + z;\\n} |
| tst.js:92:4:96:1 | {\\n p: ... r: 0\\n} | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:93:3:93:3 | p | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:93:3:93:7 | p: 19 | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:93:3:93:7 | p: 19 | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:93:6:93:7 | 19 | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:94:3:94:3 | q | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:94:3:94:7 | q: 23 | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:94:3:94:7 | q: 23 | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:94:6:94:7 | 23 | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:95:3:95:3 | r | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:95:3:95:6 | r: 0 | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:95:3:95:6 | r: 0 | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:95:6:95:6 | 0 | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:98:1:103:2 | (functi ... + z;\\n}) | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:98:1:103:17 | (functi ... 3, 0 ]) | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:98:1:103:17 | exceptional return of (functi ... 3, 0 ]) | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:98:2:98:1 | this | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:98:2:103:1 | exceptional return of anonymous function | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:98:2:103:1 | functio ... + z;\\n} | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:98:2:103:1 | return of anonymous function | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:98:11:98:24 | [ x, ...rest ] | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:98:11:98:24 | rest | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:98:11:98:24 | x | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:98:13:98:13 | x | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:98:13:98:13 | x | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:98:19:98:22 | ...rest | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:98:19:98:22 | rest | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:99:7:99:11 | [ y ] | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:99:7:99:18 | [ y ] = rest | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:99:7:99:18 | y | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:99:9:99:9 | y | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:99:9:99:9 | y | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:99:15:99:18 | rest | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:100:7:100:7 | z | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:100:7:100:7 | z | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:101:3:101:9 | [ , z ] | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:101:3:101:16 | [ , z ] = rest | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:101:3:101:16 | z | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:101:7:101:7 | z | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:101:7:101:7 | z | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:101:13:101:16 | rest | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:102:10:102:10 | x | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:102:10:102:14 | x + y | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:102:10:102:18 | x + y + z | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:102:14:102:14 | y | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:102:18:102:18 | z | tst.js:98:2:98:1 | entry node of functio ... + z;\\n} |
| tst.js:103:4:103:16 | [ 19, 23, 0 ] | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:103:6:103:7 | 19 | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:103:6:103:7 | 19 | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:103:10:103:11 | 23 | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:103:10:103:11 | 23 | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:103:14:103:14 | 0 | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:103:14:103:14 | 0 | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:105:1:105:1 | x | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:105:1:105:6 | x ?? y | tst.js:85:5:85:28 | vs2 = ( ... o) v ) |
| tst.js:105:6:105:6 | y | tst.js:105:6:105:6 | y |
| tst.js:107:1:113:2 | (functi ... v2c;\\n}) | tst.js:107:1:113:3 | (functi ... 2c;\\n}); |
| tst.js:107:2:107:1 | this | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:107:2:113:1 | exceptional return of anonymous function | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:107:2:113:1 | functio ... v2c;\\n} | tst.js:107:1:113:3 | (functi ... 2c;\\n}); |
| tst.js:107:2:113:1 | return of anonymous function | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:108:6:108:32 | {v1a, v ... = o1c} | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:108:6:108:38 | v1a | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:108:6:108:38 | v1b | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:108:6:108:38 | v1c | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:108:6:108:38 | {v1a, v ... } = o1d | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:108:7:108:9 | v1a | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:108:7:108:9 | v1a | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:108:7:108:9 | v1a | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:108:7:108:9 | v1a | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:108:12:108:14 | v1b | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:108:12:108:14 | v1b | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:108:12:108:20 | v1b = o1b | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:108:12:108:20 | v1b = o1b | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:108:18:108:20 | o1b | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:108:23:108:25 | v1c | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:108:23:108:25 | v1c | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:108:23:108:31 | v1c = o1c | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:108:23:108:31 | v1c = o1c | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:108:29:108:31 | o1c | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:108:36:108:38 | o1d | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:109:2:109:4 | v1a | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:109:2:109:10 | v1a + v1b | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:109:2:109:16 | v1a + v1b + v1c | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:109:8:109:10 | v1b | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:109:14:109:16 | v1c | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:111:6:111:32 | [v2a, v ... = o2c] | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:111:6:111:38 | [v2a, v ... ] = o2d | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:111:6:111:38 | v2a | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:111:6:111:38 | v2b | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:111:6:111:38 | v2c | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:111:7:111:9 | v2a | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:111:7:111:9 | v2a | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:111:12:111:14 | v2b | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:111:12:111:14 | v2b | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:111:18:111:20 | o2b | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:111:23:111:25 | v2c | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:111:23:111:25 | v2c | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:111:29:111:31 | o2c | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:111:36:111:38 | o2d | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:112:2:112:4 | v2a | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:112:2:112:10 | v2a + v2b | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:112:2:112:16 | v2a + v2b + v2c | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:112:8:112:10 | v2b | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:112:14:112:16 | v2c | tst.js:107:2:107:1 | entry node of functio ... v2c;\\n} |
| tst.js:115:1:115:5 | Array | tst.js:107:1:113:3 | (functi ... 2c;\\n}); |
| tst.js:115:1:115:10 | Array.call | tst.js:107:1:113:3 | (functi ... 2c;\\n}); |
| tst.js:115:1:115:12 | Array.call() | tst.js:107:1:113:3 | (functi ... 2c;\\n}); |
| tst.js:115:1:115:12 | exceptional return of Array.call() | tst.js:107:1:113:3 | (functi ... 2c;\\n}); |
| tst.js:115:1:115:12 | reflective call | tst.js:107:1:113:3 | (functi ... 2c;\\n}); |
| tst.js:115:7:115:10 | call | tst.js:107:1:113:3 | (functi ... 2c;\\n}); |
| tst.js:117:5:117:6 | x2 | tst.js:107:1:113:3 | (functi ... 2c;\\n}); |
| tst.js:117:5:117:24 | x2 = Object.seal(x1) | tst.js:107:1:113:3 | (functi ... 2c;\\n}); |
| tst.js:117:10:117:15 | Object | tst.js:107:1:113:3 | (functi ... 2c;\\n}); |
| tst.js:117:10:117:20 | Object.seal | tst.js:107:1:113:3 | (functi ... 2c;\\n}); |
| tst.js:117:10:117:24 | Object.seal(x1) | tst.js:107:1:113:3 | (functi ... 2c;\\n}); |
| tst.js:117:10:117:24 | exceptional return of Object.seal(x1) | tst.js:107:1:113:3 | (functi ... 2c;\\n}); |
| tst.js:117:17:117:20 | seal | tst.js:107:1:113:3 | (functi ... 2c;\\n}); |
| tst.js:117:22:117:23 | x1 | tst.js:107:1:113:3 | (functi ... 2c;\\n}); |

View File

@@ -0,0 +1,4 @@
import javascript
from DataFlow::Node node
select node, node.getBasicBlock()

View File

@@ -0,0 +1,282 @@
/** Step Summaries and Type Tracking */
import python
import internal.DataFlowPublic
import internal.DataFlowPrivate
/** Any string that may appear as the name of an attribute or access path. */
class AttributeName extends string {
AttributeName() { this = any(Attribute a).getName() }
}
/** Either an attribute name, or the empty string (representing no attribute). */
class OptionalAttributeName extends string {
OptionalAttributeName() { this instanceof AttributeName or this = "" }
}
/**
* A description of a step on an inter-procedural data flow path.
*/
private newtype TStepSummary =
LevelStep() or
CallStep() or
ReturnStep() or
StoreStep(AttributeName attr) or
LoadStep(AttributeName attr)
/**
* INTERNAL: Use `TypeTracker` or `TypeBackTracker` instead.
*
* A description of a step on an inter-procedural data flow path.
*/
class StepSummary extends TStepSummary {
/** Gets a textual representation of this step summary. */
string toString() {
this instanceof LevelStep and result = "level"
or
this instanceof CallStep and result = "call"
or
this instanceof ReturnStep and result = "return"
or
exists(string attr | this = StoreStep(attr) | result = "store " + attr)
or
exists(string attr | this = LoadStep(attr) | result = "load " + attr)
}
}
module StepSummary {
cached
predicate step(Node nodeFrom, Node nodeTo, StepSummary summary) {
exists(Node mid | EssaFlow::essaFlowStep*(nodeFrom, mid) and smallstep(mid, nodeTo, summary))
}
predicate smallstep(Node nodeFrom, Node nodeTo, StepSummary summary) {
EssaFlow::essaFlowStep(nodeFrom, nodeTo) and
summary = LevelStep()
or
callStep(nodeFrom, nodeTo) and summary = CallStep()
or
returnStep(nodeFrom, nodeTo) and
summary = ReturnStep()
or
exists(string attr |
basicStoreStep(nodeFrom, nodeTo, attr) and
summary = StoreStep(attr)
or
basicLoadStep(nodeFrom, nodeTo, attr) and summary = LoadStep(attr)
)
}
}
/** Holds if `nodeFrom` steps to `nodeTo` by being passed as a parameter in a call. */
predicate callStep(ArgumentNode nodeFrom, ParameterNode nodeTo) {
// TODO: Support special methods?
exists(DataFlowCall call, int i |
nodeFrom.argumentOf(call, i) and nodeTo.isParameterOf(call.getCallable(), i)
)
}
/** Holds if `nodeFrom` steps to `nodeTo` by being returned from a call. */
predicate returnStep(ReturnNode nodeFrom, Node nodeTo) {
exists(DataFlowCall call |
nodeFrom.getEnclosingCallable() = call.getCallable() and nodeTo.asCfgNode() = call.getNode()
)
}
/**
* Holds if `nodeFrom` is being written to the `attr` attribute of the object in `nodeTo`.
*
* Note that the choice of `nodeTo` does not have to make sense "chronologically".
* All we care about is whether the `attr` attribute of `nodeTo` can have a specific type,
* and the assumption is that if a specific type appears here, then any access of that
* particular attribute can yield something of that particular type.
*
* Thus, in an example such as
*
* ```python
* def foo(y):
* x = Foo()
* bar(x)
* x.attr = y
* baz(x)
*
* def bar(x):
* z = x.attr
* ```
* for the attribute write `x.attr = y`, we will have `attr` being the literal string `"attr"`,
* `nodeFrom` will be `y`, and `nodeTo` will be the object `Foo()` created on the first line of the
* function. This means we will track the fact that `x.attr` can have the type of `y` into the
* assignment to `z` inside `bar`, even though this attribute write happens _after_ `bar` is called.
*/
predicate basicStoreStep(Node nodeFrom, Node nodeTo, string attr) {
exists(AttributeAssignment a, Node var |
a.getName() = attr and
EssaFlow::essaFlowStep*(nodeTo, var) and
var.asVar() = a.getInput() and
nodeFrom.asCfgNode() = a.getValue()
)
}
/**
* Holds if `nodeTo` is the result of accessing the `attr` attribute of `nodeFrom`.
*/
predicate basicLoadStep(Node nodeFrom, Node nodeTo, string attr) {
exists(AttrNode s | nodeTo.asCfgNode() = s and s.getObject(attr) = nodeFrom.asCfgNode())
}
/**
* A utility class that is equivalent to `boolean` but does not require type joining.
*/
private class Boolean extends boolean {
Boolean() { this = true or this = false }
}
private newtype TTypeTracker = MkTypeTracker(Boolean hasCall, OptionalAttributeName attr)
/**
* Summary of the steps needed to track a value to a given dataflow node.
*
* This can be used to track objects that implement a certain API in order to
* recognize calls to that API. Note that type-tracking does not by itself provide a
* source/sink relation, that is, it may determine that a node has a given type,
* but it won't determine where that type came from.
*
* It is recommended that all uses of this type are written in the following form,
* for tracking some type `myType`:
* ```
* Node myType(DataFlow::TypeTracker t) {
* t.start() and
* result = < source of myType >
* or
* exists (TypeTracker t2 |
* result = myType(t2).track(t2, t)
* )
* }
*
* DataFlow::SourceNode myType() { result = myType(DataFlow::TypeTracker::end()) }
* ```
*
* Instead of `result = myType(t2).track(t2, t)`, you can also use the equivalent
* `t = t2.step(myType(t2), result)`. If you additionally want to track individual
* intra-procedural steps, use `t = t2.smallstep(myCallback(t2), result)`.
*/
class TypeTracker extends TTypeTracker {
Boolean hasCall;
OptionalAttributeName attr;
TypeTracker() { this = MkTypeTracker(hasCall, attr) }
/** Gets the summary resulting from appending `step` to this type-tracking summary. */
cached
TypeTracker append(StepSummary step) {
step = LevelStep() and result = this
or
step = CallStep() and result = MkTypeTracker(true, attr)
or
step = ReturnStep() and hasCall = false and result = this
or
step = LoadStep(attr) and result = MkTypeTracker(hasCall, "")
or
exists(string p | step = StoreStep(p) and attr = "" and result = MkTypeTracker(hasCall, p))
}
/** Gets a textual representation of this summary. */
string toString() {
exists(string withCall, string withAttr |
(if hasCall = true then withCall = "with" else withCall = "without") and
(if attr != "" then withAttr = " with attribute " + attr else withAttr = "") and
result = "type tracker " + withCall + " call steps" + withAttr
)
}
/**
* Holds if this is the starting point of type tracking.
*/
predicate start() { hasCall = false and attr = "" }
/**
* Holds if this is the starting point of type tracking, and the value starts in the attribute named `attrName`.
* The type tracking only ends after the attribute has been loaded.
*/
predicate startInAttr(AttributeName attrName) { hasCall = false and attr = attrName }
/**
* Holds if this is the starting point of type tracking
* when tracking a parameter into a call, but not out of it.
*/
predicate call() { hasCall = true and attr = "" }
/**
* Holds if this is the end point of type tracking.
*/
predicate end() { attr = "" }
/**
* INTERNAL. DO NOT USE.
*
* Holds if this type has been tracked into a call.
*/
boolean hasCall() { result = hasCall }
/**
* INTERNAL. DO NOT USE.
*
* Gets the attribute associated with this type tracker.
*/
string getAttr() { result = attr }
/**
* Gets a type tracker that starts where this one has left off to allow continued
* tracking.
*
* This predicate is only defined if the type has not been tracked into an attribute.
*/
TypeTracker continue() { attr = "" and result = this }
/**
* Gets the summary that corresponds to having taken a forwards
* heap and/or inter-procedural step from `nodeFrom` to `nodeTo`.
*/
pragma[inline]
TypeTracker step(Node nodeFrom, Node nodeTo) {
exists(StepSummary summary |
StepSummary::step(nodeFrom, nodeTo, summary) and
result = this.append(summary)
)
}
/**
* Gets the summary that corresponds to having taken a forwards
* local, heap and/or inter-procedural step from `nodeFrom` to `nodeTo`.
*
* Unlike `TypeTracker::step`, this predicate exposes all edges
* in the flow graph, and not just the edges between `Node`s.
* It may therefore be less performant.
*
* Type tracking predicates using small steps typically take the following form:
* ```ql
* DataFlow::Node myType(DataFlow::TypeTracker t) {
* t.start() and
* result = < source of myType >
* or
* exists (DataFlow::TypeTracker t2 |
* t = t2.smallstep(myType(t2), result)
* )
* }
*
* DataFlow::Node myType() {
* result = myType(DataFlow::TypeTracker::end())
* }
* ```
*/
pragma[inline]
TypeTracker smallstep(Node nodeFrom, Node nodeTo) {
exists(StepSummary summary |
StepSummary::smallstep(nodeFrom, nodeTo, summary) and
result = this.append(summary)
)
or
EssaFlow::essaFlowStep(nodeFrom, nodeTo) and
result = this
}
}

View File

@@ -4,6 +4,7 @@
private import python
private import DataFlowPrivate
import experimental.dataflow.TypeTracker
/**
* IPA type for data flow nodes.
@@ -69,6 +70,14 @@ class Node extends TNode {
/** Convenience method for casting to ExprNode and calling getNode and getNode again. */
Expr asExpr() { none() }
/**
* Gets a node that this node may flow to using one heap and/or interprocedural step.
*
* See `TypeTracker` for more details about how to use this.
*/
pragma[inline]
Node track(TypeTracker t2, TypeTracker t) { t = t2.step(this, result) }
}
class EssaNode extends Node, TEssaNode {

View File

@@ -30,6 +30,16 @@ predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeT
subscriptStep(nodeFrom, nodeTo)
or
stringManipulation(nodeFrom, nodeTo)
or
jsonStep(nodeFrom, nodeTo)
or
containerStep(nodeFrom, nodeTo)
or
copyStep(nodeFrom, nodeTo)
or
forStep(nodeFrom, nodeTo)
or
unpackingAssignmentStep(nodeFrom, nodeTo)
}
/**
@@ -118,8 +128,101 @@ predicate stringManipulation(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeT
)
or
// f-strings
nodeTo.getNode().getNode().(Fstring).getAValue() = nodeFrom.getNode().getNode()
nodeTo.asExpr().(Fstring).getAValue() = nodeFrom.asExpr()
// TODO: Handle encode/decode from base64/quopri
// TODO: Handle os.path.join
// TODO: Handle functions in https://docs.python.org/3/library/binascii.html
}
/**
* Holds if taint can flow from `nodeFrom` to `nodeTo` with a step related to JSON encoding/decoding.
*/
predicate jsonStep(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeTo) {
exists(CallNode call | call = nodeTo.getNode() |
call.getFunction().(AttrNode).getObject(["load", "loads", "dumps"]).(NameNode).getId() = "json" and
call.getArg(0) = nodeFrom.getNode()
)
}
/**
* Holds if taint can flow from `nodeFrom` to `nodeTo` with a step related to containers
* (lists/sets/dictionaries): literals, constructor invocation, methods. Note that this
* is currently very imprecise, as an example, since we model `dict.get`, we treat any
* `<tainted object>.get(<arg>)` will be tainted, whether it's true or not.
*/
predicate containerStep(DataFlow::CfgNode nodeFrom, DataFlow::Node nodeTo) {
// construction by literal
// TODO: Not limiting the content argument here feels like a BIG hack, but we currently get nothing for free :|
storeStep(nodeFrom, _, nodeTo)
or
// constructor call
exists(CallNode call | call = nodeTo.asCfgNode() |
call.getFunction().(NameNode).getId() in ["list", "set", "frozenset", "dict", "defaultdict",
"tuple"] and
call.getArg(0) = nodeFrom.getNode()
)
or
// functions operating on collections
exists(CallNode call | call = nodeTo.asCfgNode() |
call.getFunction().(NameNode).getId() in ["sorted", "reversed", "iter", "next"] and
call.getArg(0) = nodeFrom.getNode()
)
or
// methods
exists(CallNode call, string name | call = nodeTo.asCfgNode() |
name in ["copy",
// general
"pop",
// dict
"values", "items", "get", "popitem"] and
call.getFunction().(AttrNode).getObject(name) = nodeFrom.asCfgNode()
)
or
// list.append, set.add
exists(CallNode call, string name |
name in ["append", "add"] and
call.getFunction().(AttrNode).getObject(name) =
nodeTo.(PostUpdateNode).getPreUpdateNode().asCfgNode() and
call.getArg(0) = nodeFrom.getNode()
)
}
/**
* Holds if taint can flow from `nodeFrom` to `nodeTo` with a step related to copying.
*/
predicate copyStep(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeTo) {
exists(CallNode call | call = nodeTo.getNode() |
// Fully qualified: copy.copy, copy.deepcopy
(
call.getFunction().(NameNode).getId() in ["copy", "deepcopy"]
or
call.getFunction().(AttrNode).getObject(["copy", "deepcopy"]).(NameNode).getId() = "copy"
) and
call.getArg(0) = nodeFrom.getNode()
)
}
/**
* Holds if taint can flow from `nodeFrom` to `nodeTo` with a step related to `for`-iteration,
* for example `for x in xs`, or `for x,y in points`.
*/
predicate forStep(DataFlow::CfgNode nodeFrom, DataFlow::EssaNode nodeTo) {
exists(EssaNodeDefinition defn, For for |
for.getTarget().getAChildNode*() = defn.getDefiningNode().getNode() and
nodeTo.getVar() = defn and
nodeFrom.asExpr() = for.getIter()
)
}
/**
* Holds if taint can flow from `nodeFrom` to `nodeTo` with a step related to iterable unpacking.
* Only handles normal assignment (`x,y = calc_point()`), since `for x,y in points` is handled by `forStep`.
*/
predicate unpackingAssignmentStep(DataFlow::CfgNode nodeFrom, DataFlow::EssaNode nodeTo) {
// `a, b = myiterable` or `head, *tail = myiterable` (only Python 3)
exists(MultiAssignmentDefinition defn, Assign assign |
assign.getATarget().contains(defn.getDefiningNode().getNode()) and
nodeTo.getVar() = defn and
nodeFrom.asExpr() = assign.getValue()
)
}

View File

@@ -6,7 +6,8 @@ class TestTaintTrackingConfiguration extends TaintTracking::Configuration {
TestTaintTrackingConfiguration() { this = "TestTaintTrackingConfiguration" }
override predicate isSource(DataFlow::Node source) {
source.(DataFlow::CfgNode).getNode().(NameNode).getId() in ["TAINTED_STRING", "TAINTED_BYTES"]
source.(DataFlow::CfgNode).getNode().(NameNode).getId() in ["TAINTED_STRING", "TAINTED_BYTES",
"TAINTED_LIST", "TAINTED_DICT"]
}
override predicate isSink(DataFlow::Node sink) {
@@ -44,7 +45,8 @@ private string repr(Expr e) {
query predicate test_taint(string arg_location, string test_res, string function_name, string repr) {
exists(Call call, Expr arg, boolean expected_taint, boolean has_taint |
call.getLocation().getFile().getShortName() = "test.py" and
// only consider files that are extracted as part of the test
exists(call.getLocation().getFile().getRelativePath()) and
(
call.getFunc().(Name).getId() = "ensure_tainted" and
expected_taint = true

View File

@@ -0,0 +1,22 @@
| test_collections.py:16 | ok | test_access | tainted_list.copy() |
| test_collections.py:24 | ok | list_clear | tainted_list |
| test_collections.py:27 | fail | list_clear | tainted_list |
| test_string.py:17 | ok | str_methods | ts.casefold() |
| test_string.py:19 | ok | str_methods | ts.format_map(..) |
| test_string.py:20 | ok | str_methods | "{unsafe}".format_map(..) |
| test_string.py:31 | fail | binary_decode_encode | base64.a85encode(..) |
| test_string.py:32 | fail | binary_decode_encode | base64.a85decode(..) |
| test_string.py:35 | fail | binary_decode_encode | base64.b85encode(..) |
| test_string.py:36 | fail | binary_decode_encode | base64.b85decode(..) |
| test_string.py:39 | fail | binary_decode_encode | base64.encodebytes(..) |
| test_string.py:40 | fail | binary_decode_encode | base64.decodebytes(..) |
| test_string.py:48 | ok | f_strings | Fstring |
| test_unpacking.py:18 | ok | extended_unpacking | first |
| test_unpacking.py:18 | ok | extended_unpacking | last |
| test_unpacking.py:18 | ok | extended_unpacking | rest |
| test_unpacking.py:23 | ok | also_allowed | a |
| test_unpacking.py:31 | ok | also_allowed | b |
| test_unpacking.py:31 | ok | also_allowed | c |
| test_unpacking.py:39 | ok | nested | x |
| test_unpacking.py:39 | ok | nested | xs |
| test_unpacking.py:39 | ok | nested | ys |

View File

@@ -0,0 +1,32 @@
# Add taintlib to PATH so it can be imported during runtime without any hassle
import sys; import os; sys.path.append(os.path.dirname(os.path.dirname((__file__))))
from taintlib import *
# This has no runtime impact, but allows autocomplete to work
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from ..taintlib import *
# Actual tests
def test_access():
tainted_list = TAINTED_LIST
ensure_tainted(
tainted_list.copy(),
)
def list_clear():
tainted_string = TAINTED_STRING
tainted_list = [tainted_string]
ensure_tainted(tainted_list)
tainted_list.clear()
ensure_not_tainted(tainted_list)
# Make tests runable
test_access()
list_clear()

View File

@@ -1,20 +1,11 @@
# Python 3 specific taint tracking for string
TAINTED_STRING = "TAINTED_STRING"
TAINTED_BYTES = b"TAINTED_BYTES"
def ensure_tainted(*args):
print("- ensure_tainted")
for i, arg in enumerate(args):
print("arg {}: {!r}".format(i, arg))
def ensure_not_tainted(*args):
print("- ensure_not_tainted")
for i, arg in enumerate(args):
print("arg {}: {!r}".format(i, arg))
# Add taintlib to PATH so it can be imported during runtime without any hassle
import sys; import os; sys.path.append(os.path.dirname(os.path.dirname((__file__))))
from taintlib import *
# This has no runtime impact, but allows autocomplete to work
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from ..taintlib import *
# Actual tests

View File

@@ -0,0 +1,46 @@
# Add taintlib to PATH so it can be imported during runtime without any hassle
import sys; import os; sys.path.append(os.path.dirname(os.path.dirname((__file__))))
from taintlib import *
# This has no runtime impact, but allows autocomplete to work
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from ..taintlib import *
# Actual tests
# Extended Iterable Unpacking -- PEP 3132
# https://www.python.org/dev/peps/pep-3132/
def extended_unpacking():
first, *rest, last = TAINTED_LIST
ensure_tainted(first, rest, last)
def also_allowed():
*a, = TAINTED_LIST
ensure_tainted(a)
# for b, *c in [(1, 2, 3), (4, 5, 6, 7)]:
# print(c)
# i=0; c=[2,3]
# i=1; c=[5,6,7]
for b, *c in [TAINTED_LIST, TAINTED_LIST]:
ensure_tainted(b, c)
def nested():
l = TAINTED_LIST
ll = [l,l]
[[x, *xs], ys] = ll
ensure_tainted(x, xs, ys)
# Make tests runable
extended_unpacking()
also_allowed()
nested()

View File

@@ -0,0 +1,172 @@
| test_collections.py:23 | ok | test_construction | tainted_string |
| test_collections.py:24 | ok | test_construction | tainted_list |
| test_collections.py:25 | ok | test_construction | tainted_tuple |
| test_collections.py:26 | ok | test_construction | tainted_set |
| test_collections.py:27 | ok | test_construction | tainted_dict |
| test_collections.py:31 | ok | test_construction | list(..) |
| test_collections.py:32 | ok | test_construction | list(..) |
| test_collections.py:33 | ok | test_construction | list(..) |
| test_collections.py:34 | ok | test_construction | list(..) |
| test_collections.py:35 | ok | test_construction | list(..) |
| test_collections.py:37 | ok | test_construction | tuple(..) |
| test_collections.py:38 | ok | test_construction | set(..) |
| test_collections.py:39 | ok | test_construction | frozenset(..) |
| test_collections.py:47 | ok | test_access | tainted_list[0] |
| test_collections.py:48 | ok | test_access | tainted_list[x] |
| test_collections.py:49 | ok | test_access | tainted_list[Slice] |
| test_collections.py:51 | ok | test_access | sorted(..) |
| test_collections.py:52 | ok | test_access | reversed(..) |
| test_collections.py:53 | ok | test_access | iter(..) |
| test_collections.py:54 | ok | test_access | next(..) |
| test_collections.py:58 | ok | test_access | a |
| test_collections.py:58 | ok | test_access | b |
| test_collections.py:58 | ok | test_access | c |
| test_collections.py:61 | ok | test_access | h |
| test_collections.py:63 | ok | test_access | i |
| test_collections.py:70 | ok | test_dict_access | tainted_dict["name"] |
| test_collections.py:71 | ok | test_dict_access | tainted_dict.get(..) |
| test_collections.py:72 | ok | test_dict_access | tainted_dict[x] |
| test_collections.py:73 | ok | test_dict_access | tainted_dict.copy() |
| test_collections.py:77 | ok | test_dict_access | v |
| test_collections.py:79 | ok | test_dict_access | v |
| test_collections.py:87 | fail | test_named_tuple | point[0] |
| test_collections.py:88 | fail | test_named_tuple | point.x |
| test_collections.py:92 | ok | test_named_tuple | point[1] |
| test_collections.py:93 | ok | test_named_tuple | point.y |
| test_collections.py:97 | fail | test_named_tuple | a |
| test_collections.py:98 | ok | test_named_tuple | b |
| test_collections.py:106 | fail | test_defaultdict | tainted_default_dict["name"] |
| test_collections.py:107 | fail | test_defaultdict | tainted_default_dict.get(..) |
| test_collections.py:108 | fail | test_defaultdict | tainted_default_dict[x] |
| test_collections.py:109 | fail | test_defaultdict | tainted_default_dict.copy() |
| test_collections.py:112 | fail | test_defaultdict | v |
| test_collections.py:114 | fail | test_defaultdict | v |
| test_collections.py:121 | ok | test_copy_1 | copy(..) |
| test_collections.py:122 | ok | test_copy_1 | deepcopy(..) |
| test_collections.py:130 | ok | test_copy_2 | copy.copy(..) |
| test_collections.py:131 | ok | test_copy_2 | copy.deepcopy(..) |
| test_collections.py:139 | ok | list_index_assign | my_list |
| test_collections.py:142 | fail | list_index_assign | my_list |
| test_collections.py:149 | ok | list_index_aug_assign | my_list |
| test_collections.py:152 | fail | list_index_aug_assign | my_list |
| test_collections.py:159 | ok | list_append | my_list |
| test_collections.py:162 | fail | list_append | my_list |
| test_collections.py:169 | ok | list_extend | my_list |
| test_collections.py:172 | fail | list_extend | my_list |
| test_collections.py:179 | ok | dict_update_dict | my_dict |
| test_collections.py:182 | fail | dict_update_dict | my_dict |
| test_collections.py:189 | ok | dict_update_kv_list | my_dict |
| test_collections.py:192 | fail | dict_update_kv_list | my_dict |
| test_collections.py:198 | ok | dict_update_kv_arg | my_dict |
| test_collections.py:201 | fail | dict_update_kv_arg | my_dict |
| test_collections.py:208 | ok | dict_manual_update | my_dict |
| test_collections.py:212 | fail | dict_manual_update | my_dict |
| test_collections.py:220 | fail | dict_merge | merged |
| test_collections.py:227 | ok | set_add | my_set |
| test_collections.py:230 | fail | set_add | my_set |
| test_json.py:26 | ok | test | json.dumps(..) |
| test_json.py:27 | ok | test | json.loads(..) |
| test_json.py:34 | fail | test | tainted_filelike |
| test_json.py:35 | fail | test | json.load(..) |
| test_json.py:48 | fail | non_syntacical | dumps(..) |
| test_json.py:49 | fail | non_syntacical | dumps_alias(..) |
| test_json.py:50 | fail | non_syntacical | loads(..) |
| test_json.py:57 | fail | non_syntacical | tainted_filelike |
| test_json.py:58 | fail | non_syntacical | load(..) |
| test_string.py:25 | ok | str_operations | ts |
| test_string.py:26 | ok | str_operations | BinaryExpr |
| test_string.py:27 | ok | str_operations | BinaryExpr |
| test_string.py:28 | ok | str_operations | BinaryExpr |
| test_string.py:29 | ok | str_operations | ts[Slice] |
| test_string.py:30 | ok | str_operations | ts[Slice] |
| test_string.py:31 | ok | str_operations | ts[Slice] |
| test_string.py:32 | ok | str_operations | ts[0] |
| test_string.py:33 | ok | str_operations | str(..) |
| test_string.py:34 | ok | str_operations | bytes(..) |
| test_string.py:35 | ok | str_operations | unicode(..) |
| test_string.py:39 | ok | str_operations | aug_assignment |
| test_string.py:41 | ok | str_operations | aug_assignment |
| test_string.py:49 | ok | str_methods | ts.capitalize() |
| test_string.py:50 | ok | str_methods | ts.center(..) |
| test_string.py:51 | ok | str_methods | ts.expandtabs() |
| test_string.py:53 | ok | str_methods | ts.format() |
| test_string.py:54 | ok | str_methods | "{}".format(..) |
| test_string.py:55 | ok | str_methods | "{unsafe}".format(..) |
| test_string.py:57 | ok | str_methods | ts.join(..) |
| test_string.py:58 | ok | str_methods | "".join(..) |
| test_string.py:60 | ok | str_methods | ts.ljust(..) |
| test_string.py:61 | ok | str_methods | ts.lstrip() |
| test_string.py:62 | ok | str_methods | ts.lower() |
| test_string.py:64 | ok | str_methods | ts.replace(..) |
| test_string.py:65 | ok | str_methods | "safe".replace(..) |
| test_string.py:67 | ok | str_methods | ts.rjust(..) |
| test_string.py:68 | ok | str_methods | ts.rstrip() |
| test_string.py:69 | ok | str_methods | ts.strip() |
| test_string.py:70 | ok | str_methods | ts.swapcase() |
| test_string.py:71 | ok | str_methods | ts.title() |
| test_string.py:72 | ok | str_methods | ts.upper() |
| test_string.py:73 | ok | str_methods | ts.zfill(..) |
| test_string.py:75 | ok | str_methods | ts.encode(..) |
| test_string.py:76 | ok | str_methods | ts.encode(..).decode(..) |
| test_string.py:78 | ok | str_methods | tb.decode(..) |
| test_string.py:79 | ok | str_methods | tb.decode(..).encode(..) |
| test_string.py:82 | ok | str_methods | ts.partition(..) |
| test_string.py:83 | ok | str_methods | ts.rpartition(..) |
| test_string.py:84 | ok | str_methods | ts.rsplit(..) |
| test_string.py:85 | ok | str_methods | ts.split(..) |
| test_string.py:86 | ok | str_methods | ts.splitlines() |
| test_string.py:91 | ok | str_methods | "safe".replace(..) |
| test_string.py:93 | fail | str_methods | ts.join(..) |
| test_string.py:94 | fail | str_methods | ts.join(..) |
| test_string.py:104 | fail | non_syntactic | meth() |
| test_string.py:105 | fail | non_syntactic | _str(..) |
| test_string.py:114 | ok | percent_fmt | BinaryExpr |
| test_string.py:115 | ok | percent_fmt | BinaryExpr |
| test_string.py:116 | ok | percent_fmt | BinaryExpr |
| test_string.py:126 | fail | binary_decode_encode | base64.b64encode(..) |
| test_string.py:127 | fail | binary_decode_encode | base64.b64decode(..) |
| test_string.py:129 | fail | binary_decode_encode | base64.standard_b64encode(..) |
| test_string.py:130 | fail | binary_decode_encode | base64.standard_b64decode(..) |
| test_string.py:132 | fail | binary_decode_encode | base64.urlsafe_b64encode(..) |
| test_string.py:133 | fail | binary_decode_encode | base64.urlsafe_b64decode(..) |
| test_string.py:135 | fail | binary_decode_encode | base64.b32encode(..) |
| test_string.py:136 | fail | binary_decode_encode | base64.b32decode(..) |
| test_string.py:138 | fail | binary_decode_encode | base64.b16encode(..) |
| test_string.py:139 | fail | binary_decode_encode | base64.b16decode(..) |
| test_string.py:142 | fail | binary_decode_encode | base64.encodestring(..) |
| test_string.py:143 | fail | binary_decode_encode | base64.decodestring(..) |
| test_string.py:148 | fail | binary_decode_encode | quopri.encodestring(..) |
| test_string.py:149 | fail | binary_decode_encode | quopri.decodestring(..) |
| test_unpacking.py:16 | ok | unpacking | a |
| test_unpacking.py:16 | ok | unpacking | b |
| test_unpacking.py:16 | ok | unpacking | c |
| test_unpacking.py:22 | ok | unpacking_to_list | a |
| test_unpacking.py:22 | ok | unpacking_to_list | b |
| test_unpacking.py:22 | ok | unpacking_to_list | c |
| test_unpacking.py:31 | ok | nested | a1 |
| test_unpacking.py:31 | ok | nested | a2 |
| test_unpacking.py:31 | ok | nested | a3 |
| test_unpacking.py:31 | ok | nested | b |
| test_unpacking.py:31 | ok | nested | c |
| test_unpacking.py:35 | ok | nested | a1 |
| test_unpacking.py:35 | ok | nested | a2 |
| test_unpacking.py:35 | ok | nested | a3 |
| test_unpacking.py:35 | ok | nested | b |
| test_unpacking.py:35 | ok | nested | c |
| test_unpacking.py:39 | ok | nested | a1 |
| test_unpacking.py:39 | ok | nested | a2 |
| test_unpacking.py:39 | ok | nested | a3 |
| test_unpacking.py:39 | ok | nested | b |
| test_unpacking.py:39 | ok | nested | c |
| test_unpacking.py:46 | ok | unpack_from_set | a |
| test_unpacking.py:46 | ok | unpack_from_set | b |
| test_unpacking.py:46 | ok | unpack_from_set | c |
| test_unpacking.py:55 | ok | contrived_1 | a |
| test_unpacking.py:55 | ok | contrived_1 | b |
| test_unpacking.py:55 | ok | contrived_1 | c |
| test_unpacking.py:56 | fail | contrived_1 | d |
| test_unpacking.py:56 | fail | contrived_1 | e |
| test_unpacking.py:56 | fail | contrived_1 | f |
| test_unpacking.py:65 | ok | contrived_2 | a |
| test_unpacking.py:65 | ok | contrived_2 | b |
| test_unpacking.py:65 | ok | contrived_2 | c |

View File

@@ -0,0 +1,254 @@
# Add taintlib to PATH so it can be imported during runtime without any hassle
import sys; import os; sys.path.append(os.path.dirname(os.path.dirname((__file__))))
from taintlib import *
# This has no runtime impact, but allows autocomplete to work
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from ..taintlib import *
# Actual tests
from collections import defaultdict, namedtuple
def test_construction():
tainted_string = TAINTED_STRING
tainted_list = [tainted_string]
tainted_tuple = (tainted_string,)
tainted_set = {tainted_string}
tainted_dict = {'key': tainted_string}
ensure_tainted(
tainted_string,
tainted_list,
tainted_tuple,
tainted_set,
tainted_dict,
)
ensure_tainted(
list(tainted_list),
list(tainted_tuple),
list(tainted_set),
list(tainted_dict.values()),
list(tainted_dict.items()),
tuple(tainted_list),
set(tainted_list),
frozenset(tainted_list),
)
def test_access(x, y, z):
tainted_list = TAINTED_LIST
ensure_tainted(
tainted_list[0],
tainted_list[x],
tainted_list[y:z],
sorted(tainted_list),
reversed(tainted_list),
iter(tainted_list),
next(iter(tainted_list)),
)
a, b, c = tainted_list[0:3]
ensure_tainted(a, b, c)
for h in tainted_list:
ensure_tainted(h)
for i in reversed(tainted_list):
ensure_tainted(i)
def test_dict_access(x):
tainted_dict = TAINTED_DICT
ensure_tainted(
tainted_dict["name"],
tainted_dict.get("name"),
tainted_dict[x],
tainted_dict.copy(),
)
for v in tainted_dict.values():
ensure_tainted(v)
for k, v in tainted_dict.items():
ensure_tainted(v)
def test_named_tuple(): # TODO: namedtuple currently not handled
Point = namedtuple('Point', ['x', 'y'])
point = Point(TAINTED_STRING, 'safe')
ensure_tainted(
point[0],
point.x,
)
ensure_not_tainted(
point[1],
point.y,
)
a, b = point
ensure_tainted(a)
ensure_not_tainted(b)
def test_defaultdict(key, x): # TODO: defaultdict currently not handled
tainted_default_dict = defaultdict(str)
tainted_default_dict[key] += TAINTED_STRING
ensure_tainted(
tainted_default_dict["name"],
tainted_default_dict.get("name"),
tainted_default_dict[x],
tainted_default_dict.copy(),
)
for v in tainted_default_dict.values():
ensure_tainted(v)
for k, v in tainted_default_dict.items():
ensure_tainted(v)
def test_copy_1():
from copy import copy, deepcopy
ensure_tainted(
copy(TAINTED_LIST),
deepcopy(TAINTED_LIST),
)
def test_copy_2():
import copy
ensure_tainted(
copy.copy(TAINTED_LIST),
copy.deepcopy(TAINTED_LIST),
)
def list_index_assign():
tainted_string = TAINTED_STRING
my_list = ["safe"]
ensure_not_tainted(my_list)
my_list[0] = tainted_string
ensure_tainted(my_list)
def list_index_aug_assign():
tainted_string = TAINTED_STRING
my_list = ["safe"]
ensure_not_tainted(my_list)
my_list[0] += tainted_string
ensure_tainted(my_list)
def list_append():
tainted_string = TAINTED_STRING
my_list = ["safe"]
ensure_not_tainted(my_list)
my_list.append(tainted_string)
ensure_tainted(my_list)
def list_extend():
my_list = ["safe"]
tainted_list = [TAINTED_STRING]
ensure_not_tainted(my_list)
my_list.extend(tainted_list)
ensure_tainted(my_list)
def dict_update_dict():
my_dict = {"key1": "safe"}
tainted_dict = {"key2": TAINTED_STRING}
ensure_not_tainted(my_dict)
my_dict.update(tainted_dict)
ensure_tainted(my_dict)
def dict_update_kv_list():
my_dict = {"key1": "safe"}
tainted_kv_list = [("key2", TAINTED_STRING)]
ensure_not_tainted(my_dict)
my_dict.update(tainted_kv_list)
ensure_tainted(my_dict)
def dict_update_kv_arg():
my_dict = {"key1": "safe"}
ensure_not_tainted(my_dict)
my_dict.update(key2=TAINTED_STRING)
ensure_tainted(my_dict)
def dict_manual_update():
my_dict = {"key1": "safe"}
tainted_dict = {"key2": TAINTED_STRING}
ensure_not_tainted(my_dict)
for k in tainted_dict:
my_dict[k] = tainted_dict[k]
ensure_tainted(my_dict)
def dict_merge():
my_dict = {"key1": "safe"}
tainted_dict = {"key2": TAINTED_STRING}
merged = {**my_dict, **tainted_dict}
ensure_tainted(merged)
def set_add():
tainted_string = TAINTED_STRING
my_set = {"safe"}
ensure_not_tainted(my_set)
my_set.add(tainted_string)
ensure_tainted(my_set)
# Make tests runable
test_construction()
test_access(0, 0, 2)
test_dict_access("name")
test_named_tuple()
test_defaultdict("key", "key")
test_copy_1()
test_copy_2()
list_index_assign()
list_index_aug_assign()
list_append()
list_extend()
dict_update_dict()
dict_update_kv_list()
dict_update_kv_arg()
dict_manual_update()
dict_merge()
set_add()

View File

@@ -0,0 +1,64 @@
# Add taintlib to PATH so it can be imported during runtime without any hassle
import sys; import os; sys.path.append(os.path.dirname(os.path.dirname((__file__))))
from taintlib import *
# This has no runtime impact, but allows autocomplete to work
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from ..taintlib import *
# Actual tests
from io import StringIO
# Workaround for Python3 not having unicode
import sys
if sys.version_info[0] == 3:
unicode = str
def test():
print("\n# test")
ts = TAINTED_STRING
import json
ensure_tainted(
json.dumps(ts),
json.loads(json.dumps(ts)),
)
# For Python2, need to convert to unicode for StringIO to work
tainted_filelike = StringIO(unicode(json.dumps(ts)))
ensure_tainted(
tainted_filelike,
json.load(tainted_filelike),
)
def non_syntacical():
print("\n# non_syntacical")
ts = TAINTED_STRING
# a less syntactical approach
from json import load, loads, dumps
dumps_alias = dumps
ensure_tainted(
dumps(ts),
dumps_alias(ts),
loads(dumps(ts)),
)
# For Python2, need to convert to unicode for StringIO to work
tainted_filelike = StringIO(unicode(dumps(ts)))
ensure_tainted(
tainted_filelike,
load(tainted_filelike),
)
# Make tests runable
test()
non_syntacical()

View File

@@ -1,27 +1,20 @@
import sys
# Add taintlib to PATH so it can be imported during runtime without any hassle
import sys; import os; sys.path.append(os.path.dirname(os.path.dirname((__file__))))
from taintlib import *
if sys.version_info[0] == 3:
unicode = str
TAINTED_STRING = "TAINTED_STRING"
TAINTED_BYTES = b"TAINTED_BYTES"
def ensure_tainted(*args):
print("- ensure_tainted")
for i, arg in enumerate(args):
print("arg {}: {!r}".format(i, arg))
def ensure_not_tainted(*args):
print("- ensure_not_tainted")
for i, arg in enumerate(args):
print("arg {}: {!r}".format(i, arg))
# This has no runtime impact, but allows autocomplete to work
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from ..taintlib import *
# Actual tests
# Workaround for Python3 not having unicode
import sys
if sys.version_info[0] == 3:
unicode = str
def str_operations():
print("\n# str_operations")
@@ -42,6 +35,11 @@ def str_operations():
unicode(ts),
)
aug_assignment = "safe"
ensure_not_tainted(aug_assignment)
aug_assignment += TAINTED_STRING
ensure_tainted(aug_assignment)
def str_methods():
print("\n# str_methods")
@@ -140,18 +138,6 @@ def binary_decode_encode():
base64.b16encode(tb),
base64.b16decode(base64.b16encode(tb)),
# # New in Python 3.4
# base64.a85encode(tb),
# base64.a85decode(base64.a85encode(tb)),
# # New in Python 3.4
# base64.b85encode(tb),
# base64.b85decode(base64.b85encode(tb)),
# # New in Python 3.1
# base64.encodebytes(tb),
# base64.decodebytes(base64.encodebytes(tb)),
# deprecated since Python 3.1, but still works
base64.encodestring(tb),
base64.decodestring(base64.encodestring(tb)),

View File

@@ -0,0 +1,75 @@
# Add taintlib to PATH so it can be imported during runtime without any hassle
import sys; import os; sys.path.append(os.path.dirname(os.path.dirname((__file__))))
from taintlib import *
# This has no runtime impact, but allows autocomplete to work
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from ..taintlib import *
# Actual tests
def unpacking():
l = TAINTED_LIST[0:3]
a, b, c = l
ensure_tainted(a, b, c)
def unpacking_to_list():
l = TAINTED_LIST[0:3]
[a, b, c] = l
ensure_tainted(a, b, c)
def nested():
l = TAINTED_LIST[0:3]
ll = [l, l, l]
# list
[[a1, a2, a3], b, c] = ll
ensure_tainted(a1, a2, a3, b, c)
# tuple
((a1, a2, a3), b, c) = ll
ensure_tainted(a1, a2, a3, b, c)
# mixed
[(a1, a2, a3), b, c] = ll
ensure_tainted(a1, a2, a3, b, c)
def unpack_from_set():
# no guarantee on ordering ... don't know why you would ever do this
a, b, c = {"foo", "bar", TAINTED_STRING}
# either all should be tainted, or none of them
ensure_tainted(a, b, c)
def contrived_1():
# A contrived example. Don't know why anyone would ever actually do this.
tainted_list = TAINTED_LIST[0:3]
no_taint_list = [1,2,3]
(a, b, c), (d, e, f) = tainted_list, no_taint_list
ensure_tainted(a, b, c)
ensure_not_tainted(d, e, f) # FP: we mark `d`, `e` and `f` as tainted.
def contrived_2():
# A contrived example. Don't know why anyone would ever actually do this.
# Old taint tracking was only able to handle taint nested 2 levels in sequences,
# so would not mark a, b, c as tainted
[[[ (a, b, c) ]]] = [[[ TAINTED_LIST[0:3] ]]]
ensure_tainted(a, b, c)
# Make tests runable
unpacking()
unpacking_to_list()
nested()
unpack_from_set()
contrived_1()
contrived_2()

View File

@@ -1,10 +0,0 @@
| test.py:26 | ok | str_methods | ts.casefold() |
| test.py:28 | ok | str_methods | ts.format_map(..) |
| test.py:29 | fail | str_methods | "{unsafe}".format_map(..) |
| test.py:40 | fail | binary_decode_encode | base64.a85encode(..) |
| test.py:41 | fail | binary_decode_encode | base64.a85decode(..) |
| test.py:44 | fail | binary_decode_encode | base64.b85encode(..) |
| test.py:45 | fail | binary_decode_encode | base64.b85decode(..) |
| test.py:48 | fail | binary_decode_encode | base64.encodebytes(..) |
| test.py:49 | fail | binary_decode_encode | base64.decodebytes(..) |
| test.py:57 | ok | f_strings | Fstring |

View File

@@ -1,62 +0,0 @@
| test.py:32 | ok | str_operations | ts |
| test.py:33 | ok | str_operations | BinaryExpr |
| test.py:34 | ok | str_operations | BinaryExpr |
| test.py:35 | ok | str_operations | BinaryExpr |
| test.py:36 | ok | str_operations | ts[Slice] |
| test.py:37 | ok | str_operations | ts[Slice] |
| test.py:38 | ok | str_operations | ts[Slice] |
| test.py:39 | ok | str_operations | ts[0] |
| test.py:40 | ok | str_operations | str(..) |
| test.py:41 | ok | str_operations | bytes(..) |
| test.py:42 | ok | str_operations | unicode(..) |
| test.py:51 | ok | str_methods | ts.capitalize() |
| test.py:52 | ok | str_methods | ts.center(..) |
| test.py:53 | ok | str_methods | ts.expandtabs() |
| test.py:55 | ok | str_methods | ts.format() |
| test.py:56 | ok | str_methods | "{}".format(..) |
| test.py:57 | ok | str_methods | "{unsafe}".format(..) |
| test.py:59 | ok | str_methods | ts.join(..) |
| test.py:60 | fail | str_methods | "".join(..) |
| test.py:62 | ok | str_methods | ts.ljust(..) |
| test.py:63 | ok | str_methods | ts.lstrip() |
| test.py:64 | ok | str_methods | ts.lower() |
| test.py:66 | ok | str_methods | ts.replace(..) |
| test.py:67 | ok | str_methods | "safe".replace(..) |
| test.py:69 | ok | str_methods | ts.rjust(..) |
| test.py:70 | ok | str_methods | ts.rstrip() |
| test.py:71 | ok | str_methods | ts.strip() |
| test.py:72 | ok | str_methods | ts.swapcase() |
| test.py:73 | ok | str_methods | ts.title() |
| test.py:74 | ok | str_methods | ts.upper() |
| test.py:75 | ok | str_methods | ts.zfill(..) |
| test.py:77 | ok | str_methods | ts.encode(..) |
| test.py:78 | ok | str_methods | ts.encode(..).decode(..) |
| test.py:80 | ok | str_methods | tb.decode(..) |
| test.py:81 | ok | str_methods | tb.decode(..).encode(..) |
| test.py:84 | ok | str_methods | ts.partition(..) |
| test.py:85 | ok | str_methods | ts.rpartition(..) |
| test.py:86 | ok | str_methods | ts.rsplit(..) |
| test.py:87 | ok | str_methods | ts.split(..) |
| test.py:88 | ok | str_methods | ts.splitlines() |
| test.py:93 | ok | str_methods | "safe".replace(..) |
| test.py:95 | fail | str_methods | ts.join(..) |
| test.py:96 | fail | str_methods | ts.join(..) |
| test.py:106 | fail | non_syntactic | meth() |
| test.py:107 | fail | non_syntactic | _str(..) |
| test.py:116 | ok | percent_fmt | BinaryExpr |
| test.py:117 | ok | percent_fmt | BinaryExpr |
| test.py:118 | fail | percent_fmt | BinaryExpr |
| test.py:128 | fail | binary_decode_encode | base64.b64encode(..) |
| test.py:129 | fail | binary_decode_encode | base64.b64decode(..) |
| test.py:131 | fail | binary_decode_encode | base64.standard_b64encode(..) |
| test.py:132 | fail | binary_decode_encode | base64.standard_b64decode(..) |
| test.py:134 | fail | binary_decode_encode | base64.urlsafe_b64encode(..) |
| test.py:135 | fail | binary_decode_encode | base64.urlsafe_b64decode(..) |
| test.py:137 | fail | binary_decode_encode | base64.b32encode(..) |
| test.py:138 | fail | binary_decode_encode | base64.b32decode(..) |
| test.py:140 | fail | binary_decode_encode | base64.b16encode(..) |
| test.py:141 | fail | binary_decode_encode | base64.b16decode(..) |
| test.py:156 | fail | binary_decode_encode | base64.encodestring(..) |
| test.py:157 | fail | binary_decode_encode | base64.decodestring(..) |
| test.py:162 | fail | binary_decode_encode | quopri.encodestring(..) |
| test.py:163 | fail | binary_decode_encode | quopri.decodestring(..) |

View File

@@ -0,0 +1,15 @@
TAINTED_STRING = "TAINTED_STRING"
TAINTED_BYTES = b"TAINTED_BYTES"
TAINTED_LIST = ["tainted-{}".format(i) for i in range(5)]
TAINTED_DICT = {"name": TAINTED_STRING, "some key": "foo"}
def ensure_tainted(*args):
print("- ensure_tainted")
for i, arg in enumerate(args):
print("arg {}: {!r}".format(i, arg))
def ensure_not_tainted(*args):
print("- ensure_not_tainted")
for i, arg in enumerate(args):
print("arg {}: {!r}".format(i, arg))

View File

@@ -0,0 +1,31 @@
class SomeClass:
pass
def simple_read_write():
x = SomeClass() # $tracked=foo
x.foo = tracked # $tracked $tracked=foo
y = x.foo # $tracked=foo $tracked
do_stuff(y) # $tracked
def foo():
x = SomeClass() # $tracked=attr
bar(x) # $tracked=attr
x.attr = tracked # $tracked=attr $tracked
baz(x) # $tracked=attr
def bar(x): # $tracked=attr
z = x.attr # $tracked $tracked=attr
do_stuff(z) # $tracked
def expects_int(x): # $int=field $f+:str=field
do_int_stuff(x.field) # $int $f+:str $int=field $f+:str=field
def expects_string(x): # $f+:int=field $str=field
do_string_stuff(x.field) # $f+:int $str $f+:int=field $str=field
def test_incompatible_types():
x = SomeClass() # $int,str=field
x.field = int(5) # $int=field $f+:str=field $int $f+:str
expects_int(x) # $int=field $f+:str=field
x.field = str("Hello") # $f+:int=field $str=field $f+:int $str
expects_string(x) # $f+:int=field $str=field

View File

@@ -0,0 +1,61 @@
def get_tracked():
x = tracked # $tracked
return x # $tracked
def use_tracked_foo(x): # $tracked
do_stuff(x) # $tracked
def foo():
use_tracked_foo(
get_tracked() # $tracked
)
def use_tracked_bar(x): # $tracked
do_stuff(x) # $tracked
def bar():
x = get_tracked() # $tracked
use_tracked_bar(x) # $tracked
def use_tracked_baz(x): # $tracked
do_stuff(x) # $tracked
def baz():
x = tracked # $tracked
use_tracked_baz(x) # $tracked
def id(x): # $tracked
return x # $tracked
def use_tracked_quux(x): # $f-:tracked
do_stuff(y) # call after return -- not tracked in here.
def quux():
x = tracked # $tracked
y = id(x) # $tracked
use_tracked_quux(y) # not tracked out of call to id.
g = None
def write_g(x): # $tracked
g = x # $tracked
def use_g():
do_stuff(g) # $f-:tracked // no global flow for now.
def global_var_write_test():
x = tracked # $tracked
write_g(x) # $tracked
use_g()
def expects_int(x): # $int
do_int_stuff(x) # $int
def expects_string(x): # $str
do_string_stuff(x) # $str
def redefine_test():
x = int(5) # $int
expects_int(x) # $int
x = str("Hello") # $str
expects_string(x) # $str

View File

@@ -0,0 +1,72 @@
import python
import experimental.dataflow.TypeTracker
import TestUtilities.InlineExpectationsTest
Node tracked(TypeTracker t) {
t.start() and
result.asCfgNode() = any(NameNode n | n.getId() = "tracked")
or
exists(TypeTracker t2 | result = tracked(t2).track(t2, t))
}
class TrackedTest extends InlineExpectationsTest {
TrackedTest() { this = "TrackedTest" }
override string getARelevantTag() { result = "tracked" }
override predicate hasActualResult(Location location, string element, string tag, string value) {
exists(Node e, TypeTracker t |
e = tracked(t) and
tag = "tracked" and
location = e.getLocation() and
value = t.getAttr() and
element = e.toString()
)
}
}
Node int_type(TypeTracker t) {
t.start() and
result.asCfgNode() = any(CallNode c | c.getFunction().(NameNode).getId() = "int")
or
exists(TypeTracker t2 | result = int_type(t2).track(t2, t))
}
Node string_type(TypeTracker t) {
t.start() and
result.asCfgNode() = any(CallNode c | c.getFunction().(NameNode).getId() = "str")
or
exists(TypeTracker t2 | result = string_type(t2).track(t2, t))
}
class TrackedIntTest extends InlineExpectationsTest {
TrackedIntTest() { this = "TrackedIntTest" }
override string getARelevantTag() { result = "int" }
override predicate hasActualResult(Location location, string element, string tag, string value) {
exists(Node e, TypeTracker t |
e = int_type(t) and
tag = "int" and
location = e.getLocation() and
value = t.getAttr() and
element = e.toString()
)
}
}
class TrackedStringTest extends InlineExpectationsTest {
TrackedStringTest() { this = "TrackedStringTest" }
override string getARelevantTag() { result = "str" }
override predicate hasActualResult(Location location, string element, string tag, string value) {
exists(Node e, TypeTracker t |
e = string_type(t) and
tag = "str" and
location = e.getLocation() and
value = t.getAttr() and
element = e.toString()
)
}
}