Merge remote-tracking branch 'upstream/rc/1.20' into csharp/dataflow/performance

This commit is contained in:
Tom Hvitved
2019-03-10 14:40:35 +01:00
62 changed files with 1856 additions and 310 deletions

View File

@@ -30,5 +30,9 @@
* The class `TrivialProperty` now includes library properties determined to be trivial using CIL analysis. This may increase the number of results for all queries that use data flow.
* Taint-tracking steps have been added for the `Json.NET` package. This will improve results for queries that use taint-tracking.
* Support has been added for EntityFrameworkCore, including
- Stored data flow sources
- Sinks for SQL expressions
- Data flow through fields that are mapped to the database.
## Changes to the autobuilder

View File

@@ -8,7 +8,7 @@
</overview>
<recommendation>
<p>Consider changing the surrounding expression to match the floating point type. If rounding is intended, explicitly round using a standard function such as `trunc`, `floor` or `round`.</p>
<p>Consider changing the surrounding expression to match the floating point type. If rounding is intended, explicitly round using a standard function such as <code>trunc</code>, <code>floor</code> or <code>round</code>.</p>
</recommendation>
<example><sample src="LossyFunctionResultCast.cpp" />

View File

@@ -8,9 +8,9 @@
<p>A function is called with arguments despite having an empty parameter list. This may indicate
that the incorrect function is being called, or that the author misunderstood the function.</p>
<p>In C, a function declared with an empty parameter list `()` is considered to have an unknown
<p>In C, a function declared with an empty parameter list <code>()</code> is considered to have an unknown
parameter list, and therefore can be called with any set of arguments. To declare a function
which takes no arguments, you must use `(void)` as the parameter list in any forward declarations.
which takes no arguments, you must use <code>(void)</code> as the parameter list in any forward declarations.
In C++, either style of declaration indicates that the function accepts no arguments.</p>
</overview>

View File

@@ -1310,6 +1310,13 @@ class CallInstruction extends Instruction {
result = getAnOperand()
}
/**
* Gets the `Function` that the call targets, if this is statically known.
*/
final Function getStaticCallTarget() {
result = getCallTarget().(FunctionInstruction).getFunctionSymbol()
}
/**
* Gets all of the arguments of the call, including the `this` pointer, if any.
*/

View File

@@ -3,6 +3,8 @@ import cpp
private import InputIR
private import semmle.code.cpp.ir.internal.IntegerConstant as Ints
private import semmle.code.cpp.models.interfaces.Alias
private class IntValue = Ints::IntValue;
/**
@@ -46,7 +48,7 @@ private IntValue getFieldBitOffset(Field field) {
* not result in any address held in that operand from escaping beyond the
* instruction.
*/
predicate operandIsConsumedWithoutEscaping(Operand operand) {
private predicate operandIsConsumedWithoutEscaping(Operand operand) {
// The source/destination address of a Load/Store does not escape (but the
// loaded/stored value could).
operand instanceof AddressOperand or
@@ -60,7 +62,18 @@ predicate operandIsConsumedWithoutEscaping(Operand operand) {
// Converting an address to a `bool` does not escape the address.
instr.(ConvertInstruction).getResultType() instanceof BoolType
)
)
) or
// Some standard function arguments never escape
isNeverEscapesArgument(operand)
}
private predicate operandEscapesDomain(Operand operand) {
not operandIsConsumedWithoutEscaping(operand) and
not operandIsPropagated(operand, _) and
not isArgumentForParameter(_, operand, _) and
not isOnlyEscapesViaReturnArgument(operand) and
not operand.getUseInstruction() instanceof ReturnValueInstruction and
not operand instanceof PhiOperand
}
/**
@@ -98,7 +111,7 @@ IntValue getPointerBitOffset(PointerOffsetInstruction instr) {
* `bitOffset`. If the address is propagated, but the offset is not known to be
* a constant, then `bitOffset` is unknown.
*/
predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
private predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
exists(Instruction instr |
instr = operand.getUseInstruction() and
(
@@ -134,34 +147,134 @@ predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
// offset of the field.
bitOffset = getFieldBitOffset(instr.(FieldAddressInstruction).getField()) or
// A copy propagates the source value.
operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0
operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0 or
// Some functions are known to propagate an argument
isAlwaysReturnedArgument(operand) and bitOffset = 0
)
)
}
/**
* Holds if any address held in operand number `tag` of instruction `instr`
* escapes outside the domain of the analysis.
*/
predicate operandEscapes(Operand operand) {
// Conservatively assume that the address escapes unless one of the following
// holds:
not (
// The operand is used in a way that does not escape the instruction
operandIsConsumedWithoutEscaping(operand) or
// The address is propagated to the result of the instruction, but that
// result does not itself escape.
operandIsPropagated(operand, _) and not resultEscapes(operand.getUseInstruction())
private predicate operandEscapesNonReturn(Operand operand) {
// The address is propagated to the result of the instruction, and that result itself is returned
operandIsPropagated(operand, _) and resultEscapesNonReturn(operand.getUseInstruction())
or
// The operand is used in a function call which returns it, and the return value is then returned
exists(CallInstruction ci, Instruction init |
isArgumentForParameter(ci, operand, init) and
(
resultMayReachReturn(init) and
resultEscapesNonReturn(ci)
or
resultEscapesNonReturn(init)
)
)
or
isOnlyEscapesViaReturnArgument(operand) and resultEscapesNonReturn(operand.getUseInstruction())
or
operand instanceof PhiOperand and
resultEscapesNonReturn(operand.getUseInstruction())
or
operandEscapesDomain(operand)
}
private predicate operandMayReachReturn(Operand operand) {
// The address is propagated to the result of the instruction, and that result itself is returned
operandIsPropagated(operand, _) and
resultMayReachReturn(operand.getUseInstruction())
or
// The operand is used in a function call which returns it, and the return value is then returned
exists(CallInstruction ci, Instruction init |
isArgumentForParameter(ci, operand, init) and
resultMayReachReturn(init) and
resultMayReachReturn(ci)
)
or
// The address is returned
operand.getUseInstruction() instanceof ReturnValueInstruction
or
isOnlyEscapesViaReturnArgument(operand) and resultMayReachReturn(operand.getUseInstruction())
or
operand instanceof PhiOperand and
resultMayReachReturn(operand.getUseInstruction())
}
private predicate operandReturned(Operand operand, IntValue bitOffset) {
// The address is propagated to the result of the instruction, and that result itself is returned
exists(IntValue bitOffset1, IntValue bitOffset2 |
operandIsPropagated(operand, bitOffset1) and
resultReturned(operand.getUseInstruction(), bitOffset2) and
bitOffset = Ints::add(bitOffset1, bitOffset2)
)
or
// The operand is used in a function call which returns it, and the return value is then returned
exists(CallInstruction ci, Instruction init, IntValue bitOffset1, IntValue bitOffset2 |
isArgumentForParameter(ci, operand, init) and
resultReturned(init, bitOffset1) and
resultReturned(ci, bitOffset2) and
bitOffset = Ints::add(bitOffset1, bitOffset2)
)
or
// The address is returned
operand.getUseInstruction() instanceof ReturnValueInstruction and
bitOffset = 0
or
isOnlyEscapesViaReturnArgument(operand) and resultReturned(operand.getUseInstruction(), _) and
bitOffset = Ints::unknown()
}
private predicate isArgumentForParameter(CallInstruction ci, Operand operand, Instruction init) {
exists(Function f |
ci = operand.getUseInstruction() and
f = ci.getStaticCallTarget() and
(
init.(InitializeParameterInstruction).getParameter() = f.getParameter(operand.(PositionalArgumentOperand).getIndex())
or
init instanceof InitializeThisInstruction and
init.getEnclosingFunction() = f and
operand instanceof ThisArgumentOperand
) and
not f.isVirtual() and
not f instanceof AliasFunction
)
}
private predicate isAlwaysReturnedArgument(Operand operand) {
exists(AliasFunction f |
f = operand.getUseInstruction().(CallInstruction).getStaticCallTarget() and
f.parameterIsAlwaysReturned(operand.(PositionalArgumentOperand).getIndex())
)
}
private predicate isOnlyEscapesViaReturnArgument(Operand operand) {
exists(AliasFunction f |
f = operand.getUseInstruction().(CallInstruction).getStaticCallTarget() and
f.parameterEscapesOnlyViaReturn(operand.(PositionalArgumentOperand).getIndex())
)
}
private predicate isNeverEscapesArgument(Operand operand) {
exists(AliasFunction f |
f = operand.getUseInstruction().(CallInstruction).getStaticCallTarget() and
f.parameterNeverEscapes(operand.(PositionalArgumentOperand).getIndex())
)
}
private predicate resultReturned(Instruction instr, IntValue bitOffset) {
operandReturned(instr.getAUse(), bitOffset)
}
private predicate resultMayReachReturn(Instruction instr) {
operandMayReachReturn(instr.getAUse())
}
/**
* Holds if any address held in the result of instruction `instr` escapes
* outside the domain of the analysis.
*/
predicate resultEscapes(Instruction instr) {
private predicate resultEscapesNonReturn(Instruction instr) {
// The result escapes if it has at least one use that escapes.
operandEscapes(instr.getAUse())
operandEscapesNonReturn(instr.getAUse())
}
/**
@@ -169,15 +282,11 @@ predicate resultEscapes(Instruction instr) {
* domain of the analysis.
*/
private predicate automaticVariableAddressEscapes(IRAutomaticVariable var) {
exists(FunctionIR funcIR |
funcIR = var.getEnclosingFunctionIR() and
// The variable's address escapes if the result of any
// VariableAddressInstruction that computes the variable's address escapes.
exists(VariableAddressInstruction instr |
instr.getEnclosingFunctionIR() = funcIR and
instr.getVariable() = var and
resultEscapes(instr)
)
// The variable's address escapes if the result of any
// VariableAddressInstruction that computes the variable's address escapes.
exists(VariableAddressInstruction instr |
instr.getVariable() = var and
resultEscapesNonReturn(instr)
)
}
@@ -207,7 +316,14 @@ predicate resultPointsTo(Instruction instr, IRVariable var, IntValue bitOffset)
// If an operand is propagated, then the result points to the same variable,
// offset by the bit offset from the propagation.
resultPointsTo(operand.getDefinitionInstruction(), var, originalBitOffset) and
operandIsPropagated(operand, propagatedBitOffset) and
(
operandIsPropagated(operand, propagatedBitOffset)
or
exists(CallInstruction ci, Instruction init |
isArgumentForParameter(ci, operand, init) and
resultReturned(init, propagatedBitOffset)
)
) and
bitOffset = Ints::add(originalBitOffset, propagatedBitOffset)
)
}

View File

@@ -1310,6 +1310,13 @@ class CallInstruction extends Instruction {
result = getAnOperand()
}
/**
* Gets the `Function` that the call targets, if this is statically known.
*/
final Function getStaticCallTarget() {
result = getCallTarget().(FunctionInstruction).getFunctionSymbol()
}
/**
* Gets all of the arguments of the call, including the `this` pointer, if any.
*/

View File

@@ -1310,6 +1310,13 @@ class CallInstruction extends Instruction {
result = getAnOperand()
}
/**
* Gets the `Function` that the call targets, if this is statically known.
*/
final Function getStaticCallTarget() {
result = getCallTarget().(FunctionInstruction).getFunctionSymbol()
}
/**
* Gets all of the arguments of the call, including the `this` pointer, if any.
*/

View File

@@ -3,6 +3,8 @@ import cpp
private import InputIR
private import semmle.code.cpp.ir.internal.IntegerConstant as Ints
private import semmle.code.cpp.models.interfaces.Alias
private class IntValue = Ints::IntValue;
/**
@@ -46,7 +48,7 @@ private IntValue getFieldBitOffset(Field field) {
* not result in any address held in that operand from escaping beyond the
* instruction.
*/
predicate operandIsConsumedWithoutEscaping(Operand operand) {
private predicate operandIsConsumedWithoutEscaping(Operand operand) {
// The source/destination address of a Load/Store does not escape (but the
// loaded/stored value could).
operand instanceof AddressOperand or
@@ -60,7 +62,18 @@ predicate operandIsConsumedWithoutEscaping(Operand operand) {
// Converting an address to a `bool` does not escape the address.
instr.(ConvertInstruction).getResultType() instanceof BoolType
)
)
) or
// Some standard function arguments never escape
isNeverEscapesArgument(operand)
}
private predicate operandEscapesDomain(Operand operand) {
not operandIsConsumedWithoutEscaping(operand) and
not operandIsPropagated(operand, _) and
not isArgumentForParameter(_, operand, _) and
not isOnlyEscapesViaReturnArgument(operand) and
not operand.getUseInstruction() instanceof ReturnValueInstruction and
not operand instanceof PhiOperand
}
/**
@@ -98,7 +111,7 @@ IntValue getPointerBitOffset(PointerOffsetInstruction instr) {
* `bitOffset`. If the address is propagated, but the offset is not known to be
* a constant, then `bitOffset` is unknown.
*/
predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
private predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
exists(Instruction instr |
instr = operand.getUseInstruction() and
(
@@ -134,34 +147,134 @@ predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
// offset of the field.
bitOffset = getFieldBitOffset(instr.(FieldAddressInstruction).getField()) or
// A copy propagates the source value.
operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0
operand = instr.(CopyInstruction).getSourceValueOperand() and bitOffset = 0 or
// Some functions are known to propagate an argument
isAlwaysReturnedArgument(operand) and bitOffset = 0
)
)
}
/**
* Holds if any address held in operand number `tag` of instruction `instr`
* escapes outside the domain of the analysis.
*/
predicate operandEscapes(Operand operand) {
// Conservatively assume that the address escapes unless one of the following
// holds:
not (
// The operand is used in a way that does not escape the instruction
operandIsConsumedWithoutEscaping(operand) or
// The address is propagated to the result of the instruction, but that
// result does not itself escape.
operandIsPropagated(operand, _) and not resultEscapes(operand.getUseInstruction())
private predicate operandEscapesNonReturn(Operand operand) {
// The address is propagated to the result of the instruction, and that result itself is returned
operandIsPropagated(operand, _) and resultEscapesNonReturn(operand.getUseInstruction())
or
// The operand is used in a function call which returns it, and the return value is then returned
exists(CallInstruction ci, Instruction init |
isArgumentForParameter(ci, operand, init) and
(
resultMayReachReturn(init) and
resultEscapesNonReturn(ci)
or
resultEscapesNonReturn(init)
)
)
or
isOnlyEscapesViaReturnArgument(operand) and resultEscapesNonReturn(operand.getUseInstruction())
or
operand instanceof PhiOperand and
resultEscapesNonReturn(operand.getUseInstruction())
or
operandEscapesDomain(operand)
}
private predicate operandMayReachReturn(Operand operand) {
// The address is propagated to the result of the instruction, and that result itself is returned
operandIsPropagated(operand, _) and
resultMayReachReturn(operand.getUseInstruction())
or
// The operand is used in a function call which returns it, and the return value is then returned
exists(CallInstruction ci, Instruction init |
isArgumentForParameter(ci, operand, init) and
resultMayReachReturn(init) and
resultMayReachReturn(ci)
)
or
// The address is returned
operand.getUseInstruction() instanceof ReturnValueInstruction
or
isOnlyEscapesViaReturnArgument(operand) and resultMayReachReturn(operand.getUseInstruction())
or
operand instanceof PhiOperand and
resultMayReachReturn(operand.getUseInstruction())
}
private predicate operandReturned(Operand operand, IntValue bitOffset) {
// The address is propagated to the result of the instruction, and that result itself is returned
exists(IntValue bitOffset1, IntValue bitOffset2 |
operandIsPropagated(operand, bitOffset1) and
resultReturned(operand.getUseInstruction(), bitOffset2) and
bitOffset = Ints::add(bitOffset1, bitOffset2)
)
or
// The operand is used in a function call which returns it, and the return value is then returned
exists(CallInstruction ci, Instruction init, IntValue bitOffset1, IntValue bitOffset2 |
isArgumentForParameter(ci, operand, init) and
resultReturned(init, bitOffset1) and
resultReturned(ci, bitOffset2) and
bitOffset = Ints::add(bitOffset1, bitOffset2)
)
or
// The address is returned
operand.getUseInstruction() instanceof ReturnValueInstruction and
bitOffset = 0
or
isOnlyEscapesViaReturnArgument(operand) and resultReturned(operand.getUseInstruction(), _) and
bitOffset = Ints::unknown()
}
private predicate isArgumentForParameter(CallInstruction ci, Operand operand, Instruction init) {
exists(Function f |
ci = operand.getUseInstruction() and
f = ci.getStaticCallTarget() and
(
init.(InitializeParameterInstruction).getParameter() = f.getParameter(operand.(PositionalArgumentOperand).getIndex())
or
init instanceof InitializeThisInstruction and
init.getEnclosingFunction() = f and
operand instanceof ThisArgumentOperand
) and
not f.isVirtual() and
not f instanceof AliasFunction
)
}
private predicate isAlwaysReturnedArgument(Operand operand) {
exists(AliasFunction f |
f = operand.getUseInstruction().(CallInstruction).getStaticCallTarget() and
f.parameterIsAlwaysReturned(operand.(PositionalArgumentOperand).getIndex())
)
}
private predicate isOnlyEscapesViaReturnArgument(Operand operand) {
exists(AliasFunction f |
f = operand.getUseInstruction().(CallInstruction).getStaticCallTarget() and
f.parameterEscapesOnlyViaReturn(operand.(PositionalArgumentOperand).getIndex())
)
}
private predicate isNeverEscapesArgument(Operand operand) {
exists(AliasFunction f |
f = operand.getUseInstruction().(CallInstruction).getStaticCallTarget() and
f.parameterNeverEscapes(operand.(PositionalArgumentOperand).getIndex())
)
}
private predicate resultReturned(Instruction instr, IntValue bitOffset) {
operandReturned(instr.getAUse(), bitOffset)
}
private predicate resultMayReachReturn(Instruction instr) {
operandMayReachReturn(instr.getAUse())
}
/**
* Holds if any address held in the result of instruction `instr` escapes
* outside the domain of the analysis.
*/
predicate resultEscapes(Instruction instr) {
private predicate resultEscapesNonReturn(Instruction instr) {
// The result escapes if it has at least one use that escapes.
operandEscapes(instr.getAUse())
operandEscapesNonReturn(instr.getAUse())
}
/**
@@ -169,15 +282,11 @@ predicate resultEscapes(Instruction instr) {
* domain of the analysis.
*/
private predicate automaticVariableAddressEscapes(IRAutomaticVariable var) {
exists(FunctionIR funcIR |
funcIR = var.getEnclosingFunctionIR() and
// The variable's address escapes if the result of any
// VariableAddressInstruction that computes the variable's address escapes.
exists(VariableAddressInstruction instr |
instr.getEnclosingFunctionIR() = funcIR and
instr.getVariable() = var and
resultEscapes(instr)
)
// The variable's address escapes if the result of any
// VariableAddressInstruction that computes the variable's address escapes.
exists(VariableAddressInstruction instr |
instr.getVariable() = var and
resultEscapesNonReturn(instr)
)
}
@@ -207,7 +316,14 @@ predicate resultPointsTo(Instruction instr, IRVariable var, IntValue bitOffset)
// If an operand is propagated, then the result points to the same variable,
// offset by the bit offset from the propagation.
resultPointsTo(operand.getDefinitionInstruction(), var, originalBitOffset) and
operandIsPropagated(operand, propagatedBitOffset) and
(
operandIsPropagated(operand, propagatedBitOffset)
or
exists(CallInstruction ci, Instruction init |
isArgumentForParameter(ci, operand, init) and
resultReturned(init, propagatedBitOffset)
)
) and
bitOffset = Ints::add(originalBitOffset, propagatedBitOffset)
)
}

View File

@@ -1,4 +1,3 @@
| test.cpp:66:30:66:36 | test.cpp:71:8:71:9 | AST only |
| test.cpp:89:28:89:34 | test.cpp:92:8:92:14 | IR only |
| test.cpp:100:13:100:18 | test.cpp:103:10:103:12 | AST only |
| test.cpp:109:9:109:14 | test.cpp:110:10:110:12 | IR only |

View File

@@ -6,6 +6,7 @@
| test.cpp:30:8:30:8 | Load: t | test.cpp:35:10:35:15 | Call: call to source |
| test.cpp:31:8:31:8 | Load: c | test.cpp:36:13:36:18 | Call: call to source |
| test.cpp:58:10:58:10 | Load: t | test.cpp:50:14:50:19 | Call: call to source |
| test.cpp:71:8:71:9 | Load: x4 | test.cpp:66:30:66:36 | InitializeParameter: source1 |
| test.cpp:76:8:76:9 | Load: u1 | test.cpp:75:7:75:8 | Uninitialized: definition of u1 |
| test.cpp:84:8:84:18 | Load: ... ? ... : ... | test.cpp:83:7:83:8 | Uninitialized: definition of u2 |
| test.cpp:86:8:86:9 | Load: i1 | test.cpp:83:7:83:8 | Uninitialized: definition of u2 |

View File

@@ -1,5 +1,44 @@
void CallByPointer(int* p);
void CallByReference(int& r);
int *GetPointer();
int &GetReference();
int FetchFromPointer(int *no_p) {
return *no_p;
}
int FetchFromReference(int &no_r) {
return no_r;
}
int *ReturnPointer(int *no_p) {
return no_p;
}
int &ReturnReference(int &no_r) {
return no_r;
}
void CallByPointerParamEscape(int *no_p) {
CallByPointer(no_p);
}
void CallByReferenceParamEscape(int &no_r) {
CallByReference(no_r);
}
int *MaybeReturn(int *no_p, int *no_q, bool no_b) {
if (no_b) {
return no_p;
} else {
return no_q;
}
}
int &EscapeAndReturn(int &no_r) {
CallByReference(no_r);
return no_r;
}
struct Point {
float x;
@@ -27,6 +66,40 @@ struct Derived : Intermediate1, Intermediate2 {
float d;
};
class C;
void CEscapes(C *no_c);
class C {
public:
void ThisEscapes() {
CEscapes(this);
}
C *ThisReturned() {
return this;
}
virtual C *Overridden() {
CEscapes(this);
return nullptr;
}
};
class OverrideReturns : C {
public:
virtual C *Overridden() {
return this;
}
};
class OverrideNone : C {
public:
virtual C *Overridden() {
return nullptr;
}
};
void Escape()
{
int no_result;
@@ -95,4 +168,86 @@ void Escape()
int passByRef;
CallByReference(passByRef);
int no_ssa_passByPtr;
FetchFromPointer(&no_ssa_passByPtr);
int no_ssa_passByRef;
FetchFromReference(no_ssa_passByRef);
int no_ssa_passByPtr_ret;
FetchFromPointer(&no_ssa_passByPtr_ret);
int no_ssa_passByRef_ret;
FetchFromReference(no_ssa_passByRef_ret);
int passByPtr2;
CallByPointerParamEscape(&passByPtr2);
int passByRef2;
CallByReferenceParamEscape(passByRef2);
int passByPtr3;
CallByPointerParamEscape(ReturnPointer(&passByPtr3));
int passByRef3;
CallByReferenceParamEscape(ReturnReference(passByRef3));
int no_ssa_passByPtr4;
int no_ssa_passByPtr5;
bool no_b2 = false;
MaybeReturn(&no_ssa_passByPtr4, &no_ssa_passByPtr5, no_b2);
int passByRef6;
EscapeAndReturn(passByRef6);
int no_ssa_passByRef7;
ReturnReference(no_ssa_passByRef7);
C no_ssa_c;
no_ssa_c.ThisReturned();
C c;
c.ThisEscapes();
C c2;
CEscapes(&c2);
C c3;
c3.ThisReturned()->ThisEscapes();
C c4;
CEscapes(c4.ThisReturned());
C c5;
c5.Overridden();
OverrideReturns or1;
or1.Overridden();
OverrideReturns or2;
CEscapes(or2.Overridden());
OverrideNone on1;
on1.Overridden();
OverrideNone on2;
CEscapes(on2.Overridden());
int condEscape1, condEscape2;
int *no_condTemp;
if(GetPointer()) {
no_condTemp = &condEscape1;
} else {
no_condTemp = &condEscape2;
}
CallByPointer(no_condTemp);
}

View File

@@ -1,108 +1,47 @@
| escape.cpp:32:9:32:17 | VariableAddress[no_result] | no_result+0:0 |
| escape.cpp:33:9:33:11 | VariableAddress[no_] | no_+0:0 |
| escape.cpp:35:5:35:7 | VariableAddress[no_] | no_+0:0 |
| escape.cpp:36:5:36:7 | VariableAddress[no_] | no_+0:0 |
| escape.cpp:36:11:36:13 | VariableAddress[no_] | no_+0:0 |
| escape.cpp:37:5:37:13 | VariableAddress[no_result] | no_result+0:0 |
| escape.cpp:37:17:37:19 | VariableAddress[no_] | no_+0:0 |
| escape.cpp:38:5:38:13 | VariableAddress[no_result] | no_result+0:0 |
| escape.cpp:38:19:38:21 | VariableAddress[no_] | no_+0:0 |
| escape.cpp:40:5:40:7 | VariableAddress[no_] | no_+0:0 |
| escape.cpp:41:6:41:8 | VariableAddress[no_] | no_+0:0 |
| escape.cpp:42:5:42:13 | VariableAddress[no_result] | no_result+0:0 |
| escape.cpp:42:19:42:28 | PointerAdd[4] | no_+0:0 |
| escape.cpp:42:21:42:23 | VariableAddress[no_] | no_+0:0 |
| escape.cpp:43:5:43:13 | VariableAddress[no_result] | no_result+0:0 |
| escape.cpp:43:19:43:28 | PointerSub[4] | no_+0:0 |
| escape.cpp:43:21:43:23 | VariableAddress[no_] | no_+0:0 |
| escape.cpp:44:5:44:13 | VariableAddress[no_result] | no_result+0:0 |
| escape.cpp:44:19:44:26 | PointerAdd[4] | no_+0:0 |
| escape.cpp:44:24:44:26 | VariableAddress[no_] | no_+0:0 |
| escape.cpp:45:10:45:12 | VariableAddress[no_] | no_+0:0 |
| escape.cpp:47:13:47:15 | VariableAddress[no_] | no_+0:0 |
| escape.cpp:50:15:50:17 | VariableAddress[no_] | no_+0:0 |
| escape.cpp:51:10:51:12 | VariableAddress[no_] | no_+0:0 |
| escape.cpp:51:16:51:18 | VariableAddress[no_] | no_+0:0 |
| escape.cpp:51:22:51:24 | VariableAddress[no_] | no_+0:0 |
| escape.cpp:54:10:54:12 | VariableAddress[no_] | no_+0:0 |
| escape.cpp:56:13:56:15 | VariableAddress[no_] | no_+0:0 |
| escape.cpp:59:9:59:16 | VariableAddress[no_Array] | no_Array+0:0 |
| escape.cpp:60:5:60:12 | VariableAddress[no_Array] | no_Array+0:0 |
| escape.cpp:61:5:61:18 | Convert | no_Array+0:0 |
| escape.cpp:61:11:61:18 | Convert | no_Array+0:0 |
| escape.cpp:61:11:61:18 | VariableAddress[no_Array] | no_Array+0:0 |
| escape.cpp:62:5:62:12 | Convert | no_Array+0:0 |
| escape.cpp:62:5:62:12 | VariableAddress[no_Array] | no_Array+0:0 |
| escape.cpp:62:5:62:15 | PointerAdd[4] | no_Array+20:0 |
| escape.cpp:63:5:63:15 | PointerAdd[4] | no_Array+20:0 |
| escape.cpp:63:7:63:14 | Convert | no_Array+0:0 |
| escape.cpp:63:7:63:14 | VariableAddress[no_Array] | no_Array+0:0 |
| escape.cpp:64:5:64:13 | VariableAddress[no_result] | no_result+0:0 |
| escape.cpp:64:17:64:24 | Convert | no_Array+0:0 |
| escape.cpp:64:17:64:24 | VariableAddress[no_Array] | no_Array+0:0 |
| escape.cpp:64:17:64:27 | PointerAdd[4] | no_Array+20:0 |
| escape.cpp:65:5:65:13 | VariableAddress[no_result] | no_result+0:0 |
| escape.cpp:65:17:65:27 | PointerAdd[4] | no_Array+20:0 |
| escape.cpp:65:19:65:26 | Convert | no_Array+0:0 |
| escape.cpp:65:19:65:26 | VariableAddress[no_Array] | no_Array+0:0 |
| escape.cpp:67:11:67:18 | VariableAddress[no_Point] | no_Point+0:0 |
| escape.cpp:67:21:67:32 | FieldAddress[x] | no_Point+0:0 |
| escape.cpp:67:21:67:32 | FieldAddress[y] | no_Point+4:0 |
| escape.cpp:67:21:67:32 | FieldAddress[z] | no_Point+8:0 |
| escape.cpp:68:11:68:14 | VariableAddress[no_x] | no_x+0:0 |
| escape.cpp:68:18:68:25 | VariableAddress[no_Point] | no_Point+0:0 |
| escape.cpp:68:27:68:27 | FieldAddress[x] | no_Point+0:0 |
| escape.cpp:69:5:69:12 | VariableAddress[no_Point] | no_Point+0:0 |
| escape.cpp:69:14:69:14 | FieldAddress[y] | no_Point+4:0 |
| escape.cpp:69:18:69:21 | VariableAddress[no_x] | no_x+0:0 |
| escape.cpp:70:11:70:14 | VariableAddress[no_y] | no_y+0:0 |
| escape.cpp:70:20:70:27 | VariableAddress[no_Point] | no_Point+0:0 |
| escape.cpp:70:31:70:31 | FieldAddress[y] | no_Point+4:0 |
| escape.cpp:71:7:71:14 | VariableAddress[no_Point] | no_Point+0:0 |
| escape.cpp:71:18:71:18 | FieldAddress[y] | no_Point+4:0 |
| escape.cpp:71:22:71:25 | VariableAddress[no_y] | no_y+0:0 |
| escape.cpp:72:11:72:14 | VariableAddress[no_z] | no_z+0:0 |
| escape.cpp:72:21:72:28 | VariableAddress[no_Point] | no_Point+0:0 |
| escape.cpp:72:30:72:30 | FieldAddress[z] | no_Point+8:0 |
| escape.cpp:73:8:73:15 | VariableAddress[no_Point] | no_Point+0:0 |
| escape.cpp:73:17:73:17 | FieldAddress[z] | no_Point+8:0 |
| escape.cpp:73:22:73:25 | VariableAddress[no_z] | no_z+0:0 |
| escape.cpp:75:13:75:22 | VariableAddress[no_Derived] | no_Derived+0:0 |
| escape.cpp:76:5:76:14 | ConvertToBase[Derived : Intermediate1] | no_Derived+0:0 |
| escape.cpp:76:5:76:14 | ConvertToBase[Intermediate1 : Base] | no_Derived+0:0 |
| escape.cpp:76:5:76:14 | VariableAddress[no_Derived] | no_Derived+0:0 |
| escape.cpp:76:16:76:16 | FieldAddress[b] | no_Derived+0:0 |
| escape.cpp:77:11:77:14 | VariableAddress[no_b] | no_b+0:0 |
| escape.cpp:77:18:77:27 | ConvertToBase[Derived : Intermediate1] | no_Derived+0:0 |
| escape.cpp:77:18:77:27 | ConvertToBase[Intermediate1 : Base] | no_Derived+0:0 |
| escape.cpp:77:18:77:27 | VariableAddress[no_Derived] | no_Derived+0:0 |
| escape.cpp:77:29:77:29 | FieldAddress[b] | no_Derived+0:0 |
| escape.cpp:78:5:78:14 | ConvertToBase[Derived : Intermediate2] | no_Derived+12:0 |
| escape.cpp:78:5:78:14 | VariableAddress[no_Derived] | no_Derived+0:0 |
| escape.cpp:78:16:78:17 | FieldAddress[i2] | no_Derived+16:0 |
| escape.cpp:79:11:79:15 | VariableAddress[no_i2] | no_i2+0:0 |
| escape.cpp:79:19:79:28 | ConvertToBase[Derived : Intermediate2] | no_Derived+12:0 |
| escape.cpp:79:19:79:28 | VariableAddress[no_Derived] | no_Derived+0:0 |
| escape.cpp:79:30:79:31 | FieldAddress[i2] | no_Derived+16:0 |
| escape.cpp:81:9:81:21 | VariableAddress[no_ssa_addrOf] | no_ssa_addrOf+0:0 |
| escape.cpp:82:10:82:13 | VariableAddress[no_p] | no_p+0:0 |
| escape.cpp:82:17:82:30 | Store | no_ssa_addrOf+0:0 |
| escape.cpp:82:18:82:30 | VariableAddress[no_ssa_addrOf] | no_ssa_addrOf+0:0 |
| escape.cpp:84:9:84:20 | VariableAddress[no_ssa_refTo] | no_ssa_refTo+0:0 |
| escape.cpp:85:10:85:13 | VariableAddress[no_r] | no_r+0:0 |
| escape.cpp:85:17:85:28 | Store | no_ssa_refTo+0:0 |
| escape.cpp:85:17:85:28 | VariableAddress[no_ssa_refTo] | no_ssa_refTo+0:0 |
| escape.cpp:87:9:87:32 | VariableAddress[no_ssa_refToArrayElement] | no_ssa_refToArrayElement+0:0 |
| escape.cpp:88:10:88:15 | VariableAddress[no_rae] | no_rae+0:0 |
| escape.cpp:88:19:88:42 | Convert | no_ssa_refToArrayElement+0:0 |
| escape.cpp:88:19:88:42 | VariableAddress[no_ssa_refToArrayElement] | no_ssa_refToArrayElement+0:0 |
| escape.cpp:88:19:88:45 | PointerAdd[4] | no_ssa_refToArrayElement+20:0 |
| escape.cpp:88:19:88:45 | Store | no_ssa_refToArrayElement+20:0 |
| escape.cpp:90:9:90:25 | VariableAddress[no_ssa_refToArray] | no_ssa_refToArray+0:0 |
| escape.cpp:91:11:91:15 | VariableAddress[no_ra] | no_ra+0:0 |
| escape.cpp:91:24:91:40 | Store | no_ssa_refToArray+0:0 |
| escape.cpp:91:24:91:40 | VariableAddress[no_ssa_refToArray] | no_ssa_refToArray+0:0 |
| escape.cpp:93:9:93:17 | VariableAddress[passByPtr] | passByPtr+0:0 |
| escape.cpp:94:20:94:28 | VariableAddress[passByPtr] | passByPtr+0:0 |
| escape.cpp:96:9:96:17 | VariableAddress[passByRef] | passByRef+0:0 |
| escape.cpp:97:21:97:29 | VariableAddress[passByRef] | passByRef+0:0 |
| escape.cpp:115:19:115:28 | PointerAdd[4] | no_+0:0 | no_+0:0 |
| escape.cpp:116:19:116:28 | PointerSub[4] | no_+0:0 | no_+0:0 |
| escape.cpp:117:19:117:26 | PointerAdd[4] | no_+0:0 | no_+0:0 |
| escape.cpp:134:5:134:18 | Convert | no_Array+0:0 | no_Array+0:0 |
| escape.cpp:134:11:134:18 | Convert | no_Array+0:0 | no_Array+0:0 |
| escape.cpp:135:5:135:12 | Convert | no_Array+0:0 | no_Array+0:0 |
| escape.cpp:135:5:135:15 | PointerAdd[4] | no_Array+20:0 | no_Array+20:0 |
| escape.cpp:136:5:136:15 | PointerAdd[4] | no_Array+20:0 | no_Array+20:0 |
| escape.cpp:136:7:136:14 | Convert | no_Array+0:0 | no_Array+0:0 |
| escape.cpp:137:17:137:24 | Convert | no_Array+0:0 | no_Array+0:0 |
| escape.cpp:137:17:137:27 | PointerAdd[4] | no_Array+20:0 | no_Array+20:0 |
| escape.cpp:138:17:138:27 | PointerAdd[4] | no_Array+20:0 | no_Array+20:0 |
| escape.cpp:138:19:138:26 | Convert | no_Array+0:0 | no_Array+0:0 |
| escape.cpp:140:21:140:32 | FieldAddress[x] | no_Point+0:0 | no_Point+0:0 |
| escape.cpp:140:21:140:32 | FieldAddress[y] | no_Point+4:0 | no_Point+4:0 |
| escape.cpp:140:21:140:32 | FieldAddress[z] | no_Point+8:0 | no_Point+8:0 |
| escape.cpp:141:27:141:27 | FieldAddress[x] | no_Point+0:0 | no_Point+0:0 |
| escape.cpp:142:14:142:14 | FieldAddress[y] | no_Point+4:0 | no_Point+4:0 |
| escape.cpp:143:31:143:31 | FieldAddress[y] | no_Point+4:0 | no_Point+4:0 |
| escape.cpp:144:18:144:18 | FieldAddress[y] | no_Point+4:0 | no_Point+4:0 |
| escape.cpp:145:30:145:30 | FieldAddress[z] | no_Point+8:0 | no_Point+8:0 |
| escape.cpp:146:17:146:17 | FieldAddress[z] | no_Point+8:0 | no_Point+8:0 |
| escape.cpp:149:5:149:14 | ConvertToBase[Derived : Intermediate1] | no_Derived+0:0 | no_Derived+0:0 |
| escape.cpp:149:5:149:14 | ConvertToBase[Intermediate1 : Base] | no_Derived+0:0 | no_Derived+0:0 |
| escape.cpp:149:16:149:16 | FieldAddress[b] | no_Derived+0:0 | no_Derived+0:0 |
| escape.cpp:150:18:150:27 | ConvertToBase[Derived : Intermediate1] | no_Derived+0:0 | no_Derived+0:0 |
| escape.cpp:150:18:150:27 | ConvertToBase[Intermediate1 : Base] | no_Derived+0:0 | no_Derived+0:0 |
| escape.cpp:150:29:150:29 | FieldAddress[b] | no_Derived+0:0 | no_Derived+0:0 |
| escape.cpp:151:5:151:14 | ConvertToBase[Derived : Intermediate2] | no_Derived+12:0 | no_Derived+12:0 |
| escape.cpp:151:16:151:17 | FieldAddress[i2] | no_Derived+16:0 | no_Derived+16:0 |
| escape.cpp:152:19:152:28 | ConvertToBase[Derived : Intermediate2] | no_Derived+12:0 | no_Derived+12:0 |
| escape.cpp:152:30:152:31 | FieldAddress[i2] | no_Derived+16:0 | no_Derived+16:0 |
| escape.cpp:155:17:155:30 | Store | no_ssa_addrOf+0:0 | no_ssa_addrOf+0:0 |
| escape.cpp:158:17:158:28 | Store | no_ssa_refTo+0:0 | no_ssa_refTo+0:0 |
| escape.cpp:161:19:161:42 | Convert | no_ssa_refToArrayElement+0:0 | no_ssa_refToArrayElement+0:0 |
| escape.cpp:161:19:161:45 | PointerAdd[4] | no_ssa_refToArrayElement+20:0 | no_ssa_refToArrayElement+20:0 |
| escape.cpp:161:19:161:45 | Store | no_ssa_refToArrayElement+20:0 | no_ssa_refToArrayElement+20:0 |
| escape.cpp:164:24:164:40 | Store | no_ssa_refToArray+0:0 | no_ssa_refToArray+0:0 |
| escape.cpp:191:30:191:42 | Call | none | passByPtr3+0:0 |
| escape.cpp:194:32:194:46 | Call | none | passByRef3+0:0 |
| escape.cpp:202:5:202:19 | Call | none | passByRef6+0:0 |
| escape.cpp:205:5:205:19 | Call | none | no_ssa_passByRef7+0:0 |
| escape.cpp:209:14:209:25 | Call | none | no_ssa_c+0:0 |
| escape.cpp:221:8:221:19 | Call | none | c3+0:0 |
| escape.cpp:225:17:225:28 | Call | none | c4+0:0 |
| escape.cpp:247:2:247:27 | Store | condEscape1+0:0 | condEscape1+0:0 |
| escape.cpp:249:9:249:34 | Store | condEscape2+0:0 | condEscape2+0:0 |

View File

@@ -1,11 +1,34 @@
import default
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.AliasAnalysis
import semmle.code.cpp.ir.implementation.raw.IR
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.AliasAnalysis as RawAA
import semmle.code.cpp.ir.implementation.raw.IR as Raw
import semmle.code.cpp.ir.implementation.aliased_ssa.internal.AliasAnalysis as UnAA
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as Un
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SSAConstruction
from Instruction instr, string pointsTo
from Raw::Instruction rawInstr, Un::Instruction unInstr, string rawPointsTo, string unPointsTo
where
exists(IRVariable var, int bitOffset |
resultPointsTo(instr, var, bitOffset) and
pointsTo = var.toString() + getBitOffsetString(bitOffset)
rawInstr = getOldInstruction(unInstr) and
not rawInstr instanceof Raw::VariableAddressInstruction and
(
exists(Variable var, int rawBitOffset, int unBitOffset |
RawAA::resultPointsTo(rawInstr, Raw::getIRUserVariable(_, var), rawBitOffset) and
rawPointsTo = var.toString() + RawAA::getBitOffsetString(rawBitOffset) and
UnAA::resultPointsTo(unInstr, Un::getIRUserVariable(_, var), unBitOffset) and
unPointsTo = var.toString() + UnAA::getBitOffsetString(unBitOffset)
)
or
exists(Variable var, int unBitOffset |
not RawAA::resultPointsTo(rawInstr, Raw::getIRUserVariable(_, var), _) and
rawPointsTo = "none" and
UnAA::resultPointsTo(unInstr, Un::getIRUserVariable(_, var), unBitOffset) and
unPointsTo = var.toString() + UnAA::getBitOffsetString(unBitOffset)
)
or
exists(Variable var, int rawBitOffset |
RawAA::resultPointsTo(rawInstr, Raw::getIRUserVariable(_, var), rawBitOffset) and
rawPointsTo = var.toString() + RawAA::getBitOffsetString(rawBitOffset) and
not UnAA::resultPointsTo(unInstr, Un::getIRUserVariable(_, var), _) and
unPointsTo = "none"
)
)
select instr.getLocation().toString(), instr.getOperationString(), pointsTo
select rawInstr.getLocation().toString(), rawInstr.getOperationString(), rawPointsTo, unPointsTo

View File

@@ -57,7 +57,7 @@ namespace Semmle.Util
/// Finds the path for the program <paramref name="prog"/> based on the
/// <code>PATH</code> environment variable, and in the case of Windows the
/// <code>PATHEXT</code> environment variable.
///
///
/// Returns <code>null</code> of no path can be found.
/// </summary>
public static string FindProgramOnPath(string prog)

View File

@@ -7,7 +7,7 @@ public class HttpHandler : IHttpHandler
public void ProcessRequest(HttpContext ctx)
{
string format = ctx.Request.QueryString["nameformat"];
// BAD: Uncontrolled format string.
FormattedName = string.Format(format, Surname, Forenames);
}

View File

@@ -9,6 +9,7 @@ module DataFlow {
private import semmle.code.csharp.dataflow.CallContext
private import semmle.code.csharp.dataflow.DelegateDataFlow
private import semmle.code.csharp.dataflow.LibraryTypeDataFlow
private import semmle.code.csharp.frameworks.EntityFramework
private import Internal::Cached
private import dotnet
private import cil
@@ -103,6 +104,15 @@ module DataFlow {
predicate localFlowStep = Internal::LocalFlow::step/2;
/**
* A dataflow node that jumps between callables. This can be extended in framework code
* to add additional dataflow steps.
*/
abstract class NonLocalJumpNode extends Node {
/** Gets a successor node that is potentially in another callable. */
abstract Node getAJumpSuccessor(boolean preservesValue);
}
/**
* A data flow node augmented with a call context and a configuration. Only
* nodes that are reachable from a source, and which can reach a sink, are
@@ -675,7 +685,7 @@ module DataFlow {
CilCall() {
call = this and
// No need to include calls that are compiled from source
not call.getImplementation().getMethod().compiledFromSource()
cilCallWithoutSource(call)
}
override DotNet::Callable getARuntimeTarget() {
@@ -1062,13 +1072,20 @@ module DataFlow {
(isSuccessor = true or isSuccessor = false)
}
pragma[noinline]
private predicate localFlowStep0(Node pred, Node succ, Configuration config, DotNet::Callable c) {
config.isAdditionalFlowStep(pred, succ) and
pred.getEnclosingCallable() = c
}
/**
* Holds if data may flow in one local step from `pred` to `succ`.
*/
bindingset[config]
predicate localFlowStep(Node pred, Node succ, Configuration config) {
localFlowStep(pred, succ) or
config.isAdditionalFlowStep(pred, succ)
localFlowStep(pred, succ)
or
localFlowStep0(pred, succ, config, succ.getEnclosingCallable())
}
/**
@@ -1079,7 +1096,7 @@ module DataFlow {
Pruning::nodeCand(node, config) and
(
config.isSource(node) or
jumpStep(_, node) or
jumpStep(_, node, config) or
node instanceof ParameterNode or
node instanceof OutNode or
node instanceof NormalReturnNode or
@@ -1095,7 +1112,7 @@ module DataFlow {
predicate localFlowExit(Node node, Configuration config) {
Pruning::nodeCand(node, config) and
(
jumpStep(node, _)
jumpStep(node, _, config)
or
node instanceof ArgumentNode
or
@@ -1145,6 +1162,26 @@ module DataFlow {
}
}
/**
* Holds if the additional step from `node1` to `node2` jumps between callables.
*/
pragma[noinline]
private predicate additionalJumpStep(Node node1, Node node2, Configuration config) {
config.isAdditionalFlowStep(node1, node2) and
node1.getEnclosingCallable() != node2.getEnclosingCallable()
}
/**
* Holds if `pred` can flow to `succ`, by jumping from one callable to
* another.
*/
bindingset[config]
private predicate jumpStep(Node node1, Node node2, Configuration config) {
additionalJumpStep(node1, node2, config)
or
jumpStepNoConfig(node1, node2)
}
/**
* Provides predicates for pruning the data flow graph, by only including
* nodes that may potentially be reached in flow from some source to some
@@ -1162,7 +1199,7 @@ module DataFlow {
or
exists(Node mid | nodeCandFwd1(mid, config) | LocalFlow::localFlowStep(mid, node, config))
or
exists(Node mid | nodeCandFwd1(mid, config) | jumpStep(mid, node))
exists(Node mid | nodeCandFwd1(mid, config) | jumpStep(mid, node, config))
or
exists(ArgumentNode arg | nodeCandFwd1(arg, config) |
flowIntoCallableStep(_, arg, node, _, config)
@@ -1183,7 +1220,7 @@ module DataFlow {
or
exists(Node mid | nodeCand1(mid, config) | LocalFlow::localFlowStep(node, mid, config))
or
exists(Node mid | nodeCand1(mid, config) | jumpStep(node, mid))
exists(Node mid | nodeCand1(mid, config) | jumpStep(node, mid, config))
or
exists(ParameterNode p | nodeCand1(p, config) |
flowIntoCallableStep(_, node, p, _, config)
@@ -1238,7 +1275,7 @@ module DataFlow {
pragma[noinline]
private predicate jumpStepCand1(Node pred, Node succ, Configuration config) {
nodeCand1(succ, config) and
jumpStep(pred, succ)
jumpStep(pred, succ, config)
}
pragma[noinline]
@@ -1333,7 +1370,7 @@ module DataFlow {
or
nodeCandFwd2(node, _, config) and
exists(Node mid | nodeCand2(mid, _, config) |
jumpStep(node, mid) and
jumpStep(node, mid, config) and
isReturned = false
)
or
@@ -1454,16 +1491,32 @@ module DataFlow {
/**
* Holds if `pred` can flow to `succ`, by jumping from one callable to
* another.
* another. Additional steps specified by the configuration are *not* taken into account.
*/
cached
predicate jumpStep(ExprNode pred, ExprNode succ) {
exists(FieldLike fl, FieldLikeRead flr | fl.isStatic() |
fl.getAnAssignedValue() = pred.getExpr() and
predicate jumpStepNoConfig(ExprNode pred, ExprNode succ) {
pred.(NonLocalJumpNode).getAJumpSuccessor(true) = succ
}
/** A dataflow node that has field-like dataflow. */
private class FieldLikeJumpNode extends NonLocalJumpNode, ExprNode {
FieldLike fl;
FieldLikeRead flr;
ExprNode succ;
FieldLikeJumpNode() {
fl.isStatic() and
fl.getAnAssignedValue() = this.getExpr() and
fl.getAnAccess() = flr and
flr = succ.getExpr() and
hasNonlocalValue(flr)
)
}
override ExprNode getAJumpSuccessor(boolean preservesValue) {
result = succ and preservesValue = true
}
}
/**
@@ -1538,6 +1591,11 @@ module DataFlow {
ret.flowsOut(out.getDefinition()) and
call.asExpr() = out.getCall()
}
cached
predicate cilCallWithoutSource(CIL::Call call) {
not call.getImplementation().getMethod().compiledFromSource()
}
}
private newtype TContext =
@@ -1600,9 +1658,7 @@ module DataFlow {
/**
* A data flow context describing flow into a callable via a call argument.
*/
abstract private class ArgumentContext extends Context {
abstract DotNet::Expr getCall();
}
abstract private class ArgumentContext extends Context { abstract DotNet::Expr getCall(); }
/**
* A data flow context describing flow into a callable via an explicit call argument.
@@ -1736,7 +1792,7 @@ module DataFlow {
ctx = mid.getContext() and
LocalFlow::localFlowBigStep(mid.getNode(), node, mid.getConfiguration())
or
jumpStep(mid.getNode(), node) and
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
ctx instanceof NoContext
or
flowIntoCallable(mid, node, ctx)

View File

@@ -172,7 +172,7 @@ private predicate flowsFrom(
// Flow through static field or property
exists(DataFlow::Node mid |
flowsFrom(sink, mid, _, _) and
Cached::jumpStep(node, mid) and
Cached::jumpStepNoConfig(node, mid) and
isReturned = false and
lastCall instanceof EmptyCallContext
)

View File

@@ -80,8 +80,11 @@ module TaintTracking {
predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
final override predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
isAdditionalTaintStep(pred, succ) or
isAdditionalTaintStep(pred, succ)
or
localAdditionalTaintStep(pred, succ)
or
succ = pred.(DataFlow::NonLocalJumpNode).getAJumpSuccessor(false)
}
final override predicate isAdditionalFlowStepIntoCall(

View File

@@ -222,11 +222,43 @@ private module Internal {
}
}
private class FieldOrProperty extends Assignable {
FieldOrProperty() {
this instanceof Field or
this instanceof Property
private class DynamicFieldOrProperty extends Assignable {
DynamicFieldOrProperty() {
(
this instanceof Field or
this instanceof Property
) and
this.getName() = any(DynamicMemberAccess dma).getLateBoundTargetName()
}
predicate isMemberOf(string name, ValueOrRefType t) {
name = this.getName() and t.hasMember(this)
}
}
private class TypeWithDynamicFieldOrProperty extends ValueOrRefType {
DynamicFieldOrProperty fp;
TypeWithDynamicFieldOrProperty() { fp.isMemberOf(_, this) }
predicate isImplicitlyConvertibleTo(string name, Type t) {
name = fp.getName() and
this.isImplicitlyConvertibleTo(t)
}
}
pragma[noinline]
private predicate isPossibleDynamicMemberAccessQualifierType(
DynamicMemberAccess dma, string name, TypeWithDynamicFieldOrProperty t
) {
exists(Type qt, boolean isExact |
qt = getAPossibleType(dma.getQualifier(), isExact) and
name = dma.getLateBoundTargetName()
|
isExact = true and t = qt
or
isExact = false and t.isImplicitlyConvertibleTo(name, qt)
)
}
/**
@@ -235,14 +267,10 @@ private module Internal {
* corresponding to the type of a relevant field or property are included.
*/
private Type getAPossibleType(Expr e, boolean isExact) {
exists(ValueOrRefType qualifierType, FieldOrProperty fp, boolean qualifierTypeIsExact |
qualifierType = getAPossibleTypeDynamicMemberAccessQualifier(e, qualifierTypeIsExact, fp)
exists(DynamicFieldOrProperty fp, string name, TypeWithDynamicFieldOrProperty t |
isPossibleDynamicMemberAccessQualifierType(e, name, t) and
fp.isMemberOf(name, t)
|
(
if qualifierTypeIsExact = true
then qualifierType.hasMember(fp)
else fp.getDeclaringType().isImplicitlyConvertibleTo(qualifierType)
) and
result = fp.getType() and
isExact = false
)
@@ -251,13 +279,6 @@ private module Internal {
result = getASourceType(e, isExact)
}
private Type getAPossibleTypeDynamicMemberAccessQualifier(
DynamicMemberAccess dma, boolean isExact, FieldOrProperty fp
) {
result = getAPossibleType(dma.getQualifier(), isExact) and
fp.getName() = dma.getLateBoundTargetName()
}
/**
* Provides the predicate `getASourceType()` for finding all relevant source
* types for a given expression.
@@ -799,22 +820,14 @@ private module Internal {
// conflicting types (for example, `Tuple<int, string>` is considered
// compatible with `Tuple<T, T>`).
override RuntimeCallable getADynamicTarget() {
// Condition 1
result = getADynamicTargetCandidate() and
// Condition 2
forall(int i | i in [0 .. getNumberOfArguments() - 1] |
result = getADynamicTargetCandidateWithCompatibleArg(i)
)
result = this.getADynamicTarget(this.getNumberOfArguments() - 1)
}
private RuntimeCallable getADynamicTargetCandidateWithCompatibleArg(int i) {
result = getADynamicTargetCandidateWithCompatibleArg1(i) or
result = getADynamicTargetCandidateWithCompatibleArg2(i)
}
pragma[noinline]
private RuntimeCallable getADynamicTargetCandidateWithCompatibleArg1(int i) {
result = this.getADynamicTargetCandidate() and
private RuntimeCallable getADynamicTarget(int i) {
i = -1 and
result = this.getADynamicTargetCandidate()
or
result = this.getADynamicTarget(i - 1) and
exists(Type parameterType, Type argumentType |
parameterType = this.getAParameterType(result, i) and
argumentType = getAPossibleType(this.getArgument(i), _)
@@ -827,6 +840,12 @@ private module Internal {
or
reflectionOrDynamicArgEqualsParamModuloTypeParameters(argumentType, parameterType)
)
or
result = this.getADynamicTarget(i - 1) and
exists(Type parameterType, Type t | parameterType = this.getAParameterType(result, i) |
this.argumentConvConstExpr(i, t) and
t.isImplicitlyConvertibleTo(parameterType)
)
}
private Type getAParameterType(RuntimeCallable c, int i) {
@@ -840,15 +859,6 @@ private module Internal {
)
}
pragma[noinline]
private RuntimeCallable getADynamicTargetCandidateWithCompatibleArg2(int i) {
result = this.getADynamicTargetCandidate() and
exists(Type parameterType, Type t | parameterType = this.getAParameterType(result, i) |
this.argumentConvConstExpr(i, t) and
t.isImplicitlyConvertibleTo(parameterType)
)
}
pragma[noinline]
private predicate argumentConvConstExpr(int i, Type t) {
convConstantExpr(this.getArgument(i), t)
@@ -954,7 +964,7 @@ private module Internal {
*/
private predicate isReflectionOrDynamicCallArgumentWithTypeParameters(Type argType, Type paramType) {
exists(DispatchReflectionOrDynamicCall call, Parameter p, int i, int j |
p = call.getAStaticTarget().getParameter(i) and
p = call.getADynamicTargetCandidate().getParameter(i) and
(
if p.isParams()
then (

View File

@@ -654,6 +654,14 @@ class QualifiableExpr extends Expr, @qualifiable_expr {
predicate isConditional() { conditional_access(this) }
}
private Expr getAnAssignOrForeachChild() {
result = any(AssignExpr e).getLValue()
or
result = any(ForeachStmt fs).getVariableDeclTuple()
or
result = getAnAssignOrForeachChild().getAChildExpr()
}
/**
* An expression representing a tuple, for example
* `(1, 2)` on line 2 or `(var x, var y)` on line 5 in
@@ -678,10 +686,7 @@ class TupleExpr extends Expr, @tuple_expr {
Expr getAnArgument() { result = getArgument(_) }
/** Holds if this tuple is a read access. */
predicate isReadAccess() {
not exists(AssignExpr e | this = e.getLValue().getAChildExpr*()) and
not exists(ForeachStmt fs | this = fs.getVariableDeclTuple().getAChildExpr*())
}
predicate isReadAccess() { not this = getAnAssignOrForeachChild() }
}
/**

View File

@@ -0,0 +1,193 @@
/**
* Classes modelling EntityFramework and EntityFrameworkCore.
*/
import csharp
private import semmle.code.csharp.frameworks.system.data.Entity
private import semmle.code.csharp.frameworks.system.collections.Generic
private import semmle.code.csharp.frameworks.Sql
private import semmle.code.csharp.dataflow.LibraryTypeDataFlow
module DataAnnotations {
class NotMappedAttribute extends Attribute {
NotMappedAttribute() {
this
.getType()
.hasQualifiedName("System.ComponentModel.DataAnnotations.Schema.NotMappedAttribute")
}
}
}
module EntityFramework {
/** An EF6 or EFCore namespace. */
class EFNamespace extends Namespace {
EFNamespace() {
this.getQualifiedName() = "Microsoft.EntityFrameworkCore"
or
this.getQualifiedName() = "System.Data.Entity"
}
}
/** A taint source where the data has come from a mapped property stored in the database. */
class StoredFlowSource extends DataFlow::Node {
StoredFlowSource() {
this.asExpr() = any(PropertyRead read | read.getTarget() instanceof MappedProperty)
}
}
private class EFClass extends Class {
EFClass() { this.getDeclaringNamespace() instanceof EFNamespace }
}
/** The class `Microsoft.EntityFrameworkCore.DbContext` or `System.Data.Entity.DbContext`. */
class DbContext extends EFClass {
DbContext() { this.getName() = "DbContext" }
Method getAFindMethod() {
result = this.getAMethod("Find")
or
result = this.getAMethod("FindAsync")
}
Method getAnUpdateMethod() { result = this.getAMethod("Update") }
}
/** The class `Microsoft.EntityFrameworkCore.DbSet<>` or `System.Data.Entity.DbSet<>`. */
class DbSet extends EFClass, UnboundGenericClass { DbSet() { this.getName() = "DbSet<>" } }
/** The class `Microsoft.EntityFrameworkCore.DbQuery<>` or `System.Data.Entity.DbQuery<>`. */
class DbQuery extends EFClass, UnboundGenericClass { DbQuery() { this.hasName("DbQuery<>") } }
/** A generic type or method that takes a mapped type as its type argument. */
private predicate usesMappedType(UnboundGeneric g) {
g instanceof DbSet
or
g instanceof DbQuery
or
exists(DbContext db |
g = db.getAnUpdateMethod()
or
g = db.getAFindMethod()
)
}
/** A type that is mapped to database table, or used as a query. */
class MappedType extends ValueOrRefType {
MappedType() {
not this instanceof ObjectType and
not this instanceof StringType and
not this instanceof ValueType and
(
exists(UnboundGeneric g | usesMappedType(g) |
this = g.getAConstructedGeneric().getATypeArgument()
)
or
this.getASubType() instanceof MappedType
)
}
}
/** A property that is potentially stored and retrieved from a database. */
class MappedProperty extends Property {
MappedProperty() {
this = any(MappedType t).getAMember() and
this.isPublic() and
not this.getAnAttribute() instanceof DataAnnotations::NotMappedAttribute
}
}
/** The struct `Microsoft.EntityFrameworkCore.RawSqlString`. */
class RawSqlStringStruct extends Struct, LibraryTypeDataFlow {
RawSqlStringStruct() { this.getQualifiedName() = "Microsoft.EntityFrameworkCore.RawSqlString" }
override predicate callableFlow(
CallableFlowSource source, CallableFlowSink sink, SourceDeclarationCallable c,
boolean preservesValue
) {
c = this.getAConstructor() and
source.(CallableFlowSourceArg).getArgumentIndex() = 0 and
sink instanceof CallableFlowSinkReturn and
preservesValue = true
or
c = this.getAConversionTo() and
source.(CallableFlowSourceArg).getArgumentIndex() = 0 and
sink instanceof CallableFlowSinkReturn and
preservesValue = true
}
ConversionOperator getAConversionTo() {
result = this.getAMember() and
result.getTargetType() instanceof RawSqlStringStruct and
result.getSourceType() instanceof StringType
}
}
/**
* A parameter that accepts raw SQL. Parameters of type `System.FormattableString`
* are not included as they are not vulnerable to SQL injection.
*/
private class SqlParameter extends Parameter {
SqlParameter() {
this.getType() instanceof StringType and
(
exists(Callable c | this = c.getParameter(0) | c.getName().matches("%Sql"))
or
this.getName() = "sql"
) and
this.getCallable().getDeclaringType().getDeclaringNamespace().getParentNamespace*() instanceof
EFNamespace
or
this.getType() instanceof RawSqlStringStruct
or
this = any(RawSqlStringStruct s).getAConstructor().getAParameter()
or
this = any(RawSqlStringStruct s).getAConversionTo().getAParameter()
}
}
/** A call to a method in EntityFrameworkCore that executes SQL. */
class EntityFrameworkCoreSqlSink extends SqlExpr, Call {
SqlParameter sqlParam;
EntityFrameworkCoreSqlSink() { this.getTarget().getAParameter() = sqlParam }
override Expr getSql() { result = this.getArgumentForParameter(sqlParam) }
}
/** A call to `System.Data.Entity.DbSet.SqlQuery`. */
class SystemDataEntityDbSetSqlExpr extends SqlExpr, MethodCall {
SystemDataEntityDbSetSqlExpr() {
this.getTarget() = any(SystemDataEntity::DbSet dbSet).getSqlQueryMethod()
}
override Expr getSql() { result = this.getArgumentForName("sql") }
}
/** A call to a method in `System.Data.Entity.Database` that executes SQL. */
class SystemDataEntityDatabaseSqlExpr extends SqlExpr, MethodCall {
SystemDataEntityDatabaseSqlExpr() {
exists(SystemDataEntity::Database db |
this.getTarget() = db.getSqlQueryMethod() or
this.getTarget() = db.getExecuteSqlCommandMethod() or
this.getTarget() = db.getExecuteSqlCommandAsyncMethod()
)
}
override Expr getSql() { result = this.getArgumentForName("sql") }
}
/**
* A dataflow node whereby data flows from a property write to a property read
* via some database. The assumption is that all writes can flow to all reads.
*/
class MappedPropertyJumpNode extends DataFlow::NonLocalJumpNode {
MappedProperty property;
MappedPropertyJumpNode() { this.asExpr() = property.getAnAssignedValue() }
override DataFlow::Node getAJumpSuccessor(boolean preservesValue) {
result.asExpr().(PropertyRead).getTarget() = property and
preservesValue = false
}
}
}

View File

@@ -2,8 +2,8 @@
import csharp
private import semmle.code.csharp.frameworks.system.Data
private import semmle.code.csharp.frameworks.system.data.Entity
private import semmle.code.csharp.frameworks.system.data.SqlClient
private import semmle.code.csharp.frameworks.EntityFramework
/** An expression containing a SQL command. */
abstract class SqlExpr extends Expr {
@@ -22,7 +22,7 @@ class CommandTextAssignmentSqlExpr extends SqlExpr, AssignExpr {
)
}
override Expr getSql() { result = getRValue() }
override Expr getSql() { result = this.getRValue() }
}
/** A construction of an `IDbCommand` object. */
@@ -34,7 +34,7 @@ class IDbCommandConstructionSqlExpr extends SqlExpr, ObjectCreation {
)
}
override Expr getSql() { result = getArgument(0) }
override Expr getSql() { result = this.getArgument(0) }
}
/** A construction of an `SqlDataAdapter` object. */
@@ -47,7 +47,7 @@ class SqlDataAdapterConstructionSqlExpr extends SqlExpr, ObjectCreation {
)
}
override Expr getSql() { result = getArgument(0) }
override Expr getSql() { result = this.getArgument(0) }
}
/** A `MySql.Data.MySqlClient.MySqlHelper` method. */
@@ -83,25 +83,3 @@ class MicrosoftSqlHelperMethodCallSqlExpr extends SqlExpr, MethodCall {
)
}
}
/** A call to `System.Data.Entity.DbSet.SqlQuery`. */
class SystemDataEntityDbSetSqlExpr extends SqlExpr, MethodCall {
SystemDataEntityDbSetSqlExpr() {
this.getTarget() = any(SystemDataEntity::DbSet dbSet).getSqlQueryMethod()
}
override Expr getSql() { result = getArgumentForName("sql") }
}
/** A call to a method in `System.Data.Entity.Database` that executes SQL. */
class SystemDataEntityDatabaseSqlExpr extends SqlExpr, MethodCall {
SystemDataEntityDatabaseSqlExpr() {
exists(SystemDataEntity::Database db |
this.getTarget() = db.getSqlQueryMethod() or
this.getTarget() = db.getExecuteSqlCommandMethod() or
this.getTarget() = db.getExecuteSqlCommandAsyncMethod()
)
}
override Expr getSql() { result = getArgumentForName("sql") }
}

View File

@@ -5,18 +5,12 @@
import csharp
private import semmle.code.csharp.frameworks.system.data.Common
private import semmle.code.csharp.frameworks.system.data.Entity
private import semmle.code.csharp.frameworks.EntityFramework
private import semmle.code.csharp.frameworks.Sql
/** A data flow source of stored user input. */
abstract class StoredFlowSource extends DataFlow::Node { }
/** An access of an Entity Framework `Entity` property that may hold stored data. */
class EntityPropertyStoredFlowSource extends StoredFlowSource {
EntityPropertyStoredFlowSource() {
this.asExpr().(PropertyAccess).getTarget() = any(SystemDataEntity::Entity e).getAProperty()
}
}
/**
* An expression that has a type of `DbRawSqlQuery`, representing the result of an Entity Framework
* SqlQuery.
@@ -37,9 +31,7 @@ class DbDataReaderStoredFlowSource extends StoredFlowSource {
}
}
/**
* An expression that accesses a method of `DbDataReader` or a sub-class.
*/
/** An expression that accesses a method of `DbDataReader` or a sub-class. */
class DbDataReaderMethodStoredFlowSource extends StoredFlowSource {
DbDataReaderMethodStoredFlowSource() {
this.asExpr().(MethodCall).getTarget().getDeclaringType() = any(SystemDataCommon::DbDataReader dataReader
@@ -47,12 +39,15 @@ class DbDataReaderMethodStoredFlowSource extends StoredFlowSource {
}
}
/**
* An expression that accesses a property of `DbDataReader` or a sub-class.
*/
/** An expression that accesses a property of `DbDataReader` or a sub-class. */
class DbDataReaderPropertyStoredFlowSource extends StoredFlowSource {
DbDataReaderPropertyStoredFlowSource() {
this.asExpr().(PropertyAccess).getTarget().getDeclaringType() = any(SystemDataCommon::DbDataReader dataReader
).getASubType*()
}
}
/** A read of a mapped property. */
class EntityFrameworkMappedProperty extends StoredFlowSource {
EntityFrameworkMappedProperty() { this instanceof EntityFramework::StoredFlowSource }
}

View File

@@ -166,4 +166,4 @@ class SplittingStressTest
;
;
}
}
}

View File

@@ -0,0 +1,7 @@
| EntityFramework.cs:52:18:52:24 | access to property Name | EntityFramework.cs:47:34:47:42 | "tainted" |
| EntityFramework.cs:53:18:53:34 | access to property Name | EntityFramework.cs:47:34:47:42 | "tainted" |
| EntityFrameworkCore.cs:50:18:50:28 | access to local variable taintSource | EntityFrameworkCore.cs:47:31:47:39 | "tainted" |
| EntityFrameworkCore.cs:51:18:51:46 | (...) ... | EntityFrameworkCore.cs:47:31:47:39 | "tainted" |
| EntityFrameworkCore.cs:52:18:52:42 | (...) ... | EntityFrameworkCore.cs:47:31:47:39 | "tainted" |
| EntityFrameworkCore.cs:60:18:60:24 | access to property Name | EntityFrameworkCore.cs:47:31:47:39 | "tainted" |
| EntityFrameworkCore.cs:61:18:61:34 | access to property Name | EntityFrameworkCore.cs:47:31:47:39 | "tainted" |

View File

@@ -0,0 +1,16 @@
import csharp
import semmle.code.csharp.dataflow.TaintTracking
class MyConfiguration extends TaintTracking::Configuration {
MyConfiguration() { this = "EntityFramework dataflow" }
override predicate isSource(DataFlow::Node node) { node.asExpr().getValue() = "tainted" }
override predicate isSink(DataFlow::Node node) {
node.asExpr() = any(MethodCall c | c.getTarget().hasName("Sink")).getAnArgument()
}
}
from MyConfiguration config, DataFlow::Node source, DataFlow::Node sink
where config.hasFlow(source, sink)
select sink, source

View File

@@ -0,0 +1,64 @@
// semmle-extractor-options: /r:System.Data.dll /r:System.ComponentModel.Primitives.dll /r:System.ComponentModel.TypeConverter.dll ${testdir}/../../../resources/stubs/EntityFramework.cs ${testdir}/../../../resources/stubs/System.Data.cs /r:System.ComponentModel.TypeConverter.dll /r:System.Data.Common.dll
using System.Data.Entity;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data;
using System.Data.Common;
namespace EFTests
{
class Person
{
public int Id { get; set; }
public string Name { get; set; }
[NotMapped]
public int Age { get; set; }
}
class MyContext : DbContext
{
DbSet<Person> person { get; set; }
void FlowSources()
{
var p = new Person();
var id = p.Id; // Remote flow source
var name = p.Name; // Remote flow source
var age = p.Age; // Not a remote flow source
}
DbCommand command;
async void SqlSinks()
{
// System.Data.Common.DbCommand.set_CommandText
command.CommandText = ""; // SqlExpr
// System.Data.SqlClient.SqlCommand.SqlCommand
new System.Data.SqlClient.SqlCommand(""); // SqlExpr
this.Database.ExecuteSqlCommand(""); // SqlExpr
await this.Database.ExecuteSqlCommandAsync(""); // SqlExpr
}
void TestDataFlow()
{
string taintSource = "tainted";
// Tainted via database, even though technically there were no reads or writes to the database in this particular case.
var p1 = new Person { Name = taintSource };
var p2 = new Person();
Sink(p2.Name); // Tainted
Sink(new Person().Name); // Tainted
p1.Age = int.Parse(taintSource);
Sink(p2.Age); // Not tainted due to NotMappedAttribute
}
void Sink(object @object)
{
}
}
}

View File

@@ -0,0 +1,71 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Common;
namespace EFCoreTests
{
class Person
{
public int Id { get; set; }
public string Name { get; set; }
[NotMapped]
public int Age { get; set; }
}
class MyContext : DbContext
{
DbSet<Person> person;
void FlowSources()
{
var p = new Person();
var id = p.Id; // Remote flow source
var name = p.Name; // Remote flow source
var age = p.Age; // Not a remote flow source
}
Microsoft.EntityFrameworkCore.Storage.IRawSqlCommandBuilder builder;
async void SqlExprs()
{
// Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.ExecuteSqlCommand
this.Database.ExecuteSqlCommand(""); // SqlExpr
await this.Database.ExecuteSqlCommandAsync(""); // SqlExpr
// Microsoft.EntityFrameworkCore.Storage.IRawSqlCommandBuilder.Build
builder.Build(""); // SqlExpr
// Microsoft.EntityFrameworkCore.RawSqlString
new RawSqlString(""); // SqlExpr
RawSqlString str = ""; // SqlExpr
}
void TestDataFlow()
{
var taintSource = "tainted";
var untaintedSource = "untainted";
Sink(taintSource); // Tainted
Sink(new RawSqlString(taintSource)); // Tainted
Sink((RawSqlString)taintSource); // Tainted
Sink((RawSqlString)(FormattableString)$"{taintSource}"); // Not tainted
// Tainted via database, even though technically there were no reads or writes to the database in this particular case.
var p1 = new Person { Name = taintSource };
p1.Name = untaintedSource;
var p2 = new Person();
Sink(p2.Name); // Tainted
Sink(new Person().Name); // Tainted
p1.Age = int.Parse(taintSource);
Sink(p2.Age); // Not tainted due to NotMappedAttribute
}
void Sink(object @object)
{
}
}
}

View File

@@ -0,0 +1,4 @@
| EntityFramework.cs:12:20:12:21 | Id |
| EntityFramework.cs:13:23:13:26 | Name |
| EntityFrameworkCore.cs:10:18:10:19 | Id |
| EntityFrameworkCore.cs:11:21:11:24 | Name |

View File

@@ -0,0 +1,5 @@
import csharp
import semmle.code.csharp.frameworks.EntityFramework
from EntityFramework::MappedProperty property
select property

View File

@@ -0,0 +1,11 @@
| EntityFramework.cs:36:13:36:36 | ... = ... |
| EntityFramework.cs:39:13:39:52 | object creation of type SqlCommand |
| EntityFramework.cs:41:13:41:47 | call to method ExecuteSqlCommand |
| EntityFramework.cs:42:19:42:58 | call to method ExecuteSqlCommandAsync |
| EntityFrameworkCore.cs:34:13:34:47 | call to method ExecuteSqlCommand |
| EntityFrameworkCore.cs:35:19:35:58 | call to method ExecuteSqlCommandAsync |
| EntityFrameworkCore.cs:38:13:38:29 | call to method Build |
| EntityFrameworkCore.cs:41:13:41:32 | object creation of type RawSqlString |
| EntityFrameworkCore.cs:42:32:42:33 | call to operator implicit conversion |
| EntityFrameworkCore.cs:51:18:51:46 | object creation of type RawSqlString |
| EntityFrameworkCore.cs:52:18:52:42 | call to operator implicit conversion |

View File

@@ -0,0 +1,5 @@
import csharp
import semmle.code.csharp.frameworks.Sql
from SqlExpr expr
select expr

View File

@@ -0,0 +1,8 @@
| EntityFramework.cs:26:22:26:25 | access to property Id |
| EntityFramework.cs:27:24:27:29 | access to property Name |
| EntityFramework.cs:52:18:52:24 | access to property Name |
| EntityFramework.cs:53:18:53:34 | access to property Name |
| EntityFrameworkCore.cs:24:22:24:25 | access to property Id |
| EntityFrameworkCore.cs:25:24:25:29 | access to property Name |
| EntityFrameworkCore.cs:60:18:60:24 | access to property Name |
| EntityFrameworkCore.cs:61:18:61:34 | access to property Name |

View File

@@ -0,0 +1,5 @@
import csharp
import semmle.code.csharp.security.dataflow.flowsources.Stored
from StoredFlowSource source
select source

View File

@@ -32,14 +32,14 @@ public class BloggingContext : DbContext
DbRawSqlQuery<Blog> blogs = Database.SqlQuery<Blog>("SELECT * FROM Blogs");
foreach (var blog in blogs)
{
// This will be a sink because it is an access of an entity property
// This will be a source because it is an access of an entity property
Console.WriteLine(blog.Name);
}
DbRawSqlQuery<string> blogNames = Database.SqlQuery<string>("SELECT Name FROM Blogs");
foreach (var blogName in blogNames)
{
// This will be a sink because it is returned from an SqlQuery
// This will be a source because it is returned from an SqlQuery
Console.WriteLine(blogName);
}
}

View File

@@ -44,7 +44,7 @@ class Program
{
if (obj2 == null)
{
obj2 = null;
obj2 = null;
}
}
}

View File

@@ -55,6 +55,6 @@ public class CommandInjectionHandler : IHttpHandler
void OnButtonClicked()
{
// BAD: Use the Roslyn APIs to dynamically evaluate C#
CSharpScript.EvaluateAsync(box1.Text);
CSharpScript.EvaluateAsync(box1.Text);
}
}

View File

@@ -5,8 +5,8 @@ nodes
| CodeInjection.cs:25:23:25:45 | access to property QueryString |
| CodeInjection.cs:31:64:31:67 | access to local variable code |
| CodeInjection.cs:42:36:42:39 | access to local variable code |
| CodeInjection.cs:58:33:58:41 | access to property Text |
| CodeInjection.cs:58:36:58:44 | access to property Text |
#select
| CodeInjection.cs:31:64:31:67 | access to local variable code | CodeInjection.cs:25:23:25:45 | access to property QueryString | CodeInjection.cs:31:64:31:67 | access to local variable code | $@ flows to here and is compiled as code. | CodeInjection.cs:25:23:25:45 | access to property QueryString | User-provided value |
| CodeInjection.cs:42:36:42:39 | access to local variable code | CodeInjection.cs:25:23:25:45 | access to property QueryString | CodeInjection.cs:42:36:42:39 | access to local variable code | $@ flows to here and is compiled as code. | CodeInjection.cs:25:23:25:45 | access to property QueryString | User-provided value |
| CodeInjection.cs:58:33:58:41 | access to property Text | CodeInjection.cs:58:33:58:41 | access to property Text | CodeInjection.cs:58:33:58:41 | access to property Text | $@ flows to here and is compiled as code. | CodeInjection.cs:58:33:58:41 | access to property Text | User-provided value |
| CodeInjection.cs:58:36:58:44 | access to property Text | CodeInjection.cs:58:36:58:44 | access to property Text | CodeInjection.cs:58:36:58:44 | access to property Text | $@ flows to here and is compiled as code. | CodeInjection.cs:58:36:58:44 | access to property Text | User-provided value |

View File

@@ -9,16 +9,16 @@ public class TaintedPathHandler : IHttpHandler
public void ProcessRequest(HttpContext ctx)
{
String path = ctx.Request.QueryString["page"];
// BAD: Uncontrolled format string.
String.Format(path, "Do not do this");
// BAD: Using an IFormatProvider.
String.Format((IFormatProvider)null, path, "Do not do this");
// GOOD: Not the format string.
String.Format("Do not do this", path);
// GOOD: Not the format string.
String.Format((IFormatProvider)null, "Do not do this", path);
}
@@ -27,7 +27,7 @@ public class TaintedPathHandler : IHttpHandler
void OnButtonClicked()
{
// BAD: Uncontrolled format string.
String.Format(box1.Text, "Do not do this");
// BAD: Uncontrolled format string.
String.Format(box1.Text, "Do not do this");
}
}

View File

@@ -8,11 +8,11 @@ nodes
| UncontrolledFormatString.cs:17:46:17:49 | access to local variable path |
| UncontrolledFormatString.cs:20:23:20:38 | "Do not do this" |
| UncontrolledFormatString.cs:23:46:23:61 | "Do not do this" |
| UncontrolledFormatString.cs:31:20:31:28 | access to property Text |
| UncontrolledFormatString.cs:31:23:31:31 | access to property Text |
| UncontrolledFormatStringBad.cs:9:25:9:47 | access to property QueryString |
| UncontrolledFormatStringBad.cs:12:39:12:44 | access to local variable format |
#select
| UncontrolledFormatString.cs:14:23:14:26 | access to local variable path | UncontrolledFormatString.cs:11:23:11:45 | access to property QueryString | UncontrolledFormatString.cs:14:23:14:26 | access to local variable path | $@ flows to here and is used as a format string. | UncontrolledFormatString.cs:11:23:11:45 | access to property QueryString | access to property QueryString |
| UncontrolledFormatString.cs:17:46:17:49 | access to local variable path | UncontrolledFormatString.cs:11:23:11:45 | access to property QueryString | UncontrolledFormatString.cs:17:46:17:49 | access to local variable path | $@ flows to here and is used as a format string. | UncontrolledFormatString.cs:11:23:11:45 | access to property QueryString | access to property QueryString |
| UncontrolledFormatString.cs:31:20:31:28 | access to property Text | UncontrolledFormatString.cs:31:20:31:28 | access to property Text | UncontrolledFormatString.cs:31:20:31:28 | access to property Text | $@ flows to here and is used as a format string. | UncontrolledFormatString.cs:31:20:31:28 | access to property Text | access to property Text |
| UncontrolledFormatString.cs:31:23:31:31 | access to property Text | UncontrolledFormatString.cs:31:23:31:31 | access to property Text | UncontrolledFormatString.cs:31:23:31:31 | access to property Text | $@ flows to here and is used as a format string. | UncontrolledFormatString.cs:31:23:31:31 | access to property Text | access to property Text |
| UncontrolledFormatStringBad.cs:12:39:12:44 | access to local variable format | UncontrolledFormatStringBad.cs:9:25:9:47 | access to property QueryString | UncontrolledFormatStringBad.cs:12:39:12:44 | access to local variable format | $@ flows to here and is used as a format string. | UncontrolledFormatStringBad.cs:9:25:9:47 | access to property QueryString | access to property QueryString |

View File

@@ -7,7 +7,7 @@ public class HttpHandler : IHttpHandler
public void ProcessRequest(HttpContext ctx)
{
string format = ctx.Request.QueryString["nameformat"];
// BAD: Uncontrolled format string.
FormattedName = string.Format(format, Surname, Forenames);
}

View File

@@ -1,6 +1,8 @@
using System.Collections.Generic;
using System.Collections;
using System.ComponentModel;
using System.Threading.Tasks;
using System;
namespace System.Data.Entity
{
@@ -16,6 +18,7 @@ namespace System.Data.Entity
{
public int ExecuteSqlQuery(string sql, params object[] parameters) => 0;
public int ExecuteSqlCommand(string sql, params object[] parameters) => 0;
public async Task ExecuteSqlCommandAsync(string sql, params object[] parameters) => throw null;
public Infrastructure.DbRawSqlQuery<T> SqlQuery<T>(string sql, params object[] parameters) => null;
}
@@ -41,3 +44,52 @@ namespace System.Data.Entity.Infrastructure
IList IListSource.GetList() => null;
}
}
namespace Microsoft.EntityFrameworkCore
{
public class DbSet<T>
{
}
public class DbContext : IDisposable
{
public void Dispose() { }
public virtual Infrastructure.DatabaseFacade Database => null;
// public Infrastructure.DbRawSqlQuery<TElement> SqlQuery<TElement>(string sql, params object[] parameters) => null;
}
namespace Infrastructure
{
public class DatabaseFacade
{
}
}
public static class RelationalDatabaseFacaseExtensions
{
public static void ExecuteSqlCommand(this Infrastructure.DatabaseFacade db, string sql, params object[] parameters) {}
public static Task ExecuteSqlCommandAsync(this Infrastructure.DatabaseFacade db, string sql, params object[] parameters) => throw null;
}
struct RawSqlString
{
public RawSqlString(string str) { }
public static implicit operator Microsoft.EntityFrameworkCore.RawSqlString (FormattableString fs) => throw null;
public static implicit operator Microsoft.EntityFrameworkCore.RawSqlString (string s) => throw null;
}
}
namespace System.ComponentModel.DataAnnotations.Schema
{
class NotMappedAttribute : Attribute
{
}
}
namespace Microsoft.EntityFrameworkCore.Storage
{
interface IRawSqlCommandBuilder
{
void Build(string sql);
}
}

View File

@@ -43,7 +43,7 @@ namespace Newtonsoft.Json.Linq
public static explicit operator string(JToken t) => null;
public IEnumerable<JToken> SelectToken(string s) => null;
public IEnumerable<JToken> SelectToken(string s) => null;
}
public class JObject : JToken

View File

@@ -71,6 +71,7 @@ namespace System.Data
IDataReader ExecuteReader();
CommandType CommandType { get; set; }
IDataParameterCollection Parameters { get; set; }
string CommandText { get; set; }
}
public interface IDataReader
@@ -117,13 +118,14 @@ namespace System.Data.Common
public virtual string GetString(int i) => "";
}
public class DbCommand : IDbCommand, IDisposable
public abstract class DbCommand : IDbCommand, IDisposable
{
public DbDataReader ExecuteReader() => null;
public CommandType CommandType { get; set; }
public IDataParameterCollection Parameters { get; set; }
IDataReader IDbCommand.ExecuteReader() => null;
public void Dispose() { }
public string CommandText { get; set; }
}
public class DbDataAdapter : IDataAdapter, IDbDataAdapter

View File

@@ -31,7 +31,7 @@ namespace System.Windows.Forms
public char PasswordChar { get; set; }
public bool UseSystemPasswordChar { get; set; }
}
class RichTextBox : TextBoxBase
{
public string Rtf => null;

View File

@@ -35,7 +35,7 @@ or garbled output.
<p>
Use a (well-tested) sanitization library if at all possible. These libraries are much more
likely to handle corner cases correctly than a custom implementation. For URI encoding,
you can use the standard `encodeURIComponent` and `decodeURIComponent` functions.
you can use the standard <code>encodeURIComponent</code> and <code>decodeURIComponent</code> functions.
</p>
<p>
Otherwise, make sure to always escape the escape character first, and unescape it last.

View File

@@ -1221,4 +1221,5 @@ module DataFlow {
import TypeInference
import Configuration
import TrackedNodes
import TypeTracking
}

View File

@@ -7,6 +7,7 @@
*/
import javascript
private import semmle.javascript.dataflow.TypeTracking
/**
* A source node for local data flow, that is, a node from which local data flow is tracked.
@@ -153,6 +154,34 @@ class SourceNode extends DataFlow::Node {
DataFlow::SourceNode getAPropertySource(string prop) {
result.flowsTo(getAPropertyWrite(prop).getRhs())
}
/**
* EXPERIMENTAL.
*
* 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.
*/
DataFlow::SourceNode track(TypeTracker t2, TypeTracker t) {
exists(StepSummary summary |
StepSummary::step(this, result, summary) and
t = StepSummary::append(t2, summary)
)
}
/**
* EXPERIMENTAL.
*
* Gets a node that may flow into this one using one heap and/or interprocedural step.
*
* See `TypeBackTracker` for more details about how to use this.
*/
DataFlow::SourceNode backtrack(TypeBackTracker t2, TypeBackTracker t) {
exists(StepSummary summary |
StepSummary::step(result, this, summary) and
t = StepSummary::prepend(summary, t2)
)
}
}
module SourceNode {

View File

@@ -4,6 +4,7 @@
*/
import javascript
private import internal.FlowSteps as FlowSteps
/**
* A data flow node that should be tracked inter-procedurally.

View File

@@ -0,0 +1,253 @@
/**
* Provides the `TypeTracker` class for tracking types interprocedurally.
*
* This provides an alternative to `DataFlow::TrackedNode` and `AbstractValue`
* for tracking certain types interprocedurally without computing which source
* a given value came from.
*/
import javascript
private import internal.FlowSteps
/**
* A pair of booleans, indicating whether a path goes through a return and/or a call.
*
* Identical to `TPathSummary` except without flow labels.
*/
private newtype TStepSummary = MkStepSummary(boolean hasReturn, boolean hasCall) {
(hasReturn = true or hasReturn = false) and
(hasCall = true or hasCall = false)
}
/**
* INTERNAL: Use `TypeTracker` or `TypeBackTracker` instead.
*
* Summary of the steps needed to track a value to a given dataflow node.
*/
class StepSummary extends TStepSummary {
Boolean hasReturn;
Boolean hasCall;
StepSummary() { this = MkStepSummary(hasReturn, hasCall) }
/** Indicates whether the path represented by this summary contains any return steps. */
boolean hasReturn() { result = hasReturn }
/** Indicates whether the path represented by this summary contains any call steps. */
boolean hasCall() { result = hasCall }
/**
* Gets the summary for the path obtained by appending `that` to `this`.
*
* Note that a path containing a `return` step cannot be appended to a path containing
* a `call` step in order to maintain well-formedness.
*/
StepSummary append(StepSummary that) {
exists(Boolean hasReturn2, Boolean hasCall2 |
that = MkStepSummary(hasReturn2, hasCall2)
|
result = MkStepSummary(hasReturn.booleanOr(hasReturn2), hasCall.booleanOr(hasCall2)) and
// avoid constructing invalid paths
not (hasCall = true and hasReturn2 = true)
)
}
/**
* Gets the summary for the path obtained by appending `this` to `that`.
*/
StepSummary prepend(StepSummary that) { result = that.append(this) }
/** Gets a textual representation of this path summary. */
string toString() {
exists(string withReturn, string withCall |
(if hasReturn = true then withReturn = "with" else withReturn = "without") and
(if hasCall = true then withCall = "with" else withCall = "without")
|
result = "path " + withReturn + " return steps and " + withCall + " call steps"
)
}
}
module StepSummary {
/**
* Gets a summary describing a path without any calls or returns.
*/
StepSummary level() { result = MkStepSummary(false, false) }
/**
* Gets a summary describing a path with one or more calls, but no returns.
*/
StepSummary call() { result = MkStepSummary(false, true) }
/**
* Gets a summary describing a path with one or more returns, but no calls.
*/
StepSummary return() { result = MkStepSummary(true, false) }
/**
* INTERNAL: Use `SourceNode.track()` or `SourceNode.backtrack()` instead.
*/
predicate step(DataFlow::SourceNode pred, DataFlow::SourceNode succ, StepSummary summary) {
exists (DataFlow::Node predNode | pred.flowsTo(predNode) |
// Flow through properties of objects
propertyFlowStep(predNode, succ) and
summary = level()
or
// Flow through global variables
globalFlowStep(predNode, succ) and
summary = level()
or
// Flow into function
callStep(predNode, succ) and
summary = call()
or
// Flow out of function
returnStep(predNode, succ) and
summary = return()
or
// Flow through an instance field between members of the same class
DataFlow::localFieldStep(predNode, succ) and
summary = level()
)
}
/**
* INTERNAL. Do not use.
*
* Appends a step summary onto a type-tracking summary.
*/
TypeTracker append(TypeTracker type, StepSummary summary) {
not (type.hasCall() = true and summary.hasReturn() = true) and
result.hasCall() = type.hasCall().booleanOr(summary.hasCall())
}
/**
* INTERNAL. Do not use.
*
* Prepends a step summary before a backwards type-tracking summary.
*/
TypeBackTracker prepend(StepSummary summary, TypeBackTracker type) {
not (type.hasReturn() = true and summary.hasCall() = true) and
result.hasReturn() = type.hasReturn().booleanOr(summary.hasReturn())
}
}
private newtype TTypeTracker = MkTypeTracker(boolean hasCall) {
hasCall = true or hasCall = false
}
/**
* EXPERIMENTAL.
*
* 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 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 is written on the following form,
* for tracking some type `myType`:
* ```
* DataFlow::SourceNode myType(DataFlow::TypeTracker t) {
* t.start() and
* result = < source of myType >
* or
* exists (DataFlow::TypeTracker t2 |
* result = myType(t2).track(t2, t)
* )
* }
*
* DataFlow::SourceNode myType() { result = myType(_) }
* ```
*
* To track values backwards, which can be useful for tracking
* the type of a callback, use the `TypeBackTracker` class instead.
*/
class TypeTracker extends TTypeTracker {
Boolean hasCall;
TypeTracker() { this = MkTypeTracker(hasCall) }
string toString() {
hasCall = true and result = "type tracker with call steps"
or
hasCall = false and result = "type tracker without call steps"
}
/**
* Holds if this is the starting point of type tracking.
*/
predicate start() {
hasCall = false
}
/**
* INTERNAL. DO NOT USE.
*
* Holds if this type has been tracked into a call.
*/
boolean hasCall() {
result = hasCall
}
}
private newtype TTypeBackTracker = MkTypeBackTracker(boolean hasReturn) {
hasReturn = true or hasReturn = false
}
/**
* EXPERIMENTAL.
*
* Summary of the steps needed to back-track a use of a value to a given dataflow node.
*
* This can be used to track callbacks that are passed to a certian API call, and are
* therefore expected to called with a certain type of value.
*
* Note that type back-tracking does not provide a source/sink relation, that is,
* it may determine that a node will be used in an API call somwwhere, but it won't
* determine exactly where that use was, or the path that led to the use.
*
* It is recommended that all uses of this type is written on the following form,
* for back-tracking some callback type `myCallback`:
* ```
* DataFlow::SourceNode myCallback(DataFlow::TypeBackTracker t) {
* t.start() and
* result = (< some API call >).getArgument(< n >).getALocalSource()
* or
* exists (DataFlow::TypeTracker t2 |
* result = myCallback(t2).backtrack(t2, t)
* )
* }
*
* DataFlow::SourceNode myCallback() { result = myCallback(_) }
* ```
*/
class TypeBackTracker extends TTypeBackTracker {
Boolean hasReturn;
TypeBackTracker() { this = MkTypeBackTracker(hasReturn) }
string toString() {
hasReturn = true and result = "type back-tracker with return steps"
or
hasReturn = false and result = "type back-tracker without return steps"
}
/**
* Holds if this is the starting point of type tracking.
*/
predicate start() {
hasReturn = false
}
/**
* INTERNAL. DO NOT USE.
*
* Holds if this type has been back-tracked into a call through return edge.
*/
boolean hasReturn() {
result = hasReturn
}
}

View File

@@ -47,9 +47,8 @@ AbstractValue getAnInitialPropertyValue(DefiniteAbstractValue baseVal, string pr
result = p.getInit().analyze().getALocalValue()
)
or
// `f.prototype` for functions `f` that are instantiated
// `f.prototype` for functions `f`
propertyName = "prototype" and
baseVal = any(NewExpr ne).getCallee().analyze().getALocalValue() and
result = TAbstractInstance(baseVal)
}

View File

@@ -0,0 +1,15 @@
import * as dummy from 'dummy';
function F() {
this.init();
}
F.prototype.init = function() {
this.method();
let m = this.method.bind(this);
m();
};
F.prototype.method = function() {};
export default F;

View File

@@ -12,6 +12,10 @@ test_isUncertain
| n.js:2:1:2:5 | m.f() |
| n.js:4:10:4:24 | require('./m2') |
| n.js:5:1:5:6 | m2.f() |
| protoclass.js:4:3:4:13 | this.init() |
| protoclass.js:8:3:8:15 | this.method() |
| protoclass.js:9:11:9:32 | this.me ... d(this) |
| protoclass.js:10:3:10:5 | m() |
| reflection.js:7:1:7:22 | add.cal ... 23, 19) |
| reflection.js:8:1:8:25 | add.app ... 3, 19]) |
| tst.js:6:1:6:3 | f() |
@@ -86,6 +90,18 @@ test_getAFunctionValue
| m.js:3:1:3:16 | module.exports.f | m.js:1:13:1:25 | function() {} |
| n.js:2:1:2:3 | m.f | m.js:1:13:1:25 | function() {} |
| n.js:5:1:5:4 | m2.f | m2.js:2:6:2:18 | function() {} |
| protoclass.js:3:1:5:1 | functio ... it();\\n} | protoclass.js:3:1:5:1 | functio ... it();\\n} |
| protoclass.js:3:10:3:10 | F | protoclass.js:3:1:5:1 | functio ... it();\\n} |
| protoclass.js:4:3:4:11 | this.init | protoclass.js:7:20:11:1 | functio ... m();\\n} |
| protoclass.js:7:1:7:1 | F | protoclass.js:3:1:5:1 | functio ... it();\\n} |
| protoclass.js:7:1:11:1 | F.proto ... m();\\n} | protoclass.js:7:20:11:1 | functio ... m();\\n} |
| protoclass.js:7:20:11:1 | functio ... m();\\n} | protoclass.js:7:20:11:1 | functio ... m();\\n} |
| protoclass.js:8:3:8:13 | this.method | protoclass.js:13:22:13:34 | function() {} |
| protoclass.js:9:11:9:21 | this.method | protoclass.js:13:22:13:34 | function() {} |
| protoclass.js:13:1:13:1 | F | protoclass.js:3:1:5:1 | functio ... it();\\n} |
| protoclass.js:13:1:13:34 | F.proto ... on() {} | protoclass.js:13:22:13:34 | function() {} |
| protoclass.js:13:22:13:34 | function() {} | protoclass.js:13:22:13:34 | function() {} |
| protoclass.js:15:16:15:16 | F | protoclass.js:3:1:5:1 | functio ... it();\\n} |
| reflection.js:1:1:3:1 | functio ... x+y;\\n} | reflection.js:1:1:3:1 | functio ... x+y;\\n} |
| reflection.js:5:3:5:5 | add | reflection.js:1:1:3:1 | functio ... x+y;\\n} |
| reflection.js:5:3:5:39 | add.app ... n 56; } | reflection.js:5:15:5:39 | functio ... n 56; } |
@@ -176,6 +192,7 @@ test_getArgument
| es2015.js:36:1:36:17 | sum(1, ...[2], 3) | 0 | es2015.js:36:5:36:5 | 1 |
| n.js:1:9:1:22 | require('./m') | 0 | n.js:1:17:1:21 | './m' |
| n.js:4:10:4:24 | require('./m2') | 0 | n.js:4:18:4:23 | './m2' |
| protoclass.js:9:11:9:32 | this.me ... d(this) | 0 | protoclass.js:9:28:9:31 | this |
| reflection.js:7:1:7:22 | add.cal ... 23, 19) | 0 | reflection.js:7:10:7:13 | null |
| reflection.js:7:1:7:22 | add.cal ... 23, 19) | 1 | reflection.js:7:16:7:17 | 23 |
| reflection.js:7:1:7:22 | add.cal ... 23, 19) | 2 | reflection.js:7:20:7:21 | 19 |
@@ -207,6 +224,10 @@ test_getNumArgument
| n.js:2:1:2:5 | m.f() | 0 |
| n.js:4:10:4:24 | require('./m2') | 1 |
| n.js:5:1:5:6 | m2.f() | 0 |
| protoclass.js:4:3:4:13 | this.init() | 0 |
| protoclass.js:8:3:8:15 | this.method() | 0 |
| protoclass.js:9:11:9:32 | this.me ... d(this) | 1 |
| protoclass.js:10:3:10:5 | m() | 0 |
| reflection.js:4:5:4:12 | sneaky() | 0 |
| reflection.js:7:1:7:22 | add.cal ... 23, 19) | 3 |
| reflection.js:7:1:7:22 | reflective call | 2 |
@@ -252,6 +273,10 @@ test_isIncomplete
| n.js:2:1:2:5 | m.f() |
| n.js:4:10:4:24 | require('./m2') |
| n.js:5:1:5:6 | m2.f() |
| protoclass.js:4:3:4:13 | this.init() |
| protoclass.js:8:3:8:15 | this.method() |
| protoclass.js:9:11:9:32 | this.me ... d(this) |
| protoclass.js:10:3:10:5 | m() |
| reflection.js:7:1:7:22 | add.cal ... 23, 19) |
| reflection.js:8:1:8:25 | add.app ... 3, 19]) |
| tst.js:6:1:6:3 | f() |
@@ -295,6 +320,10 @@ test_getCalleeNode
| n.js:2:1:2:5 | m.f() | n.js:2:1:2:3 | m.f |
| n.js:4:10:4:24 | require('./m2') | n.js:4:10:4:16 | require |
| n.js:5:1:5:6 | m2.f() | n.js:5:1:5:4 | m2.f |
| protoclass.js:4:3:4:13 | this.init() | protoclass.js:4:3:4:11 | this.init |
| protoclass.js:8:3:8:15 | this.method() | protoclass.js:8:3:8:13 | this.method |
| protoclass.js:9:11:9:32 | this.me ... d(this) | protoclass.js:9:11:9:26 | this.method.bind |
| protoclass.js:10:3:10:5 | m() | protoclass.js:10:3:10:3 | m |
| reflection.js:4:5:4:12 | sneaky() | reflection.js:4:5:4:10 | sneaky |
| reflection.js:7:1:7:22 | add.cal ... 23, 19) | reflection.js:7:1:7:8 | add.call |
| reflection.js:7:1:7:22 | reflective call | reflection.js:7:1:7:3 | add |
@@ -334,6 +363,7 @@ test_getLastArgument
| es2015.js:27:5:27:23 | console.log(this.x) | es2015.js:27:17:27:22 | this.x |
| n.js:1:9:1:22 | require('./m') | n.js:1:17:1:21 | './m' |
| n.js:4:10:4:24 | require('./m2') | n.js:4:18:4:23 | './m2' |
| protoclass.js:9:11:9:32 | this.me ... d(this) | protoclass.js:9:28:9:31 | this |
| reflection.js:7:1:7:22 | add.cal ... 23, 19) | reflection.js:7:20:7:21 | 19 |
| reflection.js:7:1:7:22 | reflective call | reflection.js:7:20:7:21 | 19 |
| reflection.js:8:1:8:25 | add.app ... 3, 19]) | reflection.js:8:17:8:24 | [23, 19] |
@@ -349,6 +379,7 @@ test_getAnArgument
| es2015.js:36:1:36:17 | sum(1, ...[2], 3) | es2015.js:36:16:36:16 | 3 |
| n.js:1:9:1:22 | require('./m') | n.js:1:17:1:21 | './m' |
| n.js:4:10:4:24 | require('./m2') | n.js:4:18:4:23 | './m2' |
| protoclass.js:9:11:9:32 | this.me ... d(this) | protoclass.js:9:28:9:31 | this |
| reflection.js:7:1:7:22 | add.cal ... 23, 19) | reflection.js:7:10:7:13 | null |
| reflection.js:7:1:7:22 | add.cal ... 23, 19) | reflection.js:7:16:7:17 | 23 |
| reflection.js:7:1:7:22 | add.cal ... 23, 19) | reflection.js:7:20:7:21 | 19 |
@@ -377,6 +408,8 @@ test_getACallee
| m.js:3:1:3:18 | module.exports.f() | m.js:1:13:1:25 | function() {} |
| n.js:2:1:2:5 | m.f() | m.js:1:13:1:25 | function() {} |
| n.js:5:1:5:6 | m2.f() | m2.js:2:6:2:18 | function() {} |
| protoclass.js:4:3:4:13 | this.init() | protoclass.js:7:20:11:1 | functio ... m();\\n} |
| protoclass.js:8:3:8:15 | this.method() | protoclass.js:13:22:13:34 | function() {} |
| reflection.js:7:1:7:22 | reflective call | reflection.js:1:1:3:1 | functio ... x+y;\\n} |
| reflection.js:8:1:8:25 | add.app ... 3, 19]) | reflection.js:5:15:5:39 | functio ... n 56; } |
| reflection.js:8:1:8:25 | reflective call | reflection.js:1:1:3:1 | functio ... x+y;\\n} |
@@ -430,6 +463,10 @@ test_getCalleeName
| n.js:2:1:2:5 | m.f() | f |
| n.js:4:10:4:24 | require('./m2') | require |
| n.js:5:1:5:6 | m2.f() | f |
| protoclass.js:4:3:4:13 | this.init() | init |
| protoclass.js:8:3:8:15 | this.method() | method |
| protoclass.js:9:11:9:32 | this.me ... d(this) | bind |
| protoclass.js:10:3:10:5 | m() | m |
| reflection.js:4:5:4:12 | sneaky() | sneaky |
| reflection.js:7:1:7:22 | add.cal ... 23, 19) | call |
| reflection.js:8:1:8:25 | add.app ... 3, 19]) | apply |

View File

@@ -1,6 +1,89 @@
| ChatListScreen.js:3:1:5:1 | instance of function foo | ChatListScreen.js:3:1:5:1 | instance of function foo |
| a.js:3:8:5:1 | instance of function setX | a.js:3:8:5:1 | instance of function setX |
| a.js:15:1:17:1 | instance of function bump | a.js:15:1:17:1 | instance of function bump |
| amd2.js:1:8:3:1 | instance of anonymous function | amd2.js:1:8:3:1 | instance of anonymous function |
| amd3.js:1:24:4:1 | instance of anonymous function | amd3.js:1:24:4:1 | instance of anonymous function |
| amd.js:1:31:6:1 | instance of anonymous function | amd.js:1:31:6:1 | instance of anonymous function |
| arguments.js:1:2:3:1 | instance of anonymous function | arguments.js:1:2:3:1 | instance of anonymous function |
| arguments.js:5:2:8:1 | instance of anonymous function | arguments.js:5:2:8:1 | instance of anonymous function |
| arguments.js:10:2:14:1 | instance of anonymous function | arguments.js:10:2:14:1 | instance of anonymous function |
| arguments.js:16:2:19:1 | instance of anonymous function | arguments.js:16:2:19:1 | instance of anonymous function |
| arguments.js:22:2:28:1 | instance of anonymous function | arguments.js:22:2:28:1 | instance of anonymous function |
| arguments.js:30:2:33:1 | instance of anonymous function | arguments.js:30:2:33:1 | instance of anonymous function |
| c.js:3:1:5:1 | instance of function f | c.js:3:1:5:1 | instance of function f |
| destructuring.js:1:1:4:1 | instance of function f | destructuring.js:1:1:4:1 | instance of function f |
| es2015.js:8:1:16:1 | instance of class Sub | es2015.js:1:11:6:1 | instance of class Sup |
| es2015.js:18:1:20:1 | instance of function f | es2015.js:18:1:20:1 | instance of function f |
| es2015.js:22:2:24:1 | instance of anonymous function | es2015.js:22:2:24:1 | instance of anonymous function |
| es2015.js:31:2:35:1 | instance of anonymous function | es2015.js:31:2:35:1 | instance of anonymous function |
| es2015.js:38:2:42:1 | instance of anonymous function | es2015.js:38:2:42:1 | instance of anonymous function |
| es2015.js:44:2:47:1 | instance of anonymous function | es2015.js:44:2:47:1 | instance of anonymous function |
| esLib.js:3:8:3:24 | instance of function foo | esLib.js:3:8:3:24 | instance of function foo |
| f.js:1:13:1:25 | instance of anonymous function | f.js:1:13:1:25 | instance of anonymous function |
| fundecls.js:1:2:10:1 | instance of anonymous function | fundecls.js:1:2:10:1 | instance of anonymous function |
| fundecls.js:4:3:4:17 | instance of function f | fundecls.js:4:3:4:17 | instance of function f |
| fundecls.js:8:5:8:19 | instance of function g | fundecls.js:8:5:8:19 | instance of function g |
| globals.html:7:8:10:7 | instance of anonymous function | globals.html:7:8:10:7 | instance of anonymous function |
| globals.html:16:8:19:7 | instance of anonymous function | globals.html:16:8:19:7 | instance of anonymous function |
| globals.html:22:7:22:21 | instance of function x | globals.html:22:7:22:21 | instance of function x |
| globals.html:26:23:26:69 | instance of anonymous function | globals.html:26:23:26:69 | instance of anonymous function |
| h.js:1:8:1:22 | instance of function f | h.js:1:8:1:22 | instance of function f |
| instances.js:1:1:4:1 | instance of function A | instances.js:1:1:4:1 | instance of function A |
| instances.js:3:14:3:26 | instance of anonymous function | instances.js:3:14:3:26 | instance of anonymous function |
| instances.js:6:19:6:31 | instance of anonymous function | instances.js:6:19:6:31 | instance of anonymous function |
| instances.js:13:1:13:18 | instance of function SubA | instances.js:1:1:4:1 | instance of function A |
| instances.js:13:1:13:18 | instance of function SubA | instances.js:13:1:13:18 | instance of function SubA |
| n.js:1:1:1:15 | instance of function f | n.js:1:1:1:15 | instance of function f |
| n.js:2:1:2:15 | instance of function g | n.js:2:1:2:15 | instance of function g |
| nestedImport.js:9:1:12:1 | instance of function tst | nestedImport.js:9:1:12:1 | instance of function tst |
| nodeJsLib.js:1:18:1:43 | instance of function nodeJsModule | nodeJsLib.js:1:18:1:43 | instance of function nodeJsModule |
| nodeJsLib.js:3:15:3:37 | instance of function nodeJsFoo | nodeJsLib.js:3:15:3:37 | instance of function nodeJsFoo |
| objlit.js:2:9:2:21 | instance of anonymous function | objlit.js:2:9:2:21 | instance of anonymous function |
| objlit.js:4:8:4:20 | instance of method baz | objlit.js:4:8:4:20 | instance of method baz |
| objlit.js:10:2:12:1 | instance of anonymous function | objlit.js:10:2:12:1 | instance of anonymous function |
| objlit.js:11:11:11:23 | instance of anonymous function | objlit.js:11:11:11:23 | instance of anonymous function |
| objlit.js:23:2:48:1 | instance of anonymous function | objlit.js:23:2:48:1 | instance of anonymous function |
| objlit.js:41:10:41:22 | instance of anonymous function | objlit.js:41:10:41:22 | instance of anonymous function |
| objlit.js:43:12:45:3 | object literal | file://:0:0:0:0 | null |
| objlit.js:43:12:45:3 | object literal | objlit.js:33:12:40:3 | object literal |
| refinements.js:1:1:8:1 | instance of function f1 | refinements.js:1:1:8:1 | instance of function f1 |
| refinements.js:10:1:24:1 | instance of function f2 | refinements.js:10:1:24:1 | instance of function f2 |
| refinements.js:26:1:32:1 | instance of function f3 | refinements.js:26:1:32:1 | instance of function f3 |
| refinements.js:34:1:40:1 | instance of function f4 | refinements.js:34:1:40:1 | instance of function f4 |
| refinements.js:35:20:35:31 | instance of anonymous function | refinements.js:35:20:35:31 | instance of anonymous function |
| refinements.js:42:1:56:1 | instance of function f5 | refinements.js:42:1:56:1 | instance of function f5 |
| refinements.js:44:3:48:3 | instance of function inner | refinements.js:44:3:48:3 | instance of function inner |
| refinements.js:58:1:62:1 | instance of function f6 | refinements.js:58:1:62:1 | instance of function f6 |
| ts2.ts:1:10:1:22 | instance of anonymous function | ts2.ts:1:10:1:22 | instance of anonymous function |
| tst2.js:3:2:5:1 | instance of anonymous function | tst2.js:3:2:5:1 | instance of anonymous function |
| tst.js:1:1:39:1 | instance of function tst | tst.js:1:1:39:1 | instance of function tst |
| tst.js:15:12:15:23 | instance of function xd | tst.js:15:12:15:23 | instance of function xd |
| tst.js:42:1:44:1 | instance of function nonstrict | tst.js:42:1:44:1 | instance of function nonstrict |
| tst.js:46:1:49:1 | instance of function strict | tst.js:46:1:49:1 | instance of function strict |
| tst.js:51:1:57:1 | instance of function capturedFn | tst.js:51:1:57:1 | instance of function capturedFn |
| tst.js:52:3:52:24 | instance of function captured | tst.js:52:3:52:24 | instance of function captured |
| tst.js:53:3:55:3 | instance of function capturing | tst.js:53:3:55:3 | instance of function capturing |
| tst.js:59:2:62:1 | instance of anonymous function | tst.js:59:2:62:1 | instance of anonymous function |
| tst.js:64:2:66:1 | instance of anonymous function | tst.js:64:2:66:1 | instance of anonymous function |
| tst.js:68:2:70:1 | instance of anonymous function | tst.js:68:2:70:1 | instance of anonymous function |
| tst.js:72:2:76:1 | instance of function s | tst.js:72:2:76:1 | instance of function s |
| tst.js:78:2:82:1 | instance of anonymous function | tst.js:78:2:82:1 | instance of anonymous function |
| tst.js:84:2:87:1 | instance of anonymous function | tst.js:84:2:87:1 | instance of anonymous function |
| tst.js:89:2:92:1 | instance of anonymous function | tst.js:89:2:92:1 | instance of anonymous function |
| tst.js:94:2:104:1 | instance of anonymous function | tst.js:94:2:104:1 | instance of anonymous function |
| tst.js:95:3:97:3 | instance of function inner | tst.js:95:3:97:3 | instance of function inner |
| tst.js:106:2:109:1 | instance of anonymous function | tst.js:106:2:109:1 | instance of anonymous function |
| tst.js:111:1:113:1 | instance of function tst | tst.js:111:1:113:1 | instance of function tst |
| tst.js:115:2:132:1 | instance of anonymous function | tst.js:115:2:132:1 | instance of anonymous function |
| tst.js:116:12:118:3 | instance of anonymous function | tst.js:116:12:118:3 | instance of anonymous function |
| tst.js:119:12:121:3 | instance of anonymous function | tst.js:119:12:121:3 | instance of anonymous function |
| tst.js:122:12:124:3 | instance of anonymous function | tst.js:122:12:124:3 | instance of anonymous function |
| tst.js:125:12:125:24 | instance of anonymous function | tst.js:125:12:125:24 | instance of anonymous function |
| tst.js:126:12:130:3 | instance of anonymous function | tst.js:126:12:130:3 | instance of anonymous function |
| tst.js:131:12:131:37 | instance of anonymous function | tst.js:131:12:131:37 | instance of anonymous function |
| tst.js:134:1:142:1 | instance of function tst2 | tst.js:134:1:142:1 | instance of function tst2 |
| tst.js:144:1:149:1 | instance of function tst3 | tst.js:144:1:149:1 | instance of function tst3 |
| tst.js:151:1:162:1 | instance of function tst4 | tst.js:151:1:162:1 | instance of function tst4 |
| tst.js:164:1:172:1 | instance of function tst5 | tst.js:164:1:172:1 | instance of function tst5 |
| tst.ts:8:1:10:1 | instance of function setX | tst.ts:8:1:10:1 | instance of function setX |
| with.js:1:1:17:1 | instance of function f | with.js:1:1:17:1 | instance of function f |

View File

@@ -0,0 +1,26 @@
test_ApiObject
| tst.js:3:11:3:21 | new myapi() |
| tst.js:15:10:15:21 | api.chain1() |
| tst.js:15:10:15:30 | api.cha ... hain2() |
test_Connection
| tst.js:6:15:6:18 | conn |
| tst.js:10:5:10:19 | this.connection |
| tst.js:15:10:15:49 | api.cha ... ction() |
| tst.js:18:7:18:21 | getConnection() |
| tst.js:30:9:30:23 | getConnection() |
| tst.js:39:7:39:21 | getConnection() |
| tst.js:47:7:47:21 | getConnection() |
test_DataCallback
| tst.js:9:11:9:12 | cb |
| tst.js:20:1:22:1 | functio ... ata);\\n} |
| tst.js:29:26:29:27 | cb |
| tst.js:32:17:32:26 | data => {} |
| tst.js:37:10:37:19 | data => {} |
| tst.js:39:32:39:45 | getDataCurry() |
| tst.js:44:19:44:20 | cb |
| tst.js:47:32:47:60 | identit ... llback) |
test_DataValue
| tst.js:20:18:20:21 | data |
| tst.js:24:19:24:22 | data |
| tst.js:32:17:32:20 | data |
| tst.js:37:10:37:13 | data |

View File

@@ -0,0 +1,91 @@
import javascript
string chainableMethod() {
result = "chain1" or
result = "chain2"
}
class ApiObject extends DataFlow::NewNode {
ApiObject() {
this = DataFlow::moduleImport("@test/myapi").getAnInstantiation()
}
DataFlow::SourceNode ref(DataFlow::TypeTracker t) {
t.start() and
result = this
or
t.start() and
result = ref(_).getAMethodCall(chainableMethod())
or
exists(DataFlow::TypeTracker t2 |
result = ref(t2).track(t2, t)
)
}
DataFlow::SourceNode ref() {
result = ref(_)
}
}
class Connection extends DataFlow::SourceNode {
ApiObject api;
Connection() {
this = api.ref().getAMethodCall("createConnection")
}
DataFlow::SourceNode ref(DataFlow::TypeTracker t) {
t.start() and
result = this
or
exists(DataFlow::TypeTracker t2 |
result = ref(t2).track(t2, t)
)
}
DataFlow::SourceNode ref() {
result = ref(_)
}
DataFlow::SourceNode getACallbackNode(DataFlow::TypeBackTracker t) {
t.start() and
result = ref().getAMethodCall("getData").getArgument(0).getALocalSource()
or
exists(DataFlow::TypeBackTracker t2 |
result = getACallbackNode(t2).backtrack(t2, t)
)
}
DataFlow::FunctionNode getACallback() {
result = getACallbackNode(_).getAFunctionValue()
}
}
class DataValue extends DataFlow::SourceNode {
Connection connection;
DataValue() {
this = connection.getACallback().getParameter(0)
}
DataFlow::SourceNode ref(DataFlow::TypeTracker t) {
t.start() and
result = this
or
exists(DataFlow::TypeTracker t2 |
result = ref(t2).track(t2, t)
)
}
DataFlow::SourceNode ref() {
result = ref(_)
}
}
query DataFlow::SourceNode test_ApiObject() { result = any(ApiObject obj).ref() }
query DataFlow::SourceNode test_Connection() { result = any(Connection c).ref() }
query DataFlow::SourceNode test_DataCallback() { result = any(Connection c).getACallbackNode(_) }
query DataFlow::SourceNode test_DataValue() { result = any(DataValue v).ref() }

View File

@@ -0,0 +1,26 @@
apiObject
| tst.js:3:11:3:21 | new myapi() |
| tst.js:15:10:15:21 | api.chain1() |
| tst.js:15:10:15:30 | api.cha ... hain2() |
connection
| type tracker with call steps | tst.js:6:15:6:18 | conn |
| type tracker with call steps | tst.js:10:5:10:19 | this.connection |
| type tracker without call steps | tst.js:15:10:15:49 | api.cha ... ction() |
| type tracker without call steps | tst.js:18:7:18:21 | getConnection() |
| type tracker without call steps | tst.js:30:9:30:23 | getConnection() |
| type tracker without call steps | tst.js:39:7:39:21 | getConnection() |
| type tracker without call steps | tst.js:47:7:47:21 | getConnection() |
dataCallback
| tst.js:9:11:9:12 | cb |
| tst.js:20:1:22:1 | functio ... ata);\\n} |
| tst.js:29:26:29:27 | cb |
| tst.js:32:17:32:26 | data => {} |
| tst.js:37:10:37:19 | data => {} |
| tst.js:39:32:39:45 | getDataCurry() |
| tst.js:44:19:44:20 | cb |
| tst.js:47:32:47:60 | identit ... llback) |
dataValue
| tst.js:20:18:20:21 | data |
| tst.js:24:19:24:22 | data |
| tst.js:32:17:32:20 | data |
| tst.js:37:10:37:13 | data |

View File

@@ -0,0 +1,61 @@
import javascript
string chainableMethod() {
result = "chain1" or
result = "chain2"
}
DataFlow::SourceNode apiObject(DataFlow::TypeTracker t) {
t.start() and
result = DataFlow::moduleImport("@test/myapi").getAnInstantiation()
or
t.start() and
result = apiObject(_).getAMethodCall(chainableMethod())
or
exists(DataFlow::TypeTracker t2 |
result = apiObject(t2).track(t2, t)
)
}
query DataFlow::SourceNode apiObject() {
result = apiObject(_)
}
query DataFlow::SourceNode connection(DataFlow::TypeTracker t) {
t.start() and
result = apiObject().getAMethodCall("createConnection")
or
exists(DataFlow::TypeTracker t2 |
result = connection(t2).track(t2, t)
)
}
DataFlow::SourceNode connection() {
result = connection(_)
}
DataFlow::SourceNode dataCallback(DataFlow::TypeBackTracker t) {
t.start() and
result = connection().getAMethodCall("getData").getArgument(0).getALocalSource()
or
exists(DataFlow::TypeBackTracker t2 |
result = dataCallback(t2).backtrack(t2, t)
)
}
query DataFlow::SourceNode dataCallback() {
result = dataCallback(_)
}
DataFlow::SourceNode dataValue(DataFlow::TypeTracker t) {
t.start() and
result = dataCallback().getAFunctionValue().getParameter(0)
or
exists(DataFlow::TypeTracker t2 |
result = dataValue(t2).track(t2, t)
)
}
query DataFlow::SourceNode dataValue() {
result = dataValue(_)
}

View File

@@ -0,0 +1,51 @@
import myapi from "@test/myapi";
let api = new myapi();
class C {
constructor(conn) {
this.connection = conn;
}
getData(cb) {
this.connection.getData(cb);
}
}
function getConnection() {
return api.chain1().chain2().createConnection();
}
new C(getConnection()).getData(useData);
function useData(data) {
useData2(data);
}
function useData2(data) {
}
// Test tracking of callback into function
function getDataIndirect(cb) {
new C(getConnection()).getData(cb);
}
getDataIndirect(data => {});
getDataIndirect(); // suppress precision gains from single-call special case
// Test tracking of callback out of function
function getDataCurry() {
return data => {};
}
new C(getConnection()).getData(getDataCurry());
getDataCurry(); // suppress precision gains from single-call special case
// Test call/return matching of callback tracking
function identity(cb) {
return cb;
}
new C(getConnection()).getData(identity(realGetDataCallback));
identity(fakeGetDataCallback);
function realGetDataCallback(data) {} // not found due to missing summarization
function fakeGetDataCallback(notData) {} // should not be found

View File

@@ -19,5 +19,5 @@ import semmle.python.security.Exceptions
import semmle.python.web.HttpResponse
from TaintedPathSource src, TaintedPathSink sink
where src.flowsTo(sink)
where src.flowsTo(sink) and src.getSource() instanceof ErrorInfoSource
select sink.getSink(), src, sink, "$@ may be exposed to an external user", src.getSource(), "Error information"

View File

@@ -31,6 +31,10 @@ class ExceptionInfo extends StringKind {
}
/** A class representing sources of information about
* execution state exposed in tracebacks and the like.
*/
abstract class ErrorInfoSource extends TaintSource {}
/**
* This kind represents exceptions themselves.
@@ -56,7 +60,7 @@ class ExceptionKind extends TaintKind {
* A source of exception objects, either explicitly created, or captured by an
* `except` statement.
*/
class ExceptionSource extends TaintSource {
class ExceptionSource extends ErrorInfoSource {
ExceptionSource() {
exists(ClassObject cls |
@@ -91,7 +95,7 @@ class ExceptionInfoSequence extends SequenceKind {
* Represents calls to functions in the `traceback` module that return
* sequences of exception information.
*/
class CallToTracebackFunction extends TaintSource {
class CallToTracebackFunction extends ErrorInfoSource {
CallToTracebackFunction() {
exists(string name |
@@ -120,7 +124,7 @@ class CallToTracebackFunction extends TaintSource {
* Represents calls to functions in the `traceback` module that return a single
* string of information about an exception.
*/
class FormattedTracebackSource extends TaintSource {
class FormattedTracebackSource extends ErrorInfoSource {
FormattedTracebackSource() {
this = traceback_function("format_exc").getACall()

View File

@@ -1,4 +1,4 @@
from flask import Flask
from flask import Flask, request, make_response
app = Flask(__name__)
@@ -35,3 +35,8 @@ def server_bad_flow():
def format_error(msg):
return "[ERROR] " + msg
#Unrelated error
@app.route('/maybe_xss')
def maybe_xss():
return make_response(request.args.get('name', ''))