mirror of
https://github.com/github/codeql.git
synced 2026-05-03 04:39:29 +02:00
Merge branch 'main' into django-3.2
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
while(flagsLoop)
|
||||
{
|
||||
...
|
||||
if(flagsIf) break;
|
||||
...
|
||||
}while(flagsLoop); // BAD: when exiting through `break`, it is possible to get into an eternal loop.
|
||||
...
|
||||
while(flagsLoop)
|
||||
{
|
||||
...
|
||||
if(flagsIf) break;
|
||||
...
|
||||
} // GOOD: correct cycle
|
||||
...
|
||||
if(intA+intB) return 1; // BAD: possibly no comparison
|
||||
...
|
||||
if(intA+intB>intC) return 1; // GOOD: correct comparison
|
||||
@@ -0,0 +1,28 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>In some situations, after code refactoring, parts of the old constructs may remain. They are correctly accepted by the compiler, but can critically affect program execution. For example, if you switch from `do {...} while ();` to `while () {...}` forgetting to remove the old construct completely, you get `while(){...}while();` which may be vulnerable. These code snippets look suspicious and require the developer's attention.</p>
|
||||
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>We recommend that you use more explicit code transformations.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>The following example demonstrates the erroneous and corrected sections of the code.</p>
|
||||
<sample src="InsufficientControlFlowManagementAfterRefactoringTheCode.c" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
CWE Common Weakness Enumeration:
|
||||
<a href="https://cwe.mitre.org/data/definitions/691.html"> CWE-691: Insufficient Control Flow Management</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* @name Errors After Refactoring
|
||||
* @description --In some situations, after code refactoring, parts of the old constructs may remain.
|
||||
* --They are correctly accepted by the compiler, but can critically affect program execution.
|
||||
* --For example, if you switch from `do {...} while ();` to `while () {...}` with errors, you run the risk of running out of resources.
|
||||
* --These code snippets look suspicious and require the developer's attention.
|
||||
* @kind problem
|
||||
* @id cpp/errors-after-refactoring
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-691
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.valuenumbering.HashCons
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
/**
|
||||
* Using `while` directly after the body of another` while`.
|
||||
*/
|
||||
class UsingWhileAfterWhile extends WhileStmt {
|
||||
/**
|
||||
* Using a loop call after another loop has finished running can result in an eternal loop.
|
||||
* For example, perhaps as a result of refactoring, the `do ... while ()` loop was incorrectly corrected.
|
||||
* Even in the case of deliberate use of such an expression, it is better to correct it.
|
||||
*/
|
||||
UsingWhileAfterWhile() {
|
||||
exists(WhileStmt wh1 |
|
||||
wh1.getStmt().getAChild*().(BreakStmt).(ControlFlowNode).getASuccessor().getASuccessor() =
|
||||
this and
|
||||
hashCons(wh1.getCondition()) = hashCons(this.getCondition()) and
|
||||
this.getStmt() instanceof EmptyStmt
|
||||
)
|
||||
or
|
||||
exists(ForStmt fr1 |
|
||||
fr1.getStmt().getAChild*().(BreakStmt).(ControlFlowNode).getASuccessor().getASuccessor() =
|
||||
this and
|
||||
hashCons(fr1.getCondition()) = hashCons(this.getCondition()) and
|
||||
this.getStmt() instanceof EmptyStmt
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Using arithmetic in a condition.
|
||||
*/
|
||||
class UsingArithmeticInComparison extends BinaryArithmeticOperation {
|
||||
/**
|
||||
* Using arithmetic operations in a comparison operation can be dangerous.
|
||||
* For example, part of the comparison may have been lost as a result of refactoring.
|
||||
* Even if you deliberately use such an expression, it is better to add an explicit comparison.
|
||||
*/
|
||||
UsingArithmeticInComparison() {
|
||||
this.getParent*() instanceof IfStmt and
|
||||
not this.getAChild*().isConstant() and
|
||||
not this.getParent*() instanceof Call and
|
||||
not this.getParent*() instanceof AssignExpr and
|
||||
not this.getParent*() instanceof ArrayExpr and
|
||||
not this.getParent*() instanceof RemExpr and
|
||||
not this.getParent*() instanceof AssignBitwiseOperation and
|
||||
not this.getParent*() instanceof AssignArithmeticOperation and
|
||||
not this.getParent*() instanceof EqualityOperation and
|
||||
not this.getParent*() instanceof RelationalOperation
|
||||
}
|
||||
|
||||
/** Holds when the expression is inside the loop body. */
|
||||
predicate insideTheLoop() { exists(Loop lp | lp.getStmt().getAChild*() = this.getParent*()) }
|
||||
|
||||
/** Holds when the expression is used in binary operations. */
|
||||
predicate workingWithValue() {
|
||||
this.getParent*() instanceof BinaryBitwiseOperation or
|
||||
this.getParent*() instanceof NotExpr
|
||||
}
|
||||
|
||||
/** Holds when the expression contains a pointer. */
|
||||
predicate workingWithPointer() {
|
||||
this.getAChild*().getFullyConverted().getType() instanceof DerivedType
|
||||
}
|
||||
|
||||
/** Holds when a null comparison expression exists. */
|
||||
predicate compareWithZero() {
|
||||
exists(Expr exp |
|
||||
exp instanceof ComparisonOperation and
|
||||
(
|
||||
globalValueNumber(exp.getAChild*()) = globalValueNumber(this) or
|
||||
hashCons(exp.getAChild*()) = hashCons(this)
|
||||
) and
|
||||
(
|
||||
exp.(ComparisonOperation).getLeftOperand().getValue() = "0" or
|
||||
exp.(ComparisonOperation).getRightOperand().getValue() = "0"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds when a comparison expression exists. */
|
||||
predicate compareWithOutZero() {
|
||||
exists(Expr exp |
|
||||
exp instanceof ComparisonOperation and
|
||||
(
|
||||
globalValueNumber(exp.getAChild*()) = globalValueNumber(this) or
|
||||
hashCons(exp.getAChild*()) = hashCons(this)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from Expr exp
|
||||
where
|
||||
exp instanceof UsingArithmeticInComparison and
|
||||
not exp.(UsingArithmeticInComparison).workingWithValue() and
|
||||
not exp.(UsingArithmeticInComparison).workingWithPointer() and
|
||||
not exp.(UsingArithmeticInComparison).insideTheLoop() and
|
||||
not exp.(UsingArithmeticInComparison).compareWithZero() and
|
||||
exp.(UsingArithmeticInComparison).compareWithOutZero()
|
||||
or
|
||||
exists(WhileStmt wst | wst instanceof UsingWhileAfterWhile and exp = wst.getCondition())
|
||||
select exp, "this expression needs your attention"
|
||||
@@ -0,0 +1,4 @@
|
||||
| test.c:15:6:15:16 | ... + ... | this expression needs your attention |
|
||||
| test.c:17:17:17:27 | ... + ... | this expression needs your attention |
|
||||
| test.c:22:10:22:15 | ... > ... | this expression needs your attention |
|
||||
| test.c:26:10:26:15 | ... > ... | this expression needs your attention |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-691/InsufficientControlFlowManagementAfterRefactoringTheCode.ql
|
||||
@@ -12,14 +12,18 @@ void workFunction_0(char *s) {
|
||||
void workFunction_1(char *s) {
|
||||
int intA,intB;
|
||||
|
||||
if(intA + intB) return; // BAD [NOT DETECTED]
|
||||
if(intA + intB) return; // BAD
|
||||
if(intA + intB>4) return; // GOOD
|
||||
if(intA>0 && (intA + intB)) return; // BAD [NOT DETECTED]
|
||||
if(intA>0 && (intA + intB)) return; // BAD
|
||||
while(intA>0)
|
||||
{
|
||||
if(intB - intA<10) break;
|
||||
intA--;
|
||||
}while(intA>0); // BAD [NOT DETECTED]
|
||||
}while(intA>0); // BAD
|
||||
for(intA=100; intA>0; intA--)
|
||||
{
|
||||
if(intB - intA<10) break;
|
||||
}while(intA>0); // BAD
|
||||
while(intA>0)
|
||||
{
|
||||
if(intB - intA<10) break;
|
||||
|
||||
11
csharp/ql/src/Metrics/Summaries/LinesOfCode.ql
Normal file
11
csharp/ql/src/Metrics/Summaries/LinesOfCode.ql
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* @id cs/summary/lines-of-code
|
||||
* @name Total lines of code in the database
|
||||
* @description The total number of lines of code across all files. This is a useful metric of the size of a database. For all files that were seen during the build, this query counts the lines of code, excluding whitespace or comments.
|
||||
* @kind metric
|
||||
* @tags summary
|
||||
*/
|
||||
|
||||
import csharp
|
||||
|
||||
select sum(File f | f.fromSource() | f.getNumberOfLinesOfCode())
|
||||
@@ -316,6 +316,15 @@ private module SsaDefReaches {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the reference to `def` at index `i` in basic block `bb` is the
|
||||
* last reference to `v` inside `bb`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
predicate lastSsaRef(Definition def, SourceVariable v, BasicBlock bb, int i) {
|
||||
ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v)
|
||||
}
|
||||
|
||||
predicate defOccursInBlock(Definition def, BasicBlock bb, SourceVariable v) {
|
||||
exists(ssaDefRank(def, v, bb, _, _))
|
||||
}
|
||||
@@ -351,8 +360,7 @@ private module SsaDefReaches {
|
||||
*/
|
||||
predicate defAdjacentRead(Definition def, BasicBlock bb1, BasicBlock bb2, int i2) {
|
||||
varBlockReaches(def, bb1, bb2) and
|
||||
ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1 and
|
||||
variableRead(bb2, i2, _, _)
|
||||
ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,15 +442,22 @@ predicate adjacentDefRead(Definition def, BasicBlock bb1, int i1, BasicBlock bb2
|
||||
bb2 = bb1
|
||||
)
|
||||
or
|
||||
exists(SourceVariable v | ssaDefRank(def, v, bb1, i1, _) = maxSsaRefRank(bb1, v)) and
|
||||
lastSsaRef(def, _, bb1, i1) and
|
||||
defAdjacentRead(def, bb1, bb2, i2)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate adjacentDefRead(
|
||||
Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2, SourceVariable v
|
||||
) {
|
||||
adjacentDefRead(def, bb1, i1, bb2, i2) and
|
||||
v = def.getSourceVariable()
|
||||
}
|
||||
|
||||
private predicate adjacentDefReachesRead(
|
||||
Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2
|
||||
) {
|
||||
adjacentDefRead(def, bb1, i1, bb2, i2) and
|
||||
exists(SourceVariable v | v = def.getSourceVariable() |
|
||||
exists(SourceVariable v | adjacentDefRead(def, bb1, i1, bb2, i2, v) |
|
||||
ssaRef(bb1, i1, v, SsaDef())
|
||||
or
|
||||
variableRead(bb1, i1, v, true)
|
||||
@@ -475,17 +490,19 @@ predicate adjacentDefNoUncertainReads(Definition def, BasicBlock bb1, int i1, Ba
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate lastRefRedef(Definition def, BasicBlock bb, int i, Definition next) {
|
||||
exists(int rnk, SourceVariable v, int j | rnk = ssaDefRank(def, v, bb, i, _) |
|
||||
exists(SourceVariable v |
|
||||
// Next reference to `v` inside `bb` is a write
|
||||
next.definesAt(v, bb, j) and
|
||||
rnk + 1 = ssaRefRank(bb, j, v, SsaDef())
|
||||
exists(int rnk, int j |
|
||||
rnk = ssaDefRank(def, v, bb, i, _) and
|
||||
next.definesAt(v, bb, j) and
|
||||
rnk + 1 = ssaRefRank(bb, j, v, SsaDef())
|
||||
)
|
||||
or
|
||||
// Can reach a write using one or more steps
|
||||
rnk = maxSsaRefRank(bb, v) and
|
||||
lastSsaRef(def, v, bb, i) and
|
||||
exists(BasicBlock bb2 |
|
||||
varBlockReaches(def, bb, bb2) and
|
||||
next.definesAt(v, bb2, j) and
|
||||
1 = ssaRefRank(bb2, j, v, SsaDef())
|
||||
1 = ssaDefRank(next, v, bb2, _, SsaDef())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -539,7 +556,8 @@ pragma[nomagic]
|
||||
predicate lastRef(Definition def, BasicBlock bb, int i) {
|
||||
lastRefRedef(def, bb, i, _)
|
||||
or
|
||||
exists(SourceVariable v | ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v) |
|
||||
lastSsaRef(def, _, bb, i) and
|
||||
(
|
||||
// Can reach exit directly
|
||||
bb instanceof ExitBasicBlock
|
||||
or
|
||||
|
||||
@@ -316,6 +316,15 @@ private module SsaDefReaches {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the reference to `def` at index `i` in basic block `bb` is the
|
||||
* last reference to `v` inside `bb`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
predicate lastSsaRef(Definition def, SourceVariable v, BasicBlock bb, int i) {
|
||||
ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v)
|
||||
}
|
||||
|
||||
predicate defOccursInBlock(Definition def, BasicBlock bb, SourceVariable v) {
|
||||
exists(ssaDefRank(def, v, bb, _, _))
|
||||
}
|
||||
@@ -351,8 +360,7 @@ private module SsaDefReaches {
|
||||
*/
|
||||
predicate defAdjacentRead(Definition def, BasicBlock bb1, BasicBlock bb2, int i2) {
|
||||
varBlockReaches(def, bb1, bb2) and
|
||||
ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1 and
|
||||
variableRead(bb2, i2, _, _)
|
||||
ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,15 +442,22 @@ predicate adjacentDefRead(Definition def, BasicBlock bb1, int i1, BasicBlock bb2
|
||||
bb2 = bb1
|
||||
)
|
||||
or
|
||||
exists(SourceVariable v | ssaDefRank(def, v, bb1, i1, _) = maxSsaRefRank(bb1, v)) and
|
||||
lastSsaRef(def, _, bb1, i1) and
|
||||
defAdjacentRead(def, bb1, bb2, i2)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate adjacentDefRead(
|
||||
Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2, SourceVariable v
|
||||
) {
|
||||
adjacentDefRead(def, bb1, i1, bb2, i2) and
|
||||
v = def.getSourceVariable()
|
||||
}
|
||||
|
||||
private predicate adjacentDefReachesRead(
|
||||
Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2
|
||||
) {
|
||||
adjacentDefRead(def, bb1, i1, bb2, i2) and
|
||||
exists(SourceVariable v | v = def.getSourceVariable() |
|
||||
exists(SourceVariable v | adjacentDefRead(def, bb1, i1, bb2, i2, v) |
|
||||
ssaRef(bb1, i1, v, SsaDef())
|
||||
or
|
||||
variableRead(bb1, i1, v, true)
|
||||
@@ -475,17 +490,19 @@ predicate adjacentDefNoUncertainReads(Definition def, BasicBlock bb1, int i1, Ba
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate lastRefRedef(Definition def, BasicBlock bb, int i, Definition next) {
|
||||
exists(int rnk, SourceVariable v, int j | rnk = ssaDefRank(def, v, bb, i, _) |
|
||||
exists(SourceVariable v |
|
||||
// Next reference to `v` inside `bb` is a write
|
||||
next.definesAt(v, bb, j) and
|
||||
rnk + 1 = ssaRefRank(bb, j, v, SsaDef())
|
||||
exists(int rnk, int j |
|
||||
rnk = ssaDefRank(def, v, bb, i, _) and
|
||||
next.definesAt(v, bb, j) and
|
||||
rnk + 1 = ssaRefRank(bb, j, v, SsaDef())
|
||||
)
|
||||
or
|
||||
// Can reach a write using one or more steps
|
||||
rnk = maxSsaRefRank(bb, v) and
|
||||
lastSsaRef(def, v, bb, i) and
|
||||
exists(BasicBlock bb2 |
|
||||
varBlockReaches(def, bb, bb2) and
|
||||
next.definesAt(v, bb2, j) and
|
||||
1 = ssaRefRank(bb2, j, v, SsaDef())
|
||||
1 = ssaDefRank(next, v, bb2, _, SsaDef())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -539,7 +556,8 @@ pragma[nomagic]
|
||||
predicate lastRef(Definition def, BasicBlock bb, int i) {
|
||||
lastRefRedef(def, bb, i, _)
|
||||
or
|
||||
exists(SourceVariable v | ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v) |
|
||||
lastSsaRef(def, _, bb, i) and
|
||||
(
|
||||
// Can reach exit directly
|
||||
bb instanceof ExitBasicBlock
|
||||
or
|
||||
|
||||
@@ -316,6 +316,15 @@ private module SsaDefReaches {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the reference to `def` at index `i` in basic block `bb` is the
|
||||
* last reference to `v` inside `bb`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
predicate lastSsaRef(Definition def, SourceVariable v, BasicBlock bb, int i) {
|
||||
ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v)
|
||||
}
|
||||
|
||||
predicate defOccursInBlock(Definition def, BasicBlock bb, SourceVariable v) {
|
||||
exists(ssaDefRank(def, v, bb, _, _))
|
||||
}
|
||||
@@ -351,8 +360,7 @@ private module SsaDefReaches {
|
||||
*/
|
||||
predicate defAdjacentRead(Definition def, BasicBlock bb1, BasicBlock bb2, int i2) {
|
||||
varBlockReaches(def, bb1, bb2) and
|
||||
ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1 and
|
||||
variableRead(bb2, i2, _, _)
|
||||
ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,15 +442,22 @@ predicate adjacentDefRead(Definition def, BasicBlock bb1, int i1, BasicBlock bb2
|
||||
bb2 = bb1
|
||||
)
|
||||
or
|
||||
exists(SourceVariable v | ssaDefRank(def, v, bb1, i1, _) = maxSsaRefRank(bb1, v)) and
|
||||
lastSsaRef(def, _, bb1, i1) and
|
||||
defAdjacentRead(def, bb1, bb2, i2)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate adjacentDefRead(
|
||||
Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2, SourceVariable v
|
||||
) {
|
||||
adjacentDefRead(def, bb1, i1, bb2, i2) and
|
||||
v = def.getSourceVariable()
|
||||
}
|
||||
|
||||
private predicate adjacentDefReachesRead(
|
||||
Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2
|
||||
) {
|
||||
adjacentDefRead(def, bb1, i1, bb2, i2) and
|
||||
exists(SourceVariable v | v = def.getSourceVariable() |
|
||||
exists(SourceVariable v | adjacentDefRead(def, bb1, i1, bb2, i2, v) |
|
||||
ssaRef(bb1, i1, v, SsaDef())
|
||||
or
|
||||
variableRead(bb1, i1, v, true)
|
||||
@@ -475,17 +490,19 @@ predicate adjacentDefNoUncertainReads(Definition def, BasicBlock bb1, int i1, Ba
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate lastRefRedef(Definition def, BasicBlock bb, int i, Definition next) {
|
||||
exists(int rnk, SourceVariable v, int j | rnk = ssaDefRank(def, v, bb, i, _) |
|
||||
exists(SourceVariable v |
|
||||
// Next reference to `v` inside `bb` is a write
|
||||
next.definesAt(v, bb, j) and
|
||||
rnk + 1 = ssaRefRank(bb, j, v, SsaDef())
|
||||
exists(int rnk, int j |
|
||||
rnk = ssaDefRank(def, v, bb, i, _) and
|
||||
next.definesAt(v, bb, j) and
|
||||
rnk + 1 = ssaRefRank(bb, j, v, SsaDef())
|
||||
)
|
||||
or
|
||||
// Can reach a write using one or more steps
|
||||
rnk = maxSsaRefRank(bb, v) and
|
||||
lastSsaRef(def, v, bb, i) and
|
||||
exists(BasicBlock bb2 |
|
||||
varBlockReaches(def, bb, bb2) and
|
||||
next.definesAt(v, bb2, j) and
|
||||
1 = ssaRefRank(bb2, j, v, SsaDef())
|
||||
1 = ssaDefRank(next, v, bb2, _, SsaDef())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -539,7 +556,8 @@ pragma[nomagic]
|
||||
predicate lastRef(Definition def, BasicBlock bb, int i) {
|
||||
lastRefRedef(def, bb, i, _)
|
||||
or
|
||||
exists(SourceVariable v | ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v) |
|
||||
lastSsaRef(def, _, bb, i) and
|
||||
(
|
||||
// Can reach exit directly
|
||||
bb instanceof ExitBasicBlock
|
||||
or
|
||||
|
||||
@@ -316,6 +316,15 @@ private module SsaDefReaches {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the reference to `def` at index `i` in basic block `bb` is the
|
||||
* last reference to `v` inside `bb`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
predicate lastSsaRef(Definition def, SourceVariable v, BasicBlock bb, int i) {
|
||||
ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v)
|
||||
}
|
||||
|
||||
predicate defOccursInBlock(Definition def, BasicBlock bb, SourceVariable v) {
|
||||
exists(ssaDefRank(def, v, bb, _, _))
|
||||
}
|
||||
@@ -351,8 +360,7 @@ private module SsaDefReaches {
|
||||
*/
|
||||
predicate defAdjacentRead(Definition def, BasicBlock bb1, BasicBlock bb2, int i2) {
|
||||
varBlockReaches(def, bb1, bb2) and
|
||||
ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1 and
|
||||
variableRead(bb2, i2, _, _)
|
||||
ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,15 +442,22 @@ predicate adjacentDefRead(Definition def, BasicBlock bb1, int i1, BasicBlock bb2
|
||||
bb2 = bb1
|
||||
)
|
||||
or
|
||||
exists(SourceVariable v | ssaDefRank(def, v, bb1, i1, _) = maxSsaRefRank(bb1, v)) and
|
||||
lastSsaRef(def, _, bb1, i1) and
|
||||
defAdjacentRead(def, bb1, bb2, i2)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate adjacentDefRead(
|
||||
Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2, SourceVariable v
|
||||
) {
|
||||
adjacentDefRead(def, bb1, i1, bb2, i2) and
|
||||
v = def.getSourceVariable()
|
||||
}
|
||||
|
||||
private predicate adjacentDefReachesRead(
|
||||
Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2
|
||||
) {
|
||||
adjacentDefRead(def, bb1, i1, bb2, i2) and
|
||||
exists(SourceVariable v | v = def.getSourceVariable() |
|
||||
exists(SourceVariable v | adjacentDefRead(def, bb1, i1, bb2, i2, v) |
|
||||
ssaRef(bb1, i1, v, SsaDef())
|
||||
or
|
||||
variableRead(bb1, i1, v, true)
|
||||
@@ -475,17 +490,19 @@ predicate adjacentDefNoUncertainReads(Definition def, BasicBlock bb1, int i1, Ba
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate lastRefRedef(Definition def, BasicBlock bb, int i, Definition next) {
|
||||
exists(int rnk, SourceVariable v, int j | rnk = ssaDefRank(def, v, bb, i, _) |
|
||||
exists(SourceVariable v |
|
||||
// Next reference to `v` inside `bb` is a write
|
||||
next.definesAt(v, bb, j) and
|
||||
rnk + 1 = ssaRefRank(bb, j, v, SsaDef())
|
||||
exists(int rnk, int j |
|
||||
rnk = ssaDefRank(def, v, bb, i, _) and
|
||||
next.definesAt(v, bb, j) and
|
||||
rnk + 1 = ssaRefRank(bb, j, v, SsaDef())
|
||||
)
|
||||
or
|
||||
// Can reach a write using one or more steps
|
||||
rnk = maxSsaRefRank(bb, v) and
|
||||
lastSsaRef(def, v, bb, i) and
|
||||
exists(BasicBlock bb2 |
|
||||
varBlockReaches(def, bb, bb2) and
|
||||
next.definesAt(v, bb2, j) and
|
||||
1 = ssaRefRank(bb2, j, v, SsaDef())
|
||||
1 = ssaDefRank(next, v, bb2, _, SsaDef())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -539,7 +556,8 @@ pragma[nomagic]
|
||||
predicate lastRef(Definition def, BasicBlock bb, int i) {
|
||||
lastRefRedef(def, bb, i, _)
|
||||
or
|
||||
exists(SourceVariable v | ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v) |
|
||||
lastSsaRef(def, _, bb, i) and
|
||||
(
|
||||
// Can reach exit directly
|
||||
bb instanceof ExitBasicBlock
|
||||
or
|
||||
|
||||
@@ -111,13 +111,24 @@ module HardcodedCredentials {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a regular expression for matching names of locations (variables, parameters, keys) that
|
||||
* indicate the value being held is a credential.
|
||||
* An assignable whose name indicates that the value being held is a credential.
|
||||
*/
|
||||
private string getACredentialRegex() {
|
||||
result = "(?i).*pass(wd|word|code|phrase)(?!.*question).*" or
|
||||
result = "(?i).*(puid|username|userid).*" or
|
||||
result = "(?i).*(cert)(?!.*(format|name)).*"
|
||||
private class CredentialVar extends Assignable {
|
||||
pragma[noinline]
|
||||
CredentialVar() {
|
||||
exists(string name | name = this.getName() |
|
||||
name.regexpMatch("(?i).*pass(wd|word|code|phrase)(?!.*question).*")
|
||||
or
|
||||
name.regexpMatch("(?i).*(puid|username|userid).*")
|
||||
or
|
||||
name.regexpMatch("(?i).*(cert)(?!.*(format|name)).*")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class CredentialVariableAccess extends VariableAccess {
|
||||
pragma[noinline]
|
||||
CredentialVariableAccess() { this.getTarget() instanceof CredentialVar }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,11 +139,11 @@ module HardcodedCredentials {
|
||||
) {
|
||||
// An argument to a library call that looks like a credential
|
||||
// "...flows to the [Username] parameter in [call to method CreateUser]"
|
||||
exists(Call call |
|
||||
exists(Call call, CredentialVar param |
|
||||
supplementaryElement = call and
|
||||
description = "the $@ parameter in $@" and
|
||||
sink = call.getArgumentForName(sinkName) and
|
||||
sinkName.regexpMatch(getACredentialRegex()) and
|
||||
sink = call.getArgumentForParameter(param) and
|
||||
sinkName = param.getName() and
|
||||
call.getTarget().fromLibrary()
|
||||
)
|
||||
or
|
||||
@@ -144,22 +155,20 @@ module HardcodedCredentials {
|
||||
description = "the $@ in $@" and
|
||||
sink = call.getArgument(0) and
|
||||
sinkName = "setter call argument" and
|
||||
p.getName().regexpMatch(getACredentialRegex()) and
|
||||
p instanceof CredentialVar and
|
||||
p.fromLibrary()
|
||||
)
|
||||
or
|
||||
// Sink compared to password variable
|
||||
// "...flows to [] which is compared against [access of UserName]"
|
||||
exists(ComparisonTest ct, VariableAccess credentialAccess, string varName |
|
||||
exists(ComparisonTest ct, CredentialVariableAccess credentialAccess |
|
||||
sinkName = sink.toString() and
|
||||
supplementaryElement = credentialAccess and
|
||||
description = "$@ which is compared against $@" and
|
||||
ct.getAnArgument() = credentialAccess and
|
||||
ct.getAnArgument() = sink and
|
||||
ct.getComparisonKind().isEquality() and
|
||||
not sink = credentialAccess and
|
||||
varName = credentialAccess.getTarget().getName() and
|
||||
varName.regexpMatch(getACredentialRegex())
|
||||
not sink = credentialAccess
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
| 4 |
|
||||
@@ -0,0 +1 @@
|
||||
Metrics/Summaries/LinesOfCode.ql
|
||||
14
csharp/ql/test/query-tests/Metrics/Summaries/file1.cs
Normal file
14
csharp/ql/test/query-tests/Metrics/Summaries/file1.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
class C1
|
||||
{
|
||||
/*
|
||||
int M()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
// int M() => 0;
|
||||
|
||||
int M() => 0; // Comment
|
||||
}
|
||||
@@ -18,7 +18,9 @@ abstract class Modifiable extends Element {
|
||||
* Holds if this element has modifier `m`.
|
||||
*
|
||||
* For most purposes, the more specialized predicates `isAbstract`, `isPublic`, etc.
|
||||
* should be used, which also take implicit modifiers into account.
|
||||
* should be used.
|
||||
*
|
||||
* Both this method and those specialized predicates take implicit modifiers into account.
|
||||
* For instance, non-default instance methods in interfaces are implicitly
|
||||
* abstract, so `isAbstract()` will hold for them even if `hasModifier("abstract")`
|
||||
* does not.
|
||||
|
||||
@@ -77,6 +77,7 @@ private module Frameworks {
|
||||
private import semmle.code.java.frameworks.ApacheHttp
|
||||
private import semmle.code.java.frameworks.apache.Lang
|
||||
private import semmle.code.java.frameworks.guava.Guava
|
||||
private import semmle.code.java.security.LdapInjection
|
||||
}
|
||||
|
||||
private predicate sourceModelCsv(string row) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import semmle.code.java.frameworks.Jndi
|
||||
import semmle.code.java.frameworks.UnboundId
|
||||
import semmle.code.java.frameworks.SpringLdap
|
||||
import semmle.code.java.frameworks.ApacheLdap
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
/** A data flow sink for unvalidated user input that is used to construct LDAP queries. */
|
||||
abstract class LdapInjectionSink extends DataFlow::Node { }
|
||||
@@ -28,70 +29,56 @@ class LdapInjectionAdditionalTaintStep extends Unit {
|
||||
|
||||
/** Default sink for LDAP injection vulnerabilities. */
|
||||
private class DefaultLdapInjectionSink extends LdapInjectionSink {
|
||||
DefaultLdapInjectionSink() {
|
||||
exists(MethodAccess ma, Method m, int index |
|
||||
ma.getMethod() = m and
|
||||
ma.getArgument(index) = this.asExpr() and
|
||||
ldapInjectionSinkMethod(m, index)
|
||||
)
|
||||
DefaultLdapInjectionSink() { sinkNode(this, "ldap") }
|
||||
}
|
||||
|
||||
private class DefaultLdapInjectionSinkModel extends SinkModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
// jndi
|
||||
"javax.naming.directory;DirContext;true;search;;;Argument[0..1];ldap",
|
||||
// apache
|
||||
"org.apache.directory.ldap.client.api;LdapConnection;true;search;;;Argument[0..2];ldap",
|
||||
// UnboundID: search
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;search;(ReadOnlySearchRequest);;Argument[0];ldap",
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;search;(SearchRequest);;Argument[0];ldap",
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;search;(SearchResultListener,String,SearchScope,DereferencePolicy,int,int,boolean,Filter,String[]);;Argument[0..7];ldap",
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;search;(SearchResultListener,String,SearchScope,DereferencePolicy,int,int,boolean,String,String[]);;Argument[0..7];ldap",
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;search;(SearchResultListener,String,SearchScope,Filter,String[]);;Argument[0..3];ldap",
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;search;(SearchResultListener,String,SearchScope,String,String[]);;Argument[0..3];ldap",
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;search;(String,SearchScope,DereferencePolicy,int,int,boolean,Filter,String[]);;Argument[0..6];ldap",
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;search;(String,SearchScope,DereferencePolicy,int,int,boolean,String,String[]);;Argument[0..6];ldap",
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;search;(String,SearchScope,Filter,String[]);;Argument[0..2];ldap",
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;search;(String,SearchScope,String,String[]);;Argument[0..2];ldap",
|
||||
// UnboundID: searchForEntry
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;searchForEntry;(ReadOnlySearchRequest);;Argument[0];ldap",
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;searchForEntry;(SearchRequest);;Argument[0];ldap",
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;searchForEntry;(String,SearchScope,DereferencePolicy,int,boolean,Filter,String[]);;Argument[0..5];ldap",
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;searchForEntry;(String,SearchScope,DereferencePolicy,int,boolean,String,String[]);;Argument[0..5];ldap",
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;searchForEntry;(String,SearchScope,Filter,String[]);;Argument[0..2];ldap",
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;searchForEntry;(String,SearchScope,String,String[]);;Argument[0..2];ldap",
|
||||
// UnboundID: asyncSearch
|
||||
"com.unboundid.ldap.sdk;LDAPConnection;false;asyncSearch;;;Argument[0];ldap",
|
||||
// Spring
|
||||
"org.springframework.ldap.core;LdapTemplate;false;find;;;Argument[0..1];ldap",
|
||||
"org.springframework.ldap.core;LdapTemplate;false;findOne;;;Argument[0..1];ldap",
|
||||
"org.springframework.ldap.core;LdapTemplate;false;search;;;Argument[0..1];ldap",
|
||||
"org.springframework.ldap.core;LdapTemplate;false;searchForContext;;;Argument[0..1];ldap",
|
||||
"org.springframework.ldap.core;LdapTemplate;false;searchForObject;;;Argument[0..1];ldap",
|
||||
"org.springframework.ldap.core;LdapTemplate;false;authenticate;(LdapQuery,String);;Argument[0];ldap",
|
||||
"org.springframework.ldap.core;LdapTemplate;false;authenticate;(Name,String,String);;Argument[0..1];ldap",
|
||||
"org.springframework.ldap.core;LdapTemplate;false;authenticate;(Name,String,String,AuthenticatedLdapEntryContextCallback);;Argument[0..1];ldap",
|
||||
"org.springframework.ldap.core;LdapTemplate;false;authenticate;(Name,String,String,AuthenticatedLdapEntryContextCallback,AuthenticationErrorCallback);;Argument[0..1];ldap",
|
||||
"org.springframework.ldap.core;LdapTemplate;false;authenticate;(Name,String,String,AuthenticationErrorCallback);;Argument[0..1];ldap",
|
||||
"org.springframework.ldap.core;LdapTemplate;false;authenticate;(String,String,String);;Argument[0..1];ldap",
|
||||
"org.springframework.ldap.core;LdapTemplate;false;authenticate;(String,String,String,AuthenticatedLdapEntryContextCallback);;Argument[0..1];ldap",
|
||||
"org.springframework.ldap.core;LdapTemplate;false;authenticate;(String,String,String,AuthenticatedLdapEntryContextCallback,AuthenticationErrorCallback);;Argument[0..1];ldap",
|
||||
"org.springframework.ldap.core;LdapTemplate;false;authenticate;(String,String,String,AuthenticationErrorCallback);;Argument[0..1];ldap"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if the method parameter at `index` is susceptible to an LDAP injection attack. */
|
||||
private predicate ldapInjectionSinkMethod(Method m, int index) {
|
||||
jndiLdapInjectionSinkMethod(m, index) or
|
||||
unboundIdLdapInjectionSinkMethod(m, index) or
|
||||
springLdapInjectionSinkMethod(m, index) or
|
||||
apacheLdapInjectionSinkMethod(m, index)
|
||||
}
|
||||
|
||||
/** Holds if the JNDI method parameter at `index` is susceptible to an LDAP injection attack. */
|
||||
private predicate jndiLdapInjectionSinkMethod(Method m, int index) {
|
||||
m.getDeclaringType().getAnAncestor() instanceof TypeDirContext and
|
||||
m.hasName("search") and
|
||||
index in [0 .. 1]
|
||||
}
|
||||
|
||||
/** Holds if the UnboundID method parameter at `index` is susceptible to an LDAP injection attack. */
|
||||
private predicate unboundIdLdapInjectionSinkMethod(Method m, int index) {
|
||||
exists(Parameter param | m.getParameter(index) = param and not param.isVarargs() |
|
||||
m instanceof MethodUnboundIdLDAPConnectionSearch or
|
||||
m instanceof MethodUnboundIdLDAPConnectionAsyncSearch or
|
||||
m instanceof MethodUnboundIdLDAPConnectionSearchForEntry
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the Spring method parameter at `index` is susceptible to an LDAP injection attack. */
|
||||
private predicate springLdapInjectionSinkMethod(Method m, int index) {
|
||||
// LdapTemplate.authenticate, LdapTemplate.find* or LdapTemplate.search* method
|
||||
(
|
||||
m instanceof MethodSpringLdapTemplateAuthenticate or
|
||||
m instanceof MethodSpringLdapTemplateFind or
|
||||
m instanceof MethodSpringLdapTemplateFindOne or
|
||||
m instanceof MethodSpringLdapTemplateSearch or
|
||||
m instanceof MethodSpringLdapTemplateSearchForContext or
|
||||
m instanceof MethodSpringLdapTemplateSearchForObject
|
||||
) and
|
||||
(
|
||||
// Parameter index is 1 (DN or query) or 2 (filter) if method is not authenticate
|
||||
index in [0 .. 1] and
|
||||
not m instanceof MethodSpringLdapTemplateAuthenticate
|
||||
or
|
||||
// But it's not the last parameter in case of authenticate method (last param is password)
|
||||
index in [0 .. 1] and
|
||||
index < m.getNumberOfParameters() - 1 and
|
||||
m instanceof MethodSpringLdapTemplateAuthenticate
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the Apache LDAP API method parameter at `index` is susceptible to an LDAP injection attack. */
|
||||
private predicate apacheLdapInjectionSinkMethod(Method m, int index) {
|
||||
exists(Parameter param | m.getParameter(index) = param and not param.isVarargs() |
|
||||
m.getDeclaringType().getAnAncestor() instanceof TypeApacheLdapConnection and
|
||||
m.hasName("search")
|
||||
)
|
||||
}
|
||||
|
||||
/** A sanitizer that clears the taint on (boxed) primitive types. */
|
||||
private class DefaultLdapSanitizer extends LdapInjectionSanitizer {
|
||||
DefaultLdapSanitizer() {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
lgtm,codescanning
|
||||
* The predicates `StepSummary::step` and `TypeTracker::step` in `TypeTracker.qll` have been changed
|
||||
to use the more restrictive type `LocalSourceNode` for their second argument. For cases where
|
||||
stepping between non-`LocalSourceNode`s is required, the `StepSummary::smallstep` predicate may be
|
||||
used instead.
|
||||
* The methods `Node::track` and `Node::backtrack` have been moved to the class `LocalSourceNode`. If
|
||||
the old behavior is required, one can use `LocalSourceNode::flowsTo` to add back the missing flow.
|
||||
@@ -32,21 +32,7 @@ private DataFlow::LocalSourceNode vulnerableHostnameRef(DataFlow::TypeTracker t,
|
||||
result.asExpr() = allInterfacesStrConst
|
||||
)
|
||||
or
|
||||
// Due to bad performance when using normal setup with `vulnerableHostnameRef(t2, hostname).track(t2, t)`
|
||||
// we have inlined that code and forced a join
|
||||
exists(DataFlow::TypeTracker t2 |
|
||||
exists(DataFlow::StepSummary summary |
|
||||
vulnerableHostnameRef_first_join(t2, hostname, result, summary) and
|
||||
t = t2.append(summary)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate vulnerableHostnameRef_first_join(
|
||||
DataFlow::TypeTracker t2, string hostname, DataFlow::Node res, DataFlow::StepSummary summary
|
||||
) {
|
||||
DataFlow::StepSummary::step(vulnerableHostnameRef(t2, hostname), res, summary)
|
||||
exists(DataFlow::TypeTracker t2 | result = vulnerableHostnameRef(t2, hostname).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to a hostname that can be used to bind to all interfaces. */
|
||||
@@ -59,21 +45,7 @@ private DataFlow::LocalSourceNode vulnerableAddressTuple(DataFlow::TypeTracker t
|
||||
t.start() and
|
||||
result.asExpr() = any(Tuple tup | tup.getElt(0) = vulnerableHostnameRef(hostname).asExpr())
|
||||
or
|
||||
// Due to bad performance when using normal setup with `vulnerableAddressTuple(t2, hostname).track(t2, t)`
|
||||
// we have inlined that code and forced a join
|
||||
exists(DataFlow::TypeTracker t2 |
|
||||
exists(DataFlow::StepSummary summary |
|
||||
vulnerableAddressTuple_first_join(t2, hostname, result, summary) and
|
||||
t = t2.append(summary)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate vulnerableAddressTuple_first_join(
|
||||
DataFlow::TypeTracker t2, string hostname, DataFlow::Node res, DataFlow::StepSummary summary
|
||||
) {
|
||||
DataFlow::StepSummary::step(vulnerableAddressTuple(t2, hostname), res, summary)
|
||||
exists(DataFlow::TypeTracker t2 | result = vulnerableAddressTuple(t2, hostname).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to a tuple for which the first element is a hostname that can be used to bind to all interfaces. */
|
||||
|
||||
@@ -422,9 +422,9 @@ module API {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data-flow node to which `nd`, which is a use of an API-graph node, flows.
|
||||
* Gets a data-flow node to which `src`, which is a use of an API-graph node, flows.
|
||||
*
|
||||
* The flow from `nd` to that node may be inter-procedural.
|
||||
* The flow from `src` to that node may be inter-procedural.
|
||||
*/
|
||||
private DataFlow::LocalSourceNode trackUseNode(
|
||||
DataFlow::LocalSourceNode src, DataFlow::TypeTracker t
|
||||
@@ -433,23 +433,19 @@ module API {
|
||||
use(_, src) and
|
||||
result = src
|
||||
or
|
||||
// Due to bad performance when using `trackUseNode(t2, attr_name).track(t2, t)`
|
||||
// we have inlined that code and forced a join
|
||||
exists(DataFlow::StepSummary summary |
|
||||
t = trackUseNode_first_join(src, result, summary).append(summary)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private DataFlow::TypeTracker trackUseNode_first_join(
|
||||
DataFlow::LocalSourceNode src, DataFlow::LocalSourceNode res, DataFlow::StepSummary summary
|
||||
) {
|
||||
DataFlow::StepSummary::step(trackUseNode(src, result), res, summary)
|
||||
exists(DataFlow::TypeTracker t2 | result = trackUseNode(src, t2).track(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data-flow node to which `src`, which is a use of an API-graph node, flows.
|
||||
*
|
||||
* The flow from `src` to that node may be inter-procedural.
|
||||
*/
|
||||
cached
|
||||
DataFlow::LocalSourceNode trackUseNode(DataFlow::LocalSourceNode src) {
|
||||
result = trackUseNode(src, DataFlow::TypeTracker::end())
|
||||
result = trackUseNode(src, DataFlow::TypeTracker::end()) and
|
||||
// We exclude module variable nodes, as these do not correspond to real uses.
|
||||
not result instanceof DataFlow::ModuleVariableNode
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -570,21 +570,7 @@ module Cryptography {
|
||||
arg = any(KeyGeneration::Range r).getKeySizeArg() and
|
||||
result = arg.getALocalSource()
|
||||
or
|
||||
// Due to bad performance when using normal setup with we have inlined that code and forced a join
|
||||
exists(DataFlow::TypeBackTracker t2 |
|
||||
exists(DataFlow::StepSummary summary |
|
||||
keysizeBacktracker_first_join(t2, arg, result, summary) and
|
||||
t = t2.prepend(summary)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate keysizeBacktracker_first_join(
|
||||
DataFlow::TypeBackTracker t2, DataFlow::Node arg, DataFlow::Node res,
|
||||
DataFlow::StepSummary summary
|
||||
) {
|
||||
DataFlow::StepSummary::step(res, keysizeBacktracker(t2, arg), summary)
|
||||
exists(DataFlow::TypeBackTracker t2 | result = keysizeBacktracker(t2, arg).backtrack(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a back-reference to the keysize argument `arg` that was used to generate a new key-pair. */
|
||||
|
||||
@@ -51,8 +51,8 @@ module StepSummary {
|
||||
* heap and/or inter-procedural step from `nodeFrom` to `nodeTo`.
|
||||
*/
|
||||
cached
|
||||
predicate step(LocalSourceNode nodeFrom, Node nodeTo, StepSummary summary) {
|
||||
exists(Node mid | typePreservingStep*(nodeFrom, mid) and smallstep(mid, nodeTo, summary))
|
||||
predicate step(LocalSourceNode nodeFrom, LocalSourceNode nodeTo, StepSummary summary) {
|
||||
exists(Node mid | nodeFrom.flowsTo(mid) and smallstep(mid, nodeTo, summary))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,7 +63,7 @@ module StepSummary {
|
||||
* type-preserving steps.
|
||||
*/
|
||||
predicate smallstep(Node nodeFrom, Node nodeTo, StepSummary summary) {
|
||||
typePreservingStep(nodeFrom, nodeTo) and
|
||||
jumpStep(nodeFrom, nodeTo) and
|
||||
summary = LevelStep()
|
||||
or
|
||||
callStep(nodeFrom, nodeTo) and summary = CallStep()
|
||||
@@ -80,12 +80,6 @@ module StepSummary {
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if it's reasonable to expect the data flow step from `nodeFrom` to `nodeTo` to preserve types. */
|
||||
private predicate typePreservingStep(Node nodeFrom, Node nodeTo) {
|
||||
simpleLocalFlowStep(nodeFrom, nodeTo) or
|
||||
jumpStep(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a callable for the call where `nodeFrom` is used as the `i`'th argument.
|
||||
*
|
||||
@@ -274,10 +268,10 @@ class TypeTracker extends TTypeTracker {
|
||||
* heap and/or inter-procedural step from `nodeFrom` to `nodeTo`.
|
||||
*/
|
||||
pragma[inline]
|
||||
TypeTracker step(LocalSourceNode nodeFrom, Node nodeTo) {
|
||||
TypeTracker step(LocalSourceNode nodeFrom, LocalSourceNode nodeTo) {
|
||||
exists(StepSummary summary |
|
||||
StepSummary::step(nodeFrom, nodeTo, summary) and
|
||||
result = this.append(summary)
|
||||
StepSummary::step(nodeFrom, pragma[only_bind_out](nodeTo), pragma[only_bind_into](summary)) and
|
||||
result = this.append(pragma[only_bind_into](summary))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -312,7 +306,7 @@ class TypeTracker extends TTypeTracker {
|
||||
result = this.append(summary)
|
||||
)
|
||||
or
|
||||
typePreservingStep(nodeFrom, nodeTo) and
|
||||
simpleLocalFlowStep(nodeFrom, nodeTo) and
|
||||
result = this
|
||||
}
|
||||
}
|
||||
@@ -417,8 +411,8 @@ class TypeBackTracker extends TTypeBackTracker {
|
||||
pragma[inline]
|
||||
TypeBackTracker step(LocalSourceNode nodeFrom, LocalSourceNode nodeTo) {
|
||||
exists(StepSummary summary |
|
||||
StepSummary::step(nodeFrom, nodeTo, summary) and
|
||||
this = result.prepend(summary)
|
||||
StepSummary::step(pragma[only_bind_out](nodeFrom), nodeTo, pragma[only_bind_into](summary)) and
|
||||
this = result.prepend(pragma[only_bind_into](summary))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -453,7 +447,7 @@ class TypeBackTracker extends TTypeBackTracker {
|
||||
this = result.prepend(summary)
|
||||
)
|
||||
or
|
||||
typePreservingStep(nodeFrom, nodeTo) and
|
||||
simpleLocalFlowStep(nodeFrom, nodeTo) and
|
||||
this = result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,22 +119,6 @@ class Node extends TNode {
|
||||
/** Gets the expression corresponding to this node, if any. */
|
||||
Expr asExpr() { none() }
|
||||
|
||||
/**
|
||||
* Gets a node that this node may flow to using one heap and/or interprocedural step.
|
||||
*
|
||||
* See `TypeTracker` for more details about how to use this.
|
||||
*/
|
||||
pragma[inline]
|
||||
Node track(TypeTracker t2, TypeTracker t) { t = t2.step(this, result) }
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
pragma[inline]
|
||||
LocalSourceNode backtrack(TypeBackTracker t2, TypeBackTracker t) { t2 = t.step(result, this) }
|
||||
|
||||
/**
|
||||
* Gets a local source node from which data may flow to this node in zero or more local data-flow steps.
|
||||
*/
|
||||
|
||||
@@ -10,13 +10,6 @@ import python
|
||||
import DataFlowPublic
|
||||
private import DataFlowPrivate
|
||||
|
||||
private predicate comes_from_cfgnode(Node node) {
|
||||
exists(CfgNode first, Node second |
|
||||
simpleLocalFlowStep(first, second) and
|
||||
simpleLocalFlowStep*(second, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that is a source of local flow. This includes things like
|
||||
* - Expressions
|
||||
@@ -40,8 +33,7 @@ private predicate comes_from_cfgnode(Node node) {
|
||||
class LocalSourceNode extends Node {
|
||||
cached
|
||||
LocalSourceNode() {
|
||||
not comes_from_cfgnode(this) and
|
||||
not this instanceof ModuleVariableNode and
|
||||
not simpleLocalFlowStep(_, this) and
|
||||
// Currently, we create synthetic post-update nodes for
|
||||
// - arguments to calls that may modify said argument
|
||||
// - direct reads a writes of object attributes
|
||||
@@ -85,6 +77,22 @@ class LocalSourceNode extends Node {
|
||||
* Gets a call to this node.
|
||||
*/
|
||||
CallCfgNode getACall() { Cached::call(this, result) }
|
||||
|
||||
/**
|
||||
* Gets a node that this node may flow to using one heap and/or interprocedural step.
|
||||
*
|
||||
* See `TypeTracker` for more details about how to use this.
|
||||
*/
|
||||
pragma[inline]
|
||||
LocalSourceNode track(TypeTracker t2, TypeTracker t) { t = t2.step(this, result) }
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
pragma[inline]
|
||||
LocalSourceNode backtrack(TypeBackTracker t2, TypeBackTracker t) { t2 = t.step(result, this) }
|
||||
}
|
||||
|
||||
cached
|
||||
|
||||
@@ -83,23 +83,11 @@ private module CryptographyModel {
|
||||
result.(DataFlow::CallCfgNode).getFunction() = curveClassWithKeySize(keySize) and
|
||||
origin = result
|
||||
or
|
||||
// Due to bad performance when using normal setup with we have inlined that code and forced a join
|
||||
exists(DataFlow::TypeTracker t2 |
|
||||
exists(DataFlow::StepSummary summary |
|
||||
curveClassInstanceWithKeySize_first_join(t2, keySize, origin, result, summary) and
|
||||
t = t2.append(summary)
|
||||
)
|
||||
result = curveClassInstanceWithKeySize(t2, keySize, origin).track(t2, t)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate curveClassInstanceWithKeySize_first_join(
|
||||
DataFlow::TypeTracker t2, int keySize, DataFlow::Node origin, DataFlow::Node res,
|
||||
DataFlow::StepSummary summary
|
||||
) {
|
||||
DataFlow::StepSummary::step(curveClassInstanceWithKeySize(t2, keySize, origin), res, summary)
|
||||
}
|
||||
|
||||
/** Gets a reference to a predefined curve class instance with a specific key size (in bits), as well as the origin of the class. */
|
||||
DataFlow::Node curveClassInstanceWithKeySize(int keySize, DataFlow::Node origin) {
|
||||
curveClassInstanceWithKeySize(DataFlow::TypeTracker::end(), keySize, origin).flowsTo(result)
|
||||
|
||||
@@ -1325,7 +1325,7 @@ private module PrivateDjango {
|
||||
}
|
||||
|
||||
/** Gets a reference to the `django.http.response.HttpResponse.write` function. */
|
||||
private DataFlow::Node write(
|
||||
private DataFlow::LocalSourceNode write(
|
||||
django::http::response::HttpResponse::InstanceSource instance, DataFlow::TypeTracker t
|
||||
) {
|
||||
t.startInAttr("write") and
|
||||
@@ -1337,7 +1337,7 @@ private module PrivateDjango {
|
||||
|
||||
/** Gets a reference to the `django.http.response.HttpResponse.write` function. */
|
||||
DataFlow::Node write(django::http::response::HttpResponse::InstanceSource instance) {
|
||||
result = write(instance, DataFlow::TypeTracker::end())
|
||||
write(instance, DataFlow::TypeTracker::end()).flowsTo(result)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -54,7 +54,7 @@ module Connection {
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `db.Connection`. */
|
||||
private DataFlow::Node instance(DataFlow::TypeTracker t) {
|
||||
private DataFlow::LocalSourceNode instance(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result instanceof InstanceSource
|
||||
or
|
||||
@@ -62,7 +62,7 @@ module Connection {
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `db.Connection`. */
|
||||
DataFlow::Node instance() { result = instance(DataFlow::TypeTracker::end()) }
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,7 +71,7 @@ module Connection {
|
||||
*/
|
||||
module cursor {
|
||||
/** Gets a reference to the `cursor` method on a connection. */
|
||||
private DataFlow::Node methodRef(DataFlow::TypeTracker t) {
|
||||
private DataFlow::LocalSourceNode methodRef(DataFlow::TypeTracker t) {
|
||||
t.startInAttr("cursor") and
|
||||
result = Connection::instance()
|
||||
or
|
||||
@@ -79,10 +79,10 @@ module cursor {
|
||||
}
|
||||
|
||||
/** Gets a reference to the `cursor` method on a connection. */
|
||||
DataFlow::Node methodRef() { result = methodRef(DataFlow::TypeTracker::end()) }
|
||||
DataFlow::Node methodRef() { methodRef(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/** Gets a reference to a result of calling the `cursor` method on a connection. */
|
||||
private DataFlow::Node methodResult(DataFlow::TypeTracker t) {
|
||||
private DataFlow::LocalSourceNode methodResult(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result.asCfgNode().(CallNode).getFunction() = methodRef().asCfgNode()
|
||||
or
|
||||
@@ -90,7 +90,7 @@ module cursor {
|
||||
}
|
||||
|
||||
/** Gets a reference to a result of calling the `cursor` method on a connection. */
|
||||
DataFlow::Node methodResult() { result = methodResult(DataFlow::TypeTracker::end()) }
|
||||
DataFlow::Node methodResult() { methodResult(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,7 +101,7 @@ module cursor {
|
||||
*
|
||||
* See https://www.python.org/dev/peps/pep-0249/#id15.
|
||||
*/
|
||||
private DataFlow::Node execute(DataFlow::TypeTracker t) {
|
||||
private DataFlow::LocalSourceNode execute(DataFlow::TypeTracker t) {
|
||||
t.startInAttr("execute") and
|
||||
result in [cursor::methodResult(), Connection::instance()]
|
||||
or
|
||||
@@ -116,7 +116,7 @@ private DataFlow::Node execute(DataFlow::TypeTracker t) {
|
||||
*
|
||||
* See https://www.python.org/dev/peps/pep-0249/#id15.
|
||||
*/
|
||||
DataFlow::Node execute() { result = execute(DataFlow::TypeTracker::end()) }
|
||||
DataFlow::Node execute() { execute(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/** A call to the `execute` method on a cursor (or on a connection). */
|
||||
private class ExecuteCall extends SqlExecution::Range, DataFlow::CallCfgNode {
|
||||
|
||||
@@ -82,21 +82,7 @@ private DataFlow::LocalSourceNode re_flag_tracker(string flag_name, DataFlow::Ty
|
||||
result.asCfgNode() = binop
|
||||
)
|
||||
or
|
||||
// Due to bad performance when using normal setup with `re_flag_tracker(t2, attr_name).track(t2, t)`
|
||||
// we have inlined that code and forced a join
|
||||
exists(DataFlow::TypeTracker t2 |
|
||||
exists(DataFlow::StepSummary summary |
|
||||
re_flag_tracker_first_join(t2, flag_name, result, summary) and
|
||||
t = t2.append(summary)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate re_flag_tracker_first_join(
|
||||
DataFlow::TypeTracker t2, string flag_name, DataFlow::Node res, DataFlow::StepSummary summary
|
||||
) {
|
||||
DataFlow::StepSummary::step(re_flag_tracker(flag_name, t2), res, summary)
|
||||
exists(DataFlow::TypeTracker t2 | result = re_flag_tracker(flag_name, t2).track(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,7 +9,11 @@ class ApiUseTest extends InlineExpectationsTest {
|
||||
override string getARelevantTag() { result = "use" }
|
||||
|
||||
private predicate relevant_node(API::Node a, DataFlow::Node n, Location l) {
|
||||
n = a.getAUse() and l = n.getLocation()
|
||||
n = a.getAUse() and
|
||||
l = n.getLocation() and
|
||||
// Module variable nodes have no suitable location, so it's best to simply exclude them entirely
|
||||
// from the inline tests.
|
||||
not n instanceof DataFlow::ModuleVariableNode
|
||||
}
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
argumentToEnsureNotTaintedNotMarkedAsSpurious
|
||||
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
|
||||
failures
|
||||
@@ -1,4 +1,4 @@
|
||||
import experimental.dataflow.tainttracking.TestTaintLib
|
||||
import experimental.meta.InlineTaintTest
|
||||
import semmle.python.dataflow.new.BarrierGuards
|
||||
|
||||
class CustomSanitizerOverrides extends TestTaintTrackingConfiguration {
|
||||
@@ -1,27 +0,0 @@
|
||||
| test_string_const_compare.py:16 | ok | test_eq | ts |
|
||||
| test_string_const_compare.py:18 | ok | test_eq | ts |
|
||||
| test_string_const_compare.py:20 | ok | test_eq | ts |
|
||||
| test_string_const_compare.py:27 | ok | test_eq_unsafe | ts |
|
||||
| test_string_const_compare.py:29 | ok | test_eq_unsafe | ts |
|
||||
| test_string_const_compare.py:35 | fail | test_eq_with_or | ts |
|
||||
| test_string_const_compare.py:37 | ok | test_eq_with_or | ts |
|
||||
| test_string_const_compare.py:43 | ok | test_non_eq1 | ts |
|
||||
| test_string_const_compare.py:45 | ok | test_non_eq1 | ts |
|
||||
| test_string_const_compare.py:51 | ok | test_non_eq2 | ts |
|
||||
| test_string_const_compare.py:53 | fail | test_non_eq2 | ts |
|
||||
| test_string_const_compare.py:59 | ok | test_in_list | ts |
|
||||
| test_string_const_compare.py:61 | ok | test_in_list | ts |
|
||||
| test_string_const_compare.py:67 | ok | test_in_tuple | ts |
|
||||
| test_string_const_compare.py:69 | ok | test_in_tuple | ts |
|
||||
| test_string_const_compare.py:75 | ok | test_in_set | ts |
|
||||
| test_string_const_compare.py:77 | ok | test_in_set | ts |
|
||||
| test_string_const_compare.py:83 | ok | test_in_unsafe1 | ts |
|
||||
| test_string_const_compare.py:85 | ok | test_in_unsafe1 | ts |
|
||||
| test_string_const_compare.py:91 | ok | test_in_unsafe2 | ts |
|
||||
| test_string_const_compare.py:93 | ok | test_in_unsafe2 | ts |
|
||||
| test_string_const_compare.py:99 | ok | test_not_in1 | ts |
|
||||
| test_string_const_compare.py:101 | ok | test_not_in1 | ts |
|
||||
| test_string_const_compare.py:107 | ok | test_not_in2 | ts |
|
||||
| test_string_const_compare.py:109 | fail | test_not_in2 | ts |
|
||||
| test_string_const_compare.py:119 | fail | test_eq_thorugh_func | ts |
|
||||
| test_string_const_compare.py:121 | ok | test_eq_thorugh_func | ts |
|
||||
@@ -15,32 +15,32 @@ def test_eq():
|
||||
if ts == "safe":
|
||||
ensure_not_tainted(ts)
|
||||
else:
|
||||
ensure_tainted(ts)
|
||||
ensure_tainted(ts) # $ tainted
|
||||
# ts should still be tainted after exiting the if block
|
||||
ensure_tainted(ts)
|
||||
ensure_tainted(ts) # $ tainted
|
||||
|
||||
|
||||
def test_eq_unsafe(x="foo"):
|
||||
"""This test-case might seem strange, but it was a FP in our old points-to based analysis."""
|
||||
ts = TAINTED_STRING
|
||||
if ts == ts:
|
||||
ensure_tainted(ts)
|
||||
ensure_tainted(ts) # $ tainted
|
||||
if ts == x:
|
||||
ensure_tainted(ts)
|
||||
ensure_tainted(ts) # $ tainted
|
||||
|
||||
|
||||
def test_eq_with_or():
|
||||
ts = TAINTED_STRING
|
||||
if ts == "safe" or ts == "also_safe":
|
||||
ensure_not_tainted(ts)
|
||||
ensure_not_tainted(ts) # $ SPURIOUS: tainted
|
||||
else:
|
||||
ensure_tainted(ts)
|
||||
ensure_tainted(ts) # $ tainted
|
||||
|
||||
|
||||
def test_non_eq1():
|
||||
ts = TAINTED_STRING
|
||||
if ts != "safe":
|
||||
ensure_tainted(ts)
|
||||
ensure_tainted(ts) # $ tainted
|
||||
else:
|
||||
ensure_not_tainted(ts)
|
||||
|
||||
@@ -48,9 +48,9 @@ def test_non_eq1():
|
||||
def test_non_eq2():
|
||||
ts = TAINTED_STRING
|
||||
if not ts == "safe":
|
||||
ensure_tainted(ts)
|
||||
ensure_tainted(ts) # $ tainted
|
||||
else:
|
||||
ensure_not_tainted(ts)
|
||||
ensure_not_tainted(ts) # $ SPURIOUS: tainted
|
||||
|
||||
|
||||
def test_in_list():
|
||||
@@ -58,7 +58,7 @@ def test_in_list():
|
||||
if ts in ["safe", "also_safe"]:
|
||||
ensure_not_tainted(ts)
|
||||
else:
|
||||
ensure_tainted(ts)
|
||||
ensure_tainted(ts) # $ tainted
|
||||
|
||||
|
||||
def test_in_tuple():
|
||||
@@ -66,7 +66,7 @@ def test_in_tuple():
|
||||
if ts in ("safe", "also_safe"):
|
||||
ensure_not_tainted(ts)
|
||||
else:
|
||||
ensure_tainted(ts)
|
||||
ensure_tainted(ts) # $ tainted
|
||||
|
||||
|
||||
def test_in_set():
|
||||
@@ -74,29 +74,29 @@ def test_in_set():
|
||||
if ts in {"safe", "also_safe"}:
|
||||
ensure_not_tainted(ts)
|
||||
else:
|
||||
ensure_tainted(ts)
|
||||
ensure_tainted(ts) # $ tainted
|
||||
|
||||
|
||||
def test_in_unsafe1(xs):
|
||||
ts = TAINTED_STRING
|
||||
if ts in xs:
|
||||
ensure_tainted(ts)
|
||||
ensure_tainted(ts) # $ tainted
|
||||
else:
|
||||
ensure_tainted(ts)
|
||||
ensure_tainted(ts) # $ tainted
|
||||
|
||||
|
||||
def test_in_unsafe2(x):
|
||||
ts = TAINTED_STRING
|
||||
if ts in ["safe", x]:
|
||||
ensure_tainted(ts)
|
||||
ensure_tainted(ts) # $ tainted
|
||||
else:
|
||||
ensure_tainted(ts)
|
||||
ensure_tainted(ts) # $ tainted
|
||||
|
||||
|
||||
def test_not_in1():
|
||||
ts = TAINTED_STRING
|
||||
if ts not in ["safe", "also_safe"]:
|
||||
ensure_tainted(ts)
|
||||
ensure_tainted(ts) # $ tainted
|
||||
else:
|
||||
ensure_not_tainted(ts)
|
||||
|
||||
@@ -104,9 +104,9 @@ def test_not_in1():
|
||||
def test_not_in2():
|
||||
ts = TAINTED_STRING
|
||||
if not ts in ["safe", "also_safe"]:
|
||||
ensure_tainted(ts)
|
||||
ensure_tainted(ts) # $ tainted
|
||||
else:
|
||||
ensure_not_tainted(ts)
|
||||
ensure_not_tainted(ts) # $ SPURIOUS: tainted
|
||||
|
||||
|
||||
def is_safe(x):
|
||||
@@ -116,9 +116,9 @@ def is_safe(x):
|
||||
def test_eq_thorugh_func():
|
||||
ts = TAINTED_STRING
|
||||
if is_safe(ts):
|
||||
ensure_not_tainted(ts)
|
||||
ensure_not_tainted(ts) # $ SPURIOUS: tainted
|
||||
else:
|
||||
ensure_tainted(ts)
|
||||
ensure_tainted(ts) # $ tainted
|
||||
|
||||
|
||||
# Make tests runable
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
argumentToEnsureNotTaintedNotMarkedAsSpurious
|
||||
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
|
||||
failures
|
||||
isSanitizer
|
||||
| TestTaintTrackingConfiguration | test.py:21:39:21:39 | ControlFlowNode for s |
|
||||
| TestTaintTrackingConfiguration | test.py:50:10:50:29 | ControlFlowNode for emulated_escaping() |
|
||||
isSanitizerGuard
|
||||
| TestTaintTrackingConfiguration | test.py:35:8:35:26 | ControlFlowNode for emulated_is_safe() |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:29:8:29:17 | ControlFlowNode for is_safe() |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:44:8:44:17 | ControlFlowNode for is_safe() |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:52:12:52:21 | ControlFlowNode for is_safe() |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:72:8:72:17 | ControlFlowNode for is_safe() |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:80:12:80:21 | ControlFlowNode for is_safe() |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:104:8:104:17 | ControlFlowNode for is_safe() |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:127:12:127:21 | ControlFlowNode for is_safe() |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:132:16:132:25 | ControlFlowNode for is_safe() |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:137:20:137:29 | ControlFlowNode for is_safe() |
|
||||
| TestTaintTrackingConfiguration | test_reference.py:30:8:30:17 | ControlFlowNode for is_safe() |
|
||||
| TestTaintTrackingConfiguration | test_reference.py:40:8:40:25 | ControlFlowNode for is_safe() |
|
||||
| TestTaintTrackingConfiguration | test_reference.py:55:8:55:21 | ControlFlowNode for is_safe() |
|
||||
@@ -1,4 +1,4 @@
|
||||
import experimental.dataflow.tainttracking.TestTaintLib
|
||||
import experimental.meta.InlineTaintTest
|
||||
|
||||
class IsSafeCheck extends DataFlow::BarrierGuard {
|
||||
IsSafeCheck() {
|
||||
@@ -1,61 +0,0 @@
|
||||
test_taint
|
||||
| test.py:22 | ok | test_custom_sanitizer | s |
|
||||
| test.py:36 | ok | test_custom_sanitizer_guard | s |
|
||||
| test.py:38 | ok | test_custom_sanitizer_guard | s |
|
||||
| test.py:40 | ok | test_custom_sanitizer_guard | s |
|
||||
| test.py:51 | ok | test_escape | s2 |
|
||||
| test_logical.py:30 | ok | test_basic | s |
|
||||
| test_logical.py:32 | ok | test_basic | s |
|
||||
| test_logical.py:35 | ok | test_basic | s |
|
||||
| test_logical.py:37 | fail | test_basic | s |
|
||||
| test_logical.py:45 | ok | test_or | s |
|
||||
| test_logical.py:47 | ok | test_or | s |
|
||||
| test_logical.py:51 | ok | test_or | s |
|
||||
| test_logical.py:53 | ok | test_or | s |
|
||||
| test_logical.py:57 | ok | test_or | s |
|
||||
| test_logical.py:59 | ok | test_or | s |
|
||||
| test_logical.py:67 | ok | test_and | s |
|
||||
| test_logical.py:69 | ok | test_and | s |
|
||||
| test_logical.py:73 | ok | test_and | s |
|
||||
| test_logical.py:75 | ok | test_and | s |
|
||||
| test_logical.py:79 | ok | test_and | s |
|
||||
| test_logical.py:81 | fail | test_and | s |
|
||||
| test_logical.py:89 | fail | test_tricky | s |
|
||||
| test_logical.py:93 | fail | test_tricky | s_ |
|
||||
| test_logical.py:100 | fail | test_nesting_not | s |
|
||||
| test_logical.py:102 | ok | test_nesting_not | s |
|
||||
| test_logical.py:105 | ok | test_nesting_not | s |
|
||||
| test_logical.py:107 | fail | test_nesting_not | s |
|
||||
| test_logical.py:116 | ok | test_nesting_not_with_and_true | s |
|
||||
| test_logical.py:118 | ok | test_nesting_not_with_and_true | s |
|
||||
| test_logical.py:121 | ok | test_nesting_not_with_and_true | s |
|
||||
| test_logical.py:123 | ok | test_nesting_not_with_and_true | s |
|
||||
| test_logical.py:126 | ok | test_nesting_not_with_and_true | s |
|
||||
| test_logical.py:128 | ok | test_nesting_not_with_and_true | s |
|
||||
| test_logical.py:137 | fail | test_with_return | s |
|
||||
| test_logical.py:146 | fail | test_with_exception | s |
|
||||
| test_reference.py:31 | fail | test_basic | s2 |
|
||||
| test_reference.py:31 | ok | test_basic | s |
|
||||
| test_reference.py:33 | ok | test_basic | s |
|
||||
| test_reference.py:33 | ok | test_basic | s2 |
|
||||
| test_reference.py:41 | fail | test_identical_call | s.strip() |
|
||||
| test_reference.py:43 | ok | test_identical_call | s.strip() |
|
||||
| test_reference.py:56 | fail | test_class_attribute_access | c.foo |
|
||||
| test_reference.py:58 | ok | test_class_attribute_access | c.foo |
|
||||
isSanitizer
|
||||
| TestTaintTrackingConfiguration | test.py:21:39:21:39 | ControlFlowNode for s |
|
||||
| TestTaintTrackingConfiguration | test.py:50:10:50:29 | ControlFlowNode for emulated_escaping() |
|
||||
isSanitizerGuard
|
||||
| TestTaintTrackingConfiguration | test.py:35:8:35:26 | ControlFlowNode for emulated_is_safe() |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:29:8:29:17 | ControlFlowNode for is_safe() |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:44:8:44:17 | ControlFlowNode for is_safe() |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:50:12:50:21 | ControlFlowNode for is_safe() |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:66:8:66:17 | ControlFlowNode for is_safe() |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:72:12:72:21 | ControlFlowNode for is_safe() |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:92:8:92:17 | ControlFlowNode for is_safe() |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:115:12:115:21 | ControlFlowNode for is_safe() |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:120:16:120:25 | ControlFlowNode for is_safe() |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:125:20:125:29 | ControlFlowNode for is_safe() |
|
||||
| TestTaintTrackingConfiguration | test_reference.py:30:8:30:17 | ControlFlowNode for is_safe() |
|
||||
| TestTaintTrackingConfiguration | test_reference.py:40:8:40:25 | ControlFlowNode for is_safe() |
|
||||
| TestTaintTrackingConfiguration | test_reference.py:55:8:55:21 | ControlFlowNode for is_safe() |
|
||||
@@ -35,9 +35,9 @@ def test_custom_sanitizer_guard():
|
||||
if emulated_is_safe(s):
|
||||
ensure_not_tainted(s)
|
||||
s = TAINTED_STRING
|
||||
ensure_tainted(s)
|
||||
ensure_tainted(s) # $ tainted
|
||||
else:
|
||||
ensure_tainted(s)
|
||||
ensure_tainted(s) # $ tainted
|
||||
|
||||
|
||||
def emulated_escaping(arg):
|
||||
|
||||
@@ -29,12 +29,12 @@ def test_basic():
|
||||
if is_safe(s):
|
||||
ensure_not_tainted(s)
|
||||
else:
|
||||
ensure_tainted(s)
|
||||
ensure_tainted(s) # $ tainted
|
||||
|
||||
if not is_safe(s):
|
||||
ensure_tainted(s)
|
||||
ensure_tainted(s) # $ tainted
|
||||
else:
|
||||
ensure_not_tainted(s)
|
||||
ensure_not_tainted(s) # $ SPURIOUS: tainted
|
||||
|
||||
|
||||
def test_or():
|
||||
@@ -42,21 +42,27 @@ def test_or():
|
||||
|
||||
# x or y
|
||||
if is_safe(s) or random_choice():
|
||||
ensure_tainted(s) # might be tainted
|
||||
# might be tainted
|
||||
ensure_tainted(s) # $ tainted
|
||||
else:
|
||||
ensure_tainted(s) # must be tainted
|
||||
# must be tainted
|
||||
ensure_tainted(s) # $ tainted
|
||||
|
||||
# not (x or y)
|
||||
if not(is_safe(s) or random_choice()):
|
||||
ensure_tainted(s) # must be tainted
|
||||
# must be tainted
|
||||
ensure_tainted(s) # $ tainted
|
||||
else:
|
||||
ensure_tainted(s) # might be tainted
|
||||
# might be tainted
|
||||
ensure_tainted(s) # $ tainted
|
||||
|
||||
# not (x or y) == not x and not y [de Morgan's laws]
|
||||
if not is_safe(s) and not random_choice():
|
||||
ensure_tainted(s) # must be tainted
|
||||
# must be tainted
|
||||
ensure_tainted(s) # $ tainted
|
||||
else:
|
||||
ensure_tainted(s) # might be tainted
|
||||
# might be tainted
|
||||
ensure_tainted(s) # $ tainted
|
||||
|
||||
|
||||
def test_and():
|
||||
@@ -64,21 +70,27 @@ def test_and():
|
||||
|
||||
# x and y
|
||||
if is_safe(s) and random_choice():
|
||||
ensure_not_tainted(s) # must not be tainted
|
||||
# cannot be tainted
|
||||
ensure_not_tainted(s)
|
||||
else:
|
||||
ensure_tainted(s) # might be tainted
|
||||
# might be tainted
|
||||
ensure_tainted(s) # $ tainted
|
||||
|
||||
# not (x and y)
|
||||
if not(is_safe(s) and random_choice()):
|
||||
ensure_tainted(s) # might be tainted
|
||||
# might be tainted
|
||||
ensure_tainted(s) # $ tainted
|
||||
else:
|
||||
# cannot be tainted
|
||||
ensure_not_tainted(s)
|
||||
|
||||
# not (x and y) == not x or not y [de Morgan's laws]
|
||||
if not is_safe(s) or not random_choice():
|
||||
ensure_tainted(s) # might be tainted
|
||||
# might be tainted
|
||||
ensure_tainted(s) # $ tainted
|
||||
else:
|
||||
ensure_not_tainted(s)
|
||||
# cannot be tainted
|
||||
ensure_not_tainted(s) # $ SPURIOUS: tainted
|
||||
|
||||
|
||||
def test_tricky():
|
||||
@@ -86,25 +98,25 @@ def test_tricky():
|
||||
|
||||
x = is_safe(s)
|
||||
if x:
|
||||
ensure_not_tainted(s) # FP
|
||||
ensure_not_tainted(s) # $ SPURIOUS: tainted
|
||||
|
||||
s_ = s
|
||||
if is_safe(s):
|
||||
ensure_not_tainted(s_) # FP
|
||||
ensure_not_tainted(s_) # $ SPURIOUS: tainted
|
||||
|
||||
|
||||
def test_nesting_not():
|
||||
s = TAINTED_STRING
|
||||
|
||||
if not(not(is_safe(s))):
|
||||
ensure_not_tainted(s)
|
||||
ensure_not_tainted(s) # $ SPURIOUS: tainted
|
||||
else:
|
||||
ensure_tainted(s)
|
||||
ensure_tainted(s) # $ tainted
|
||||
|
||||
if not(not(not(is_safe(s)))):
|
||||
ensure_tainted(s)
|
||||
ensure_tainted(s) # $ tainted
|
||||
else:
|
||||
ensure_not_tainted(s)
|
||||
ensure_not_tainted(s) # $ SPURIOUS: tainted
|
||||
|
||||
|
||||
# Adding `and True` makes the sanitizer trigger when it would otherwise not. See output in
|
||||
@@ -113,17 +125,17 @@ def test_nesting_not_with_and_true():
|
||||
s = TAINTED_STRING
|
||||
|
||||
if not(is_safe(s) and True):
|
||||
ensure_tainted(s)
|
||||
ensure_tainted(s) # $ tainted
|
||||
else:
|
||||
ensure_not_tainted(s)
|
||||
|
||||
if not(not(is_safe(s) and True)):
|
||||
ensure_not_tainted(s)
|
||||
else:
|
||||
ensure_tainted(s)
|
||||
ensure_tainted(s) # $ tainted
|
||||
|
||||
if not(not(not(is_safe(s) and True))):
|
||||
ensure_tainted(s)
|
||||
ensure_tainted(s) # $ tainted
|
||||
else:
|
||||
ensure_not_tainted(s)
|
||||
|
||||
@@ -134,7 +146,7 @@ def test_with_return():
|
||||
if not is_safe(s):
|
||||
return
|
||||
|
||||
ensure_not_tainted(s)
|
||||
ensure_not_tainted(s) # $ SPURIOUS: tainted
|
||||
|
||||
|
||||
def test_with_exception():
|
||||
@@ -143,7 +155,7 @@ def test_with_exception():
|
||||
if not is_safe(s):
|
||||
raise Exception("unsafe")
|
||||
|
||||
ensure_not_tainted(s)
|
||||
ensure_not_tainted(s) # $ SPURIOUS: tainted
|
||||
|
||||
# Make tests runable
|
||||
|
||||
|
||||
@@ -28,9 +28,9 @@ def test_basic():
|
||||
s2 = s
|
||||
|
||||
if is_safe(s):
|
||||
ensure_not_tainted(s, s2)
|
||||
ensure_not_tainted(s, s2) # $ SPURIOUS: tainted
|
||||
else:
|
||||
ensure_tainted(s, s2)
|
||||
ensure_tainted(s, s2) # $ tainted
|
||||
|
||||
|
||||
def test_identical_call():
|
||||
@@ -38,9 +38,9 @@ def test_identical_call():
|
||||
s = TAINTED_STRING
|
||||
|
||||
if is_safe(s.strip()):
|
||||
ensure_not_tainted(s.strip())
|
||||
ensure_not_tainted(s.strip()) # $ SPURIOUS: tainted
|
||||
else:
|
||||
ensure_tainted(s.strip())
|
||||
ensure_tainted(s.strip()) # $ tainted
|
||||
|
||||
|
||||
class C(object):
|
||||
@@ -53,9 +53,9 @@ def test_class_attribute_access():
|
||||
c = C(s)
|
||||
|
||||
if is_safe(c.foo):
|
||||
ensure_not_tainted(c.foo)
|
||||
ensure_not_tainted(c.foo) # $ SPURIOUS: tainted
|
||||
else:
|
||||
ensure_tainted(c.foo)
|
||||
ensure_tainted(c.foo) # $ tainted
|
||||
|
||||
|
||||
# Make tests runable
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
argumentToEnsureNotTaintedNotMarkedAsSpurious
|
||||
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
|
||||
failures
|
||||
@@ -0,0 +1 @@
|
||||
import experimental.meta.InlineTaintTest
|
||||
@@ -1,34 +0,0 @@
|
||||
| test_collections.py:16 | ok | test_access | tainted_list.copy() |
|
||||
| test_collections.py:24 | ok | list_clear | tainted_list |
|
||||
| test_collections.py:27 | fail | list_clear | tainted_list |
|
||||
| test_pathlib.py:26 | fail | test_basic | tainted_path |
|
||||
| test_pathlib.py:28 | fail | test_basic | tainted_pure_path |
|
||||
| test_pathlib.py:29 | fail | test_basic | tainted_pure_posix_path |
|
||||
| test_pathlib.py:30 | fail | test_basic | tainted_pure_windows_path |
|
||||
| test_pathlib.py:32 | fail | test_basic | BinaryExpr |
|
||||
| test_pathlib.py:33 | fail | test_basic | BinaryExpr |
|
||||
| test_pathlib.py:35 | fail | test_basic | tainted_path.joinpath(..) |
|
||||
| test_pathlib.py:36 | fail | test_basic | pathlib.Path(..).joinpath(..) |
|
||||
| test_pathlib.py:37 | fail | test_basic | pathlib.Path(..).joinpath(..) |
|
||||
| test_pathlib.py:39 | fail | test_basic | str(..) |
|
||||
| test_pathlib.py:49 | fail | test_basic | tainted_posix_path |
|
||||
| test_pathlib.py:55 | fail | test_basic | tainted_windows_path |
|
||||
| test_string.py:17 | ok | str_methods | ts.casefold() |
|
||||
| test_string.py:19 | ok | str_methods | ts.format_map(..) |
|
||||
| test_string.py:20 | ok | str_methods | "{unsafe}".format_map(..) |
|
||||
| test_string.py:31 | ok | binary_decode_encode | base64.a85encode(..) |
|
||||
| test_string.py:32 | ok | binary_decode_encode | base64.a85decode(..) |
|
||||
| test_string.py:35 | ok | binary_decode_encode | base64.b85encode(..) |
|
||||
| test_string.py:36 | ok | binary_decode_encode | base64.b85decode(..) |
|
||||
| test_string.py:39 | ok | binary_decode_encode | base64.encodebytes(..) |
|
||||
| test_string.py:40 | ok | binary_decode_encode | base64.decodebytes(..) |
|
||||
| test_string.py:48 | ok | f_strings | Fstring |
|
||||
| test_unpacking.py:18 | ok | extended_unpacking | first |
|
||||
| test_unpacking.py:18 | ok | extended_unpacking | last |
|
||||
| test_unpacking.py:18 | ok | extended_unpacking | rest |
|
||||
| test_unpacking.py:23 | ok | also_allowed | a |
|
||||
| test_unpacking.py:31 | ok | also_allowed | b |
|
||||
| test_unpacking.py:31 | ok | also_allowed | c |
|
||||
| test_unpacking.py:39 | ok | nested | x |
|
||||
| test_unpacking.py:39 | ok | nested | xs |
|
||||
| test_unpacking.py:39 | ok | nested | ys |
|
||||
@@ -1 +0,0 @@
|
||||
import experimental.dataflow.tainttracking.TestTaintLib
|
||||
@@ -13,7 +13,7 @@ def test_access():
|
||||
tainted_list = TAINTED_LIST
|
||||
|
||||
ensure_tainted(
|
||||
tainted_list.copy(),
|
||||
tainted_list.copy(), # $ tainted
|
||||
)
|
||||
|
||||
|
||||
@@ -21,10 +21,10 @@ def list_clear():
|
||||
tainted_string = TAINTED_STRING
|
||||
tainted_list = [tainted_string]
|
||||
|
||||
ensure_tainted(tainted_list)
|
||||
ensure_tainted(tainted_list) # $ tainted
|
||||
|
||||
tainted_list.clear()
|
||||
ensure_not_tainted(tainted_list)
|
||||
ensure_not_tainted(tainted_list) # $ SPURIOUS: tainted
|
||||
|
||||
# Make tests runable
|
||||
|
||||
|
||||
@@ -23,20 +23,20 @@ def test_basic():
|
||||
tainted_pure_windows_path = pathlib.PureWindowsPath(ts)
|
||||
|
||||
ensure_tainted(
|
||||
tainted_path,
|
||||
tainted_path, # $ MISSING: tainted
|
||||
|
||||
tainted_pure_path,
|
||||
tainted_pure_posix_path,
|
||||
tainted_pure_windows_path,
|
||||
tainted_pure_path, # $ MISSING: tainted
|
||||
tainted_pure_posix_path, # $ MISSING: tainted
|
||||
tainted_pure_windows_path, # $ MISSING: tainted
|
||||
|
||||
pathlib.Path("foo") / ts,
|
||||
ts / pathlib.Path("foo"),
|
||||
pathlib.Path("foo") / ts, # $ MISSING: tainted
|
||||
ts / pathlib.Path("foo"), # $ MISSING: tainted
|
||||
|
||||
tainted_path.joinpath("foo", "bar"),
|
||||
pathlib.Path("foo").joinpath(tainted_path, "bar"),
|
||||
pathlib.Path("foo").joinpath("bar", tainted_path),
|
||||
tainted_path.joinpath("foo", "bar"), # $ MISSING: tainted
|
||||
pathlib.Path("foo").joinpath(tainted_path, "bar"), # $ MISSING: tainted
|
||||
pathlib.Path("foo").joinpath("bar", tainted_path), # $ MISSING: tainted
|
||||
|
||||
str(tainted_path),
|
||||
str(tainted_path), # $ MISSING: tainted
|
||||
|
||||
# TODO: Tainted methods and attributes
|
||||
# https://docs.python.org/3.8/library/pathlib.html#methods-and-properties
|
||||
@@ -46,13 +46,13 @@ def test_basic():
|
||||
tainted_posix_path = pathlib.PosixPath(ts)
|
||||
|
||||
ensure_tainted(
|
||||
tainted_posix_path,
|
||||
tainted_posix_path, # $ MISSING: tainted
|
||||
)
|
||||
|
||||
if os.name == "nt":
|
||||
tainted_windows_path = pathlib.WindowsPath(ts)
|
||||
ensure_tainted(
|
||||
tainted_windows_path,
|
||||
tainted_windows_path, # $ MISSING: tainted
|
||||
)
|
||||
|
||||
# Make tests runable
|
||||
|
||||
@@ -14,10 +14,10 @@ def str_methods():
|
||||
ts = TAINTED_STRING
|
||||
tb = TAINTED_BYTES
|
||||
ensure_tainted(
|
||||
ts.casefold(),
|
||||
ts.casefold(), # $ tainted
|
||||
|
||||
ts.format_map({}),
|
||||
"{unsafe}".format_map({"unsafe": ts}),
|
||||
ts.format_map({}), # $ tainted
|
||||
"{unsafe}".format_map({"unsafe": ts}), # $ tainted
|
||||
)
|
||||
|
||||
|
||||
@@ -28,16 +28,16 @@ def binary_decode_encode():
|
||||
|
||||
ensure_tainted(
|
||||
# New in Python 3.4
|
||||
base64.a85encode(tb),
|
||||
base64.a85decode(base64.a85encode(tb)),
|
||||
base64.a85encode(tb), # $ tainted
|
||||
base64.a85decode(base64.a85encode(tb)), # $ tainted
|
||||
|
||||
# New in Python 3.4
|
||||
base64.b85encode(tb),
|
||||
base64.b85decode(base64.b85encode(tb)),
|
||||
base64.b85encode(tb), # $ tainted
|
||||
base64.b85decode(base64.b85encode(tb)), # $ tainted
|
||||
|
||||
# New in Python 3.1
|
||||
base64.encodebytes(tb),
|
||||
base64.decodebytes(base64.encodebytes(tb)),
|
||||
base64.encodebytes(tb), # $ tainted
|
||||
base64.decodebytes(base64.encodebytes(tb)), # $ tainted
|
||||
)
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ def f_strings():
|
||||
print("\n# f_strings")
|
||||
ts = TAINTED_STRING
|
||||
|
||||
ensure_tainted(f"foo {ts} bar")
|
||||
ensure_tainted(f"foo {ts} bar") # $ tainted
|
||||
|
||||
|
||||
# Make tests runable
|
||||
|
||||
@@ -15,12 +15,12 @@ if TYPE_CHECKING:
|
||||
|
||||
def extended_unpacking():
|
||||
first, *rest, last = TAINTED_LIST
|
||||
ensure_tainted(first, rest, last)
|
||||
ensure_tainted(first, rest, last) # $ tainted
|
||||
|
||||
|
||||
def also_allowed():
|
||||
*a, = TAINTED_LIST
|
||||
ensure_tainted(a)
|
||||
ensure_tainted(a) # $ tainted
|
||||
|
||||
# for b, *c in [(1, 2, 3), (4, 5, 6, 7)]:
|
||||
# print(c)
|
||||
@@ -28,7 +28,7 @@ def also_allowed():
|
||||
# i=1; c=[5,6,7]
|
||||
|
||||
for b, *c in [TAINTED_LIST, TAINTED_LIST]:
|
||||
ensure_tainted(b, c)
|
||||
ensure_tainted(b, c) # $ tainted
|
||||
|
||||
|
||||
def nested():
|
||||
@@ -36,7 +36,7 @@ def nested():
|
||||
ll = [l,l]
|
||||
|
||||
[[x, *xs], ys] = ll
|
||||
ensure_tainted(x, xs, ys)
|
||||
ensure_tainted(x, xs, ys) # $ tainted
|
||||
|
||||
|
||||
# Make tests runable
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
argumentToEnsureNotTaintedNotMarkedAsSpurious
|
||||
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
|
||||
failures
|
||||
@@ -0,0 +1 @@
|
||||
import experimental.meta.InlineTaintTest
|
||||
@@ -1,176 +0,0 @@
|
||||
| test_collections.py:23 | ok | test_construction | tainted_string |
|
||||
| test_collections.py:24 | ok | test_construction | tainted_list |
|
||||
| test_collections.py:25 | ok | test_construction | tainted_tuple |
|
||||
| test_collections.py:26 | ok | test_construction | tainted_set |
|
||||
| test_collections.py:27 | ok | test_construction | tainted_dict |
|
||||
| test_collections.py:31 | ok | test_construction | list(..) |
|
||||
| test_collections.py:32 | ok | test_construction | list(..) |
|
||||
| test_collections.py:33 | ok | test_construction | list(..) |
|
||||
| test_collections.py:34 | ok | test_construction | list(..) |
|
||||
| test_collections.py:35 | ok | test_construction | list(..) |
|
||||
| test_collections.py:37 | ok | test_construction | tuple(..) |
|
||||
| test_collections.py:38 | ok | test_construction | set(..) |
|
||||
| test_collections.py:39 | ok | test_construction | frozenset(..) |
|
||||
| test_collections.py:47 | ok | test_access | tainted_list[0] |
|
||||
| test_collections.py:48 | ok | test_access | tainted_list[x] |
|
||||
| test_collections.py:49 | ok | test_access | tainted_list[Slice] |
|
||||
| test_collections.py:51 | ok | test_access | sorted(..) |
|
||||
| test_collections.py:52 | ok | test_access | reversed(..) |
|
||||
| test_collections.py:53 | ok | test_access | iter(..) |
|
||||
| test_collections.py:54 | ok | test_access | next(..) |
|
||||
| test_collections.py:58 | ok | test_access | a |
|
||||
| test_collections.py:58 | ok | test_access | b |
|
||||
| test_collections.py:58 | ok | test_access | c |
|
||||
| test_collections.py:61 | ok | test_access | h |
|
||||
| test_collections.py:63 | ok | test_access | i |
|
||||
| test_collections.py:70 | ok | test_dict_access | tainted_dict["name"] |
|
||||
| test_collections.py:71 | ok | test_dict_access | tainted_dict.get(..) |
|
||||
| test_collections.py:72 | ok | test_dict_access | tainted_dict[x] |
|
||||
| test_collections.py:73 | ok | test_dict_access | tainted_dict.copy() |
|
||||
| test_collections.py:77 | ok | test_dict_access | v |
|
||||
| test_collections.py:79 | ok | test_dict_access | v |
|
||||
| test_collections.py:87 | fail | test_named_tuple | point[0] |
|
||||
| test_collections.py:88 | fail | test_named_tuple | point.x |
|
||||
| test_collections.py:92 | ok | test_named_tuple | point[1] |
|
||||
| test_collections.py:93 | ok | test_named_tuple | point.y |
|
||||
| test_collections.py:97 | fail | test_named_tuple | a |
|
||||
| test_collections.py:98 | ok | test_named_tuple | b |
|
||||
| test_collections.py:106 | fail | test_defaultdict | tainted_default_dict["name"] |
|
||||
| test_collections.py:107 | fail | test_defaultdict | tainted_default_dict.get(..) |
|
||||
| test_collections.py:108 | fail | test_defaultdict | tainted_default_dict[x] |
|
||||
| test_collections.py:109 | fail | test_defaultdict | tainted_default_dict.copy() |
|
||||
| test_collections.py:112 | fail | test_defaultdict | v |
|
||||
| test_collections.py:114 | fail | test_defaultdict | v |
|
||||
| test_collections.py:121 | ok | test_copy_1 | copy(..) |
|
||||
| test_collections.py:122 | ok | test_copy_1 | deepcopy(..) |
|
||||
| test_collections.py:130 | ok | test_copy_2 | copy.copy(..) |
|
||||
| test_collections.py:131 | ok | test_copy_2 | copy.deepcopy(..) |
|
||||
| test_collections.py:139 | ok | list_index_assign | my_list |
|
||||
| test_collections.py:142 | fail | list_index_assign | my_list |
|
||||
| test_collections.py:149 | ok | list_index_aug_assign | my_list |
|
||||
| test_collections.py:152 | fail | list_index_aug_assign | my_list |
|
||||
| test_collections.py:159 | ok | list_append | my_list |
|
||||
| test_collections.py:162 | ok | list_append | my_list |
|
||||
| test_collections.py:169 | ok | list_extend | my_list |
|
||||
| test_collections.py:172 | fail | list_extend | my_list |
|
||||
| test_collections.py:179 | ok | dict_update_dict | my_dict |
|
||||
| test_collections.py:182 | fail | dict_update_dict | my_dict |
|
||||
| test_collections.py:189 | ok | dict_update_kv_list | my_dict |
|
||||
| test_collections.py:192 | fail | dict_update_kv_list | my_dict |
|
||||
| test_collections.py:198 | ok | dict_update_kv_arg | my_dict |
|
||||
| test_collections.py:201 | fail | dict_update_kv_arg | my_dict |
|
||||
| test_collections.py:208 | ok | dict_manual_update | my_dict |
|
||||
| test_collections.py:212 | fail | dict_manual_update | my_dict |
|
||||
| test_collections.py:220 | fail | dict_merge | merged |
|
||||
| test_collections.py:227 | ok | set_add | my_set |
|
||||
| test_collections.py:230 | ok | set_add | my_set |
|
||||
| test_json.py:26 | ok | test | json.dumps(..) |
|
||||
| test_json.py:27 | ok | test | json.loads(..) |
|
||||
| test_json.py:34 | fail | test | tainted_filelike |
|
||||
| test_json.py:35 | fail | test | json.load(..) |
|
||||
| test_json.py:48 | ok | non_syntacical | dumps(..) |
|
||||
| test_json.py:49 | ok | non_syntacical | dumps_alias(..) |
|
||||
| test_json.py:50 | ok | non_syntacical | loads(..) |
|
||||
| test_json.py:57 | fail | non_syntacical | tainted_filelike |
|
||||
| test_json.py:58 | fail | non_syntacical | load(..) |
|
||||
| test_string.py:25 | ok | str_operations | ts |
|
||||
| test_string.py:26 | ok | str_operations | BinaryExpr |
|
||||
| test_string.py:27 | ok | str_operations | BinaryExpr |
|
||||
| test_string.py:28 | ok | str_operations | BinaryExpr |
|
||||
| test_string.py:29 | ok | str_operations | ts[Slice] |
|
||||
| test_string.py:30 | ok | str_operations | ts[Slice] |
|
||||
| test_string.py:31 | ok | str_operations | ts[Slice] |
|
||||
| test_string.py:32 | ok | str_operations | ts[0] |
|
||||
| test_string.py:33 | ok | str_operations | str(..) |
|
||||
| test_string.py:34 | ok | str_operations | bytes(..) |
|
||||
| test_string.py:35 | ok | str_operations | unicode(..) |
|
||||
| test_string.py:39 | ok | str_operations | aug_assignment |
|
||||
| test_string.py:41 | ok | str_operations | aug_assignment |
|
||||
| test_string.py:49 | ok | str_methods | ts.capitalize() |
|
||||
| test_string.py:50 | ok | str_methods | ts.center(..) |
|
||||
| test_string.py:51 | ok | str_methods | ts.expandtabs() |
|
||||
| test_string.py:53 | ok | str_methods | ts.format() |
|
||||
| test_string.py:54 | ok | str_methods | "{}".format(..) |
|
||||
| test_string.py:55 | ok | str_methods | "{unsafe}".format(..) |
|
||||
| test_string.py:57 | ok | str_methods | ts.join(..) |
|
||||
| test_string.py:58 | ok | str_methods | "".join(..) |
|
||||
| test_string.py:60 | ok | str_methods | ts.ljust(..) |
|
||||
| test_string.py:61 | ok | str_methods | ts.lstrip() |
|
||||
| test_string.py:62 | ok | str_methods | ts.lower() |
|
||||
| test_string.py:64 | ok | str_methods | ts.replace(..) |
|
||||
| test_string.py:65 | ok | str_methods | "safe".replace(..) |
|
||||
| test_string.py:67 | ok | str_methods | ts.rjust(..) |
|
||||
| test_string.py:68 | ok | str_methods | ts.rstrip() |
|
||||
| test_string.py:69 | ok | str_methods | ts.strip() |
|
||||
| test_string.py:70 | ok | str_methods | ts.swapcase() |
|
||||
| test_string.py:71 | ok | str_methods | ts.title() |
|
||||
| test_string.py:72 | ok | str_methods | ts.upper() |
|
||||
| test_string.py:73 | ok | str_methods | ts.zfill(..) |
|
||||
| test_string.py:75 | ok | str_methods | ts.encode(..) |
|
||||
| test_string.py:76 | ok | str_methods | ts.encode(..).decode(..) |
|
||||
| test_string.py:78 | ok | str_methods | tb.decode(..) |
|
||||
| test_string.py:79 | ok | str_methods | tb.decode(..).encode(..) |
|
||||
| test_string.py:82 | ok | str_methods | ts.partition(..) |
|
||||
| test_string.py:83 | ok | str_methods | ts.rpartition(..) |
|
||||
| test_string.py:84 | ok | str_methods | ts.rsplit(..) |
|
||||
| test_string.py:85 | ok | str_methods | ts.split(..) |
|
||||
| test_string.py:86 | ok | str_methods | ts.splitlines() |
|
||||
| test_string.py:91 | ok | str_methods | "safe".replace(..) |
|
||||
| test_string.py:93 | fail | str_methods | ts.join(..) |
|
||||
| test_string.py:94 | fail | str_methods | ts.join(..) |
|
||||
| test_string.py:104 | fail | non_syntactic | meth() |
|
||||
| test_string.py:105 | fail | non_syntactic | _str(..) |
|
||||
| test_string.py:114 | ok | percent_fmt | BinaryExpr |
|
||||
| test_string.py:115 | ok | percent_fmt | BinaryExpr |
|
||||
| test_string.py:116 | ok | percent_fmt | BinaryExpr |
|
||||
| test_string.py:126 | ok | binary_decode_encode | base64.b64encode(..) |
|
||||
| test_string.py:127 | ok | binary_decode_encode | base64.b64decode(..) |
|
||||
| test_string.py:129 | ok | binary_decode_encode | base64.standard_b64encode(..) |
|
||||
| test_string.py:130 | ok | binary_decode_encode | base64.standard_b64decode(..) |
|
||||
| test_string.py:132 | ok | binary_decode_encode | base64.urlsafe_b64encode(..) |
|
||||
| test_string.py:133 | ok | binary_decode_encode | base64.urlsafe_b64decode(..) |
|
||||
| test_string.py:135 | ok | binary_decode_encode | base64.b32encode(..) |
|
||||
| test_string.py:136 | ok | binary_decode_encode | base64.b32decode(..) |
|
||||
| test_string.py:138 | ok | binary_decode_encode | base64.b16encode(..) |
|
||||
| test_string.py:139 | ok | binary_decode_encode | base64.b16decode(..) |
|
||||
| test_string.py:142 | ok | binary_decode_encode | base64.encodestring(..) |
|
||||
| test_string.py:143 | ok | binary_decode_encode | base64.decodestring(..) |
|
||||
| test_string.py:148 | fail | binary_decode_encode | quopri.encodestring(..) |
|
||||
| test_string.py:149 | fail | binary_decode_encode | quopri.decodestring(..) |
|
||||
| test_string.py:159 | ok | test_os_path_join | os.path.join(..) |
|
||||
| test_string.py:160 | ok | test_os_path_join | os.path.join(..) |
|
||||
| test_string.py:161 | ok | test_os_path_join | os.path.join(..) |
|
||||
| test_string.py:162 | ok | test_os_path_join | ospath_alias.join(..) |
|
||||
| test_unpacking.py:16 | ok | unpacking | a |
|
||||
| test_unpacking.py:16 | ok | unpacking | b |
|
||||
| test_unpacking.py:16 | ok | unpacking | c |
|
||||
| test_unpacking.py:22 | ok | unpacking_to_list | a |
|
||||
| test_unpacking.py:22 | ok | unpacking_to_list | b |
|
||||
| test_unpacking.py:22 | ok | unpacking_to_list | c |
|
||||
| test_unpacking.py:31 | ok | nested | a1 |
|
||||
| test_unpacking.py:31 | ok | nested | a2 |
|
||||
| test_unpacking.py:31 | ok | nested | a3 |
|
||||
| test_unpacking.py:31 | ok | nested | b |
|
||||
| test_unpacking.py:31 | ok | nested | c |
|
||||
| test_unpacking.py:35 | ok | nested | a1 |
|
||||
| test_unpacking.py:35 | ok | nested | a2 |
|
||||
| test_unpacking.py:35 | ok | nested | a3 |
|
||||
| test_unpacking.py:35 | ok | nested | b |
|
||||
| test_unpacking.py:35 | ok | nested | c |
|
||||
| test_unpacking.py:39 | ok | nested | a1 |
|
||||
| test_unpacking.py:39 | ok | nested | a2 |
|
||||
| test_unpacking.py:39 | ok | nested | a3 |
|
||||
| test_unpacking.py:39 | ok | nested | b |
|
||||
| test_unpacking.py:39 | ok | nested | c |
|
||||
| test_unpacking.py:46 | ok | unpack_from_set | a |
|
||||
| test_unpacking.py:46 | ok | unpack_from_set | b |
|
||||
| test_unpacking.py:46 | ok | unpack_from_set | c |
|
||||
| test_unpacking.py:55 | ok | contrived_1 | a |
|
||||
| test_unpacking.py:55 | ok | contrived_1 | b |
|
||||
| test_unpacking.py:55 | ok | contrived_1 | c |
|
||||
| test_unpacking.py:56 | fail | contrived_1 | d |
|
||||
| test_unpacking.py:56 | fail | contrived_1 | e |
|
||||
| test_unpacking.py:56 | fail | contrived_1 | f |
|
||||
| test_unpacking.py:65 | ok | contrived_2 | a |
|
||||
| test_unpacking.py:65 | ok | contrived_2 | b |
|
||||
| test_unpacking.py:65 | ok | contrived_2 | c |
|
||||
@@ -1 +0,0 @@
|
||||
import experimental.dataflow.tainttracking.TestTaintLib
|
||||
@@ -20,23 +20,23 @@ def test_construction():
|
||||
tainted_dict = {'key': tainted_string}
|
||||
|
||||
ensure_tainted(
|
||||
tainted_string,
|
||||
tainted_list,
|
||||
tainted_tuple,
|
||||
tainted_set,
|
||||
tainted_dict,
|
||||
tainted_string, # $ tainted
|
||||
tainted_list, # $ tainted
|
||||
tainted_tuple, # $ tainted
|
||||
tainted_set, # $ tainted
|
||||
tainted_dict, # $ tainted
|
||||
)
|
||||
|
||||
ensure_tainted(
|
||||
list(tainted_list),
|
||||
list(tainted_tuple),
|
||||
list(tainted_set),
|
||||
list(tainted_dict.values()),
|
||||
list(tainted_dict.items()),
|
||||
list(tainted_list), # $ tainted
|
||||
list(tainted_tuple), # $ tainted
|
||||
list(tainted_set), # $ tainted
|
||||
list(tainted_dict.values()), # $ tainted
|
||||
list(tainted_dict.items()), # $ tainted
|
||||
|
||||
tuple(tainted_list),
|
||||
set(tainted_list),
|
||||
frozenset(tainted_list),
|
||||
tuple(tainted_list), # $ tainted
|
||||
set(tainted_list), # $ tainted
|
||||
frozenset(tainted_list), # $ tainted
|
||||
)
|
||||
|
||||
|
||||
@@ -44,39 +44,39 @@ def test_access(x, y, z):
|
||||
tainted_list = TAINTED_LIST
|
||||
|
||||
ensure_tainted(
|
||||
tainted_list[0],
|
||||
tainted_list[x],
|
||||
tainted_list[y:z],
|
||||
tainted_list[0], # $ tainted
|
||||
tainted_list[x], # $ tainted
|
||||
tainted_list[y:z], # $ tainted
|
||||
|
||||
sorted(tainted_list),
|
||||
reversed(tainted_list),
|
||||
iter(tainted_list),
|
||||
next(iter(tainted_list)),
|
||||
sorted(tainted_list), # $ tainted
|
||||
reversed(tainted_list), # $ tainted
|
||||
iter(tainted_list), # $ tainted
|
||||
next(iter(tainted_list)), # $ tainted
|
||||
)
|
||||
|
||||
a, b, c = tainted_list[0:3]
|
||||
ensure_tainted(a, b, c)
|
||||
ensure_tainted(a, b, c) # $ tainted
|
||||
|
||||
for h in tainted_list:
|
||||
ensure_tainted(h)
|
||||
ensure_tainted(h) # $ tainted
|
||||
for i in reversed(tainted_list):
|
||||
ensure_tainted(i)
|
||||
ensure_tainted(i) # $ tainted
|
||||
|
||||
|
||||
def test_dict_access(x):
|
||||
tainted_dict = TAINTED_DICT
|
||||
|
||||
ensure_tainted(
|
||||
tainted_dict["name"],
|
||||
tainted_dict.get("name"),
|
||||
tainted_dict[x],
|
||||
tainted_dict.copy(),
|
||||
tainted_dict["name"], # $ tainted
|
||||
tainted_dict.get("name"), # $ tainted
|
||||
tainted_dict[x], # $ tainted
|
||||
tainted_dict.copy(), # $ tainted
|
||||
)
|
||||
|
||||
for v in tainted_dict.values():
|
||||
ensure_tainted(v)
|
||||
ensure_tainted(v) # $ tainted
|
||||
for k, v in tainted_dict.items():
|
||||
ensure_tainted(v)
|
||||
ensure_tainted(v) # $ tainted
|
||||
|
||||
|
||||
def test_named_tuple(): # TODO: namedtuple currently not handled
|
||||
@@ -84,8 +84,8 @@ def test_named_tuple(): # TODO: namedtuple currently not handled
|
||||
point = Point(TAINTED_STRING, 'safe')
|
||||
|
||||
ensure_tainted(
|
||||
point[0],
|
||||
point.x,
|
||||
point[0], # $ MISSING: tainted
|
||||
point.x, # $ MISSING: tainted
|
||||
)
|
||||
|
||||
ensure_not_tainted(
|
||||
@@ -94,7 +94,7 @@ def test_named_tuple(): # TODO: namedtuple currently not handled
|
||||
)
|
||||
|
||||
a, b = point
|
||||
ensure_tainted(a)
|
||||
ensure_tainted(a) # $ MISSING: tainted
|
||||
ensure_not_tainted(b)
|
||||
|
||||
|
||||
@@ -103,23 +103,23 @@ def test_defaultdict(key, x): # TODO: defaultdict currently not handled
|
||||
tainted_default_dict[key] += TAINTED_STRING
|
||||
|
||||
ensure_tainted(
|
||||
tainted_default_dict["name"],
|
||||
tainted_default_dict.get("name"),
|
||||
tainted_default_dict[x],
|
||||
tainted_default_dict.copy(),
|
||||
tainted_default_dict["name"], # $ MISSING: tainted
|
||||
tainted_default_dict.get("name"), # $ MISSING: tainted
|
||||
tainted_default_dict[x], # $ MISSING: tainted
|
||||
tainted_default_dict.copy(), # $ MISSING: tainted
|
||||
)
|
||||
for v in tainted_default_dict.values():
|
||||
ensure_tainted(v)
|
||||
ensure_tainted(v) # $ MISSING: tainted
|
||||
for k, v in tainted_default_dict.items():
|
||||
ensure_tainted(v)
|
||||
ensure_tainted(v) # $ MISSING: tainted
|
||||
|
||||
|
||||
def test_copy_1():
|
||||
from copy import copy, deepcopy
|
||||
|
||||
ensure_tainted(
|
||||
copy(TAINTED_LIST),
|
||||
deepcopy(TAINTED_LIST),
|
||||
copy(TAINTED_LIST), # $ tainted
|
||||
deepcopy(TAINTED_LIST), # $ tainted
|
||||
)
|
||||
|
||||
|
||||
@@ -127,8 +127,8 @@ def test_copy_2():
|
||||
import copy
|
||||
|
||||
ensure_tainted(
|
||||
copy.copy(TAINTED_LIST),
|
||||
copy.deepcopy(TAINTED_LIST),
|
||||
copy.copy(TAINTED_LIST), # $ tainted
|
||||
copy.deepcopy(TAINTED_LIST), # $ tainted
|
||||
)
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ def list_index_assign():
|
||||
ensure_not_tainted(my_list)
|
||||
|
||||
my_list[0] = tainted_string
|
||||
ensure_tainted(my_list)
|
||||
ensure_tainted(my_list) # $ MISSING: tainted
|
||||
|
||||
|
||||
def list_index_aug_assign():
|
||||
@@ -149,7 +149,7 @@ def list_index_aug_assign():
|
||||
ensure_not_tainted(my_list)
|
||||
|
||||
my_list[0] += tainted_string
|
||||
ensure_tainted(my_list)
|
||||
ensure_tainted(my_list) # $ MISSING: tainted
|
||||
|
||||
|
||||
def list_append():
|
||||
@@ -159,7 +159,7 @@ def list_append():
|
||||
ensure_not_tainted(my_list)
|
||||
|
||||
my_list.append(tainted_string)
|
||||
ensure_tainted(my_list)
|
||||
ensure_tainted(my_list) # $ tainted
|
||||
|
||||
|
||||
def list_extend():
|
||||
@@ -169,7 +169,7 @@ def list_extend():
|
||||
ensure_not_tainted(my_list)
|
||||
|
||||
my_list.extend(tainted_list)
|
||||
ensure_tainted(my_list)
|
||||
ensure_tainted(my_list) # $ MISSING: tainted
|
||||
|
||||
|
||||
def dict_update_dict():
|
||||
@@ -179,7 +179,7 @@ def dict_update_dict():
|
||||
ensure_not_tainted(my_dict)
|
||||
|
||||
my_dict.update(tainted_dict)
|
||||
ensure_tainted(my_dict)
|
||||
ensure_tainted(my_dict) # $ MISSING: tainted
|
||||
|
||||
|
||||
def dict_update_kv_list():
|
||||
@@ -189,7 +189,7 @@ def dict_update_kv_list():
|
||||
ensure_not_tainted(my_dict)
|
||||
|
||||
my_dict.update(tainted_kv_list)
|
||||
ensure_tainted(my_dict)
|
||||
ensure_tainted(my_dict) # $ MISSING: tainted
|
||||
|
||||
|
||||
def dict_update_kv_arg():
|
||||
@@ -198,7 +198,7 @@ def dict_update_kv_arg():
|
||||
ensure_not_tainted(my_dict)
|
||||
|
||||
my_dict.update(key2=TAINTED_STRING)
|
||||
ensure_tainted(my_dict)
|
||||
ensure_tainted(my_dict) # $ MISSING: tainted
|
||||
|
||||
|
||||
def dict_manual_update():
|
||||
@@ -209,7 +209,7 @@ def dict_manual_update():
|
||||
|
||||
for k in tainted_dict:
|
||||
my_dict[k] = tainted_dict[k]
|
||||
ensure_tainted(my_dict)
|
||||
ensure_tainted(my_dict) # $ MISSING: tainted
|
||||
|
||||
|
||||
def dict_merge():
|
||||
@@ -217,7 +217,7 @@ def dict_merge():
|
||||
tainted_dict = {"key2": TAINTED_STRING}
|
||||
|
||||
merged = {**my_dict, **tainted_dict}
|
||||
ensure_tainted(merged)
|
||||
ensure_tainted(merged) # $ MISSING: tainted
|
||||
|
||||
|
||||
def set_add():
|
||||
@@ -227,7 +227,7 @@ def set_add():
|
||||
ensure_not_tainted(my_set)
|
||||
|
||||
my_set.add(tainted_string)
|
||||
ensure_tainted(my_set)
|
||||
ensure_tainted(my_set) # $ tainted
|
||||
|
||||
|
||||
# Make tests runable
|
||||
|
||||
@@ -23,16 +23,16 @@ def test():
|
||||
import json
|
||||
|
||||
ensure_tainted(
|
||||
json.dumps(ts),
|
||||
json.loads(json.dumps(ts)),
|
||||
json.dumps(ts), # $ tainted
|
||||
json.loads(json.dumps(ts)), # $ tainted
|
||||
)
|
||||
|
||||
# For Python2, need to convert to unicode for StringIO to work
|
||||
tainted_filelike = StringIO(unicode(json.dumps(ts)))
|
||||
|
||||
ensure_tainted(
|
||||
tainted_filelike,
|
||||
json.load(tainted_filelike),
|
||||
tainted_filelike, # $ MISSING: tainted
|
||||
json.load(tainted_filelike), # $ MISSING: tainted
|
||||
)
|
||||
|
||||
def non_syntacical():
|
||||
@@ -45,17 +45,17 @@ def non_syntacical():
|
||||
dumps_alias = dumps
|
||||
|
||||
ensure_tainted(
|
||||
dumps(ts),
|
||||
dumps_alias(ts),
|
||||
loads(dumps(ts)),
|
||||
dumps(ts), # $ tainted
|
||||
dumps_alias(ts), # $ tainted
|
||||
loads(dumps(ts)), # $ tainted
|
||||
)
|
||||
|
||||
# For Python2, need to convert to unicode for StringIO to work
|
||||
tainted_filelike = StringIO(unicode(dumps(ts)))
|
||||
|
||||
ensure_tainted(
|
||||
tainted_filelike,
|
||||
load(tainted_filelike),
|
||||
tainted_filelike, # $ MISSING: tainted
|
||||
load(tainted_filelike), # $ MISSING: tainted
|
||||
)
|
||||
|
||||
# Make tests runable
|
||||
|
||||
@@ -22,23 +22,23 @@ def str_operations():
|
||||
tb = TAINTED_BYTES
|
||||
|
||||
ensure_tainted(
|
||||
ts,
|
||||
ts + "foo",
|
||||
"foo" + ts,
|
||||
ts * 5,
|
||||
ts[0 : len(ts)],
|
||||
ts[:],
|
||||
ts[0:1000],
|
||||
ts[0],
|
||||
str(ts),
|
||||
bytes(tb),
|
||||
unicode(ts),
|
||||
ts, # $ tainted
|
||||
ts + "foo", # $ tainted
|
||||
"foo" + ts, # $ tainted
|
||||
ts * 5, # $ tainted
|
||||
ts[0 : len(ts)], # $ tainted
|
||||
ts[:], # $ tainted
|
||||
ts[0:1000], # $ tainted
|
||||
ts[0], # $ tainted
|
||||
str(ts), # $ tainted
|
||||
bytes(tb), # $ tainted
|
||||
unicode(ts), # $ tainted
|
||||
)
|
||||
|
||||
aug_assignment = "safe"
|
||||
ensure_not_tainted(aug_assignment)
|
||||
aug_assignment += TAINTED_STRING
|
||||
ensure_tainted(aug_assignment)
|
||||
ensure_tainted(aug_assignment) # $ tainted
|
||||
|
||||
|
||||
def str_methods():
|
||||
@@ -46,52 +46,54 @@ def str_methods():
|
||||
ts = TAINTED_STRING
|
||||
tb = TAINTED_BYTES
|
||||
ensure_tainted(
|
||||
ts.capitalize(),
|
||||
ts.center(100),
|
||||
ts.expandtabs(),
|
||||
ts.capitalize(), # $ tainted
|
||||
ts.center(100), # $ tainted
|
||||
ts.expandtabs(), # $ tainted
|
||||
|
||||
ts.format(),
|
||||
"{}".format(ts),
|
||||
"{unsafe}".format(unsafe=ts),
|
||||
ts.format(), # $ tainted
|
||||
"{}".format(ts), # $ tainted
|
||||
"{unsafe}".format(unsafe=ts), # $ tainted
|
||||
|
||||
ts.join(["", ""]),
|
||||
"".join([ts]),
|
||||
ts.join(["", ""]), # $ tainted
|
||||
"".join([ts]), # $ tainted
|
||||
|
||||
ts.ljust(100),
|
||||
ts.lstrip(),
|
||||
ts.lower(),
|
||||
ts.ljust(100), # $ tainted
|
||||
ts.lstrip(), # $ tainted
|
||||
ts.lower(), # $ tainted
|
||||
|
||||
ts.replace("old", "new"),
|
||||
"safe".replace("safe", ts),
|
||||
ts.replace("old", "new"), # $ tainted
|
||||
"safe".replace("safe", ts), # $ tainted
|
||||
|
||||
ts.rjust(100),
|
||||
ts.rstrip(),
|
||||
ts.strip(),
|
||||
ts.swapcase(),
|
||||
ts.title(),
|
||||
ts.upper(),
|
||||
ts.zfill(100),
|
||||
ts.rjust(100), # $ tainted
|
||||
ts.rstrip(), # $ tainted
|
||||
ts.strip(), # $ tainted
|
||||
ts.swapcase(), # $ tainted
|
||||
ts.title(), # $ tainted
|
||||
ts.upper(), # $ tainted
|
||||
ts.zfill(100), # $ tainted
|
||||
|
||||
ts.encode("utf-8"),
|
||||
ts.encode("utf-8").decode("utf-8"),
|
||||
ts.encode("utf-8"), # $ tainted
|
||||
ts.encode("utf-8").decode("utf-8"), # $ tainted
|
||||
|
||||
tb.decode("utf-8"),
|
||||
tb.decode("utf-8").encode("utf-8"),
|
||||
tb.decode("utf-8"), # $ tainted
|
||||
tb.decode("utf-8").encode("utf-8"), # $ tainted
|
||||
|
||||
# string methods that return a list
|
||||
ts.partition("_"),
|
||||
ts.rpartition("_"),
|
||||
ts.rsplit("_"),
|
||||
ts.split("_"),
|
||||
ts.splitlines(),
|
||||
ts.partition("_"), # $ tainted
|
||||
ts.rpartition("_"), # $ tainted
|
||||
ts.rsplit("_"), # $ tainted
|
||||
ts.split("_"), # $ tainted
|
||||
ts.splitlines(), # $ tainted
|
||||
)
|
||||
|
||||
ensure_not_tainted(
|
||||
# Intuitively I think this should be safe, but better discuss it
|
||||
"safe".replace(ts, "also-safe"),
|
||||
|
||||
ts.join([]), # FP due to separator not being used with zero/one elements
|
||||
ts.join(["safe"]), # FP due to separator not being used with zero/one elements
|
||||
# FPs due to separator (`ts`) not ending up in result, when the list only has
|
||||
# zero/one elements
|
||||
ts.join([]), # $ SPURIOUS: tainted
|
||||
ts.join(["safe"]), # $ SPURIOUS: tainted
|
||||
)
|
||||
|
||||
|
||||
@@ -101,8 +103,8 @@ def non_syntactic():
|
||||
meth = ts.upper
|
||||
_str = str
|
||||
ensure_tainted(
|
||||
meth(),
|
||||
_str(ts),
|
||||
meth(), # $ MISSING: tainted
|
||||
_str(ts), # $ MISSING: tainted
|
||||
)
|
||||
|
||||
|
||||
@@ -111,9 +113,9 @@ def percent_fmt():
|
||||
ts = TAINTED_STRING
|
||||
tainted_fmt = ts + " %s %s"
|
||||
ensure_tainted(
|
||||
tainted_fmt % (1, 2),
|
||||
"%s foo bar" % ts,
|
||||
"%s %s %s" % (1, 2, ts),
|
||||
tainted_fmt % (1, 2), # $ tainted
|
||||
"%s foo bar" % ts, # $ tainted
|
||||
"%s %s %s" % (1, 2, ts), # $ tainted
|
||||
)
|
||||
|
||||
|
||||
@@ -123,30 +125,30 @@ def binary_decode_encode():
|
||||
import base64
|
||||
|
||||
ensure_tainted(
|
||||
base64.b64encode(tb),
|
||||
base64.b64decode(base64.b64encode(tb)),
|
||||
base64.b64encode(tb), # $ tainted
|
||||
base64.b64decode(base64.b64encode(tb)), # $ tainted
|
||||
|
||||
base64.standard_b64encode(tb),
|
||||
base64.standard_b64decode(base64.standard_b64encode(tb)),
|
||||
base64.standard_b64encode(tb), # $ tainted
|
||||
base64.standard_b64decode(base64.standard_b64encode(tb)), # $ tainted
|
||||
|
||||
base64.urlsafe_b64encode(tb),
|
||||
base64.urlsafe_b64decode(base64.urlsafe_b64encode(tb)),
|
||||
base64.urlsafe_b64encode(tb), # $ tainted
|
||||
base64.urlsafe_b64decode(base64.urlsafe_b64encode(tb)), # $ tainted
|
||||
|
||||
base64.b32encode(tb),
|
||||
base64.b32decode(base64.b32encode(tb)),
|
||||
base64.b32encode(tb), # $ tainted
|
||||
base64.b32decode(base64.b32encode(tb)), # $ tainted
|
||||
|
||||
base64.b16encode(tb),
|
||||
base64.b16decode(base64.b16encode(tb)),
|
||||
base64.b16encode(tb), # $ tainted
|
||||
base64.b16decode(base64.b16encode(tb)), # $ tainted
|
||||
|
||||
# deprecated since Python 3.1, but still works
|
||||
base64.encodestring(tb),
|
||||
base64.decodestring(base64.encodestring(tb)),
|
||||
base64.encodestring(tb), # $ tainted
|
||||
base64.decodestring(base64.encodestring(tb)), # $ tainted
|
||||
)
|
||||
|
||||
import quopri
|
||||
ensure_tainted(
|
||||
quopri.encodestring(tb),
|
||||
quopri.decodestring(quopri.encodestring(tb)),
|
||||
quopri.encodestring(tb), # $ MISSING: tainted
|
||||
quopri.decodestring(quopri.encodestring(tb)), # $ MISSING: tainted
|
||||
)
|
||||
|
||||
|
||||
@@ -156,10 +158,10 @@ def test_os_path_join():
|
||||
print("\n# test_os_path_join")
|
||||
ts = TAINTED_STRING
|
||||
ensure_tainted(
|
||||
os.path.join(ts, "foo", "bar"),
|
||||
os.path.join(ts),
|
||||
os.path.join("foo", "bar", ts),
|
||||
ospath_alias.join("foo", "bar", ts),
|
||||
os.path.join(ts, "foo", "bar"), # $ tainted
|
||||
os.path.join(ts), # $ tainted
|
||||
os.path.join("foo", "bar", ts), # $ tainted
|
||||
ospath_alias.join("foo", "bar", ts), # $ tainted
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -13,13 +13,13 @@ if TYPE_CHECKING:
|
||||
def unpacking():
|
||||
l = TAINTED_LIST[0:3]
|
||||
a, b, c = l
|
||||
ensure_tainted(a, b, c)
|
||||
ensure_tainted(a, b, c) # $ tainted
|
||||
|
||||
|
||||
def unpacking_to_list():
|
||||
l = TAINTED_LIST[0:3]
|
||||
[a, b, c] = l
|
||||
ensure_tainted(a, b, c)
|
||||
ensure_tainted(a, b, c) # $ tainted
|
||||
|
||||
|
||||
def nested():
|
||||
@@ -28,22 +28,22 @@ def nested():
|
||||
|
||||
# list
|
||||
[[a1, a2, a3], b, c] = ll
|
||||
ensure_tainted(a1, a2, a3, b, c)
|
||||
ensure_tainted(a1, a2, a3, b, c) # $ tainted
|
||||
|
||||
# tuple
|
||||
((a1, a2, a3), b, c) = ll
|
||||
ensure_tainted(a1, a2, a3, b, c)
|
||||
ensure_tainted(a1, a2, a3, b, c) # $ tainted
|
||||
|
||||
# mixed
|
||||
[(a1, a2, a3), b, c] = ll
|
||||
ensure_tainted(a1, a2, a3, b, c)
|
||||
ensure_tainted(a1, a2, a3, b, c) # $ tainted
|
||||
|
||||
|
||||
def unpack_from_set():
|
||||
# no guarantee on ordering ... don't know why you would ever do this
|
||||
a, b, c = {"foo", "bar", TAINTED_STRING}
|
||||
# either all should be tainted, or none of them
|
||||
ensure_tainted(a, b, c)
|
||||
ensure_tainted(a, b, c) # $ tainted
|
||||
|
||||
|
||||
def contrived_1():
|
||||
@@ -52,8 +52,8 @@ def contrived_1():
|
||||
no_taint_list = [1,2,3]
|
||||
|
||||
(a, b, c), (d, e, f) = tainted_list, no_taint_list
|
||||
ensure_tainted(a, b, c)
|
||||
ensure_not_tainted(d, e, f) # FP: we mark `d`, `e` and `f` as tainted.
|
||||
ensure_tainted(a, b, c) # $ tainted
|
||||
ensure_not_tainted(d, e, f) # $ SPURIOUS: tainted
|
||||
|
||||
|
||||
def contrived_2():
|
||||
@@ -62,7 +62,7 @@ def contrived_2():
|
||||
# Old taint tracking was only able to handle taint nested 2 levels in sequences,
|
||||
# so would not mark a, b, c as tainted
|
||||
[[[ (a, b, c) ]]] = [[[ TAINTED_LIST[0:3] ]]]
|
||||
ensure_tainted(a, b, c)
|
||||
ensure_tainted(a, b, c) # $ tainted
|
||||
|
||||
|
||||
# Make tests runable
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
argumentToEnsureNotTaintedNotMarkedAsSpurious
|
||||
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
|
||||
failures
|
||||
@@ -0,0 +1 @@
|
||||
import experimental.meta.InlineTaintTest
|
||||
@@ -25,14 +25,14 @@ initially_tainted = NOT_TAINTED
|
||||
ensure_not_tainted(initially_tainted)
|
||||
|
||||
def use_of_initially_tainted():
|
||||
ensure_not_tainted(initially_tainted) # FP
|
||||
ensure_not_tainted(initially_tainted) # $ SPURIOUS: tainted
|
||||
|
||||
|
||||
# A very similar case to the above, but here we _do_ want taint flow, because the initially tainted
|
||||
# value is actually used before it gets reassigned to an untainted value.
|
||||
# value is actually used before it gets reassigned to an untainted value.
|
||||
|
||||
def use_of_initially_tainted2():
|
||||
ensure_tainted(initially_tainted2)
|
||||
ensure_tainted(initially_tainted2) # $ tainted
|
||||
|
||||
initially_tainted2 = TAINTED_STRING
|
||||
use_of_initially_tainted2()
|
||||
@@ -47,7 +47,7 @@ def write_tainted():
|
||||
g = TAINTED_STRING
|
||||
|
||||
def sink_global():
|
||||
ensure_tainted(g)
|
||||
ensure_tainted(g) # $ tainted
|
||||
|
||||
write_global()
|
||||
write_tainted()
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
| test.py:12 | ok | test | tainted_later |
|
||||
| test.py:25 | ok | test | initially_tainted |
|
||||
| test.py:28 | fail | use_of_initially_tainted | initially_tainted |
|
||||
| test.py:35 | ok | use_of_initially_tainted2 | initially_tainted2 |
|
||||
| test.py:40 | ok | test | initially_tainted2 |
|
||||
| test.py:50 | ok | sink_global | g |
|
||||
@@ -1 +0,0 @@
|
||||
import experimental.dataflow.tainttracking.TestTaintLib
|
||||
@@ -1,6 +1,7 @@
|
||||
module_tracker
|
||||
| import_as_attr.py:1:6:1:11 | ControlFlowNode for ImportExpr |
|
||||
module_attr_tracker
|
||||
| import_as_attr.py:0:0:0:0 | ModuleVariableNode for Global Variable attr_ref in Module import_as_attr |
|
||||
| import_as_attr.py:1:20:1:35 | ControlFlowNode for ImportMember |
|
||||
| import_as_attr.py:1:28:1:35 | GSSA Variable attr_ref |
|
||||
| import_as_attr.py:3:1:3:1 | GSSA Variable x |
|
||||
|
||||
94
python/ql/test/experimental/meta/InlineTaintTest.qll
Normal file
94
python/ql/test/experimental/meta/InlineTaintTest.qll
Normal file
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* Defines a InlineExpectationsTest for checking whether any arguments in
|
||||
* `ensure_tainted` and `ensure_not_tainted` calls are tainted.
|
||||
*
|
||||
* Also defines query predicates to ensure that:
|
||||
* - if any arguments to `ensure_not_tainted` are tainted, their annotation is marked with `SPURIOUS`.
|
||||
* - if any arguments to `ensure_tainted` are not tainted, their annotation is marked with `MISSING`.
|
||||
*
|
||||
* The functionality of this module is tested in `ql/test/experimental/meta/inline-taint-test-demo`.
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
import semmle.python.dataflow.new.RemoteFlowSources
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import experimental.dataflow.TestUtil.PrintNode
|
||||
|
||||
DataFlow::Node shouldBeTainted() {
|
||||
exists(DataFlow::CallCfgNode call |
|
||||
call.getFunction().asCfgNode().(NameNode).getId() = "ensure_tainted" and
|
||||
result in [call.getArg(_), call.getArgByName(_)]
|
||||
)
|
||||
}
|
||||
|
||||
DataFlow::Node shouldNotBeTainted() {
|
||||
exists(DataFlow::CallCfgNode call |
|
||||
call.getFunction().asCfgNode().(NameNode).getId() = "ensure_not_tainted" and
|
||||
result in [call.getArg(_), call.getArgByName(_)]
|
||||
)
|
||||
}
|
||||
|
||||
class TestTaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TestTaintTrackingConfiguration() { this = "TestTaintTrackingConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source.asCfgNode().(NameNode).getId() in [
|
||||
"TAINTED_STRING", "TAINTED_BYTES", "TAINTED_LIST", "TAINTED_DICT"
|
||||
]
|
||||
or
|
||||
source instanceof RemoteFlowSource
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink = shouldBeTainted()
|
||||
or
|
||||
sink = shouldNotBeTainted()
|
||||
}
|
||||
}
|
||||
|
||||
class InlineTaintTest extends InlineExpectationsTest {
|
||||
InlineTaintTest() { this = "InlineTaintTest" }
|
||||
|
||||
override string getARelevantTag() { result = "tainted" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(location.getFile().getRelativePath()) and
|
||||
exists(DataFlow::Node sink |
|
||||
any(TestTaintTrackingConfiguration config).hasFlow(_, sink) and
|
||||
location = sink.getLocation() and
|
||||
element = prettyExp(sink.asExpr()) and
|
||||
value = "" and
|
||||
tag = "tainted"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
query predicate argumentToEnsureNotTaintedNotMarkedAsSpurious(
|
||||
Location location, string error, string element
|
||||
) {
|
||||
error = "ERROR, you should add `SPURIOUS:` to this annotation" and
|
||||
location = shouldNotBeTainted().getLocation() and
|
||||
any(InlineTaintTest test).hasActualResult(location, element, "tainted", _) and
|
||||
exists(GoodExpectation good, ActualResult actualResult |
|
||||
good.matchesActualResult(actualResult) and
|
||||
actualResult.getLocation() = location and
|
||||
actualResult.toString() = element
|
||||
)
|
||||
}
|
||||
|
||||
query predicate untaintedArgumentToEnsureTaintedNotMarkedAsMissing(
|
||||
Location location, string error, string element
|
||||
) {
|
||||
error = "ERROR, you should add `# $ MISSING: tainted` annotation" and
|
||||
exists(DataFlow::Node sink |
|
||||
sink = shouldBeTainted() and
|
||||
element = prettyExp(sink.asExpr()) and
|
||||
not any(TestTaintTrackingConfiguration config).hasFlow(_, sink) and
|
||||
location = sink.getLocation() and
|
||||
not exists(FalseNegativeExpectation missingResult |
|
||||
missingResult.getLocation().getStartLine() = location.getStartLine()
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
argumentToEnsureNotTaintedNotMarkedAsSpurious
|
||||
| taint_test.py:48:9:48:29 | taint_test.py:48 | ERROR, you should add `SPURIOUS:` to this annotation | should_not_be_tainted |
|
||||
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
|
||||
| taint_test.py:32:9:32:25 | taint_test.py:32 | ERROR, you should add `# $ MISSING: tainted` annotation | should_be_tainted |
|
||||
| taint_test.py:37:24:37:40 | taint_test.py:37 | ERROR, you should add `# $ MISSING: tainted` annotation | should_be_tainted |
|
||||
failures
|
||||
| taint_test.py:41:20:41:21 | ts | Fixed missing result:tainted= |
|
||||
@@ -0,0 +1 @@
|
||||
import experimental.meta.InlineTaintTest
|
||||
@@ -0,0 +1,49 @@
|
||||
def expected_usage():
|
||||
ts = TAINTED_STRING
|
||||
|
||||
# simulating handling something we _want_ to treat at tainted, but we currently treat as untainted
|
||||
should_be_tainted = "pretend this is unsafe"
|
||||
|
||||
ensure_tainted(
|
||||
ts, # $ tainted
|
||||
should_be_tainted, # $ MISSING: tainted
|
||||
)
|
||||
|
||||
# having one annotation for multiple arguments is OK, as long as all arguments
|
||||
# fulfil the same annotation
|
||||
ensure_tainted(ts, ts) # $ tainted
|
||||
|
||||
# simulating handling something we _want_ to treat at untainted, but we currently treat as tainted
|
||||
should_not_be_tainted = "pretend this is now safe" + ts
|
||||
ensure_not_tainted(
|
||||
should_not_be_tainted, # $ SPURIOUS: tainted
|
||||
"FOO"
|
||||
)
|
||||
|
||||
|
||||
def bad_usage():
|
||||
ts = TAINTED_STRING
|
||||
|
||||
# simulating handling something we _want_ to treat at tainted, but we currently treat as untainted
|
||||
should_be_tainted = "pretend this is unsafe"
|
||||
|
||||
# This element _should_ have a `$ MISSING: tainted` annotation, which will be alerted in the .expected output
|
||||
ensure_tainted(
|
||||
should_be_tainted,
|
||||
)
|
||||
|
||||
# using one annotation for multiple arguments i not OK when it's mixed whether our
|
||||
# taint-tracking works as expected
|
||||
ensure_tainted(ts, should_be_tainted) # $ tainted
|
||||
|
||||
# if you try to get around it by adding BOTH annotations, that results in a problem
|
||||
# from the default set of inline-test-expectation rules
|
||||
ensure_tainted(ts, should_be_tainted) # $ tainted MISSING: tainted
|
||||
|
||||
# simulating handling something we _want_ to treat at untainted, but we currently treat as tainted
|
||||
should_not_be_tainted = "pretend this is now safe" + ts
|
||||
|
||||
# This annotation _should_ have used `SPURIOUS`, which will be alerted on in the .expected output
|
||||
ensure_not_tainted(
|
||||
should_not_be_tainted # $ tainted
|
||||
)
|
||||
@@ -0,0 +1,3 @@
|
||||
argumentToEnsureNotTaintedNotMarkedAsSpurious
|
||||
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
|
||||
failures
|
||||
@@ -0,0 +1 @@
|
||||
import experimental.meta.InlineTaintTest
|
||||
@@ -1,100 +0,0 @@
|
||||
| response_test.py:61 | ok | get_redirect_url | foo |
|
||||
| taint_forms.py:6 | ok | to_python | value |
|
||||
| taint_forms.py:9 | ok | validate | value |
|
||||
| taint_forms.py:12 | ok | run_validators | value |
|
||||
| taint_forms.py:15 | ok | clean | value |
|
||||
| taint_forms.py:33 | ok | clean | cleaned_data |
|
||||
| taint_forms.py:34 | ok | clean | cleaned_data["key"] |
|
||||
| taint_forms.py:35 | ok | clean | cleaned_data.get(..) |
|
||||
| taint_forms.py:39 | ok | clean | self.cleaned_data |
|
||||
| taint_forms.py:40 | ok | clean | self.cleaned_data["key"] |
|
||||
| taint_forms.py:41 | ok | clean | self.cleaned_data.get(..) |
|
||||
| taint_forms.py:46 | ok | clean_foo | self.cleaned_data |
|
||||
| taint_test.py:8 | ok | test_taint | bar |
|
||||
| taint_test.py:8 | ok | test_taint | foo |
|
||||
| taint_test.py:9 | ok | test_taint | baz |
|
||||
| taint_test.py:15 | ok | test_taint | request |
|
||||
| taint_test.py:17 | ok | test_taint | request.body |
|
||||
| taint_test.py:18 | ok | test_taint | request.path |
|
||||
| taint_test.py:19 | ok | test_taint | request.path_info |
|
||||
| taint_test.py:23 | ok | test_taint | request.method |
|
||||
| taint_test.py:25 | ok | test_taint | request.encoding |
|
||||
| taint_test.py:26 | ok | test_taint | request.content_type |
|
||||
| taint_test.py:29 | ok | test_taint | request.content_params |
|
||||
| taint_test.py:30 | ok | test_taint | request.content_params["key"] |
|
||||
| taint_test.py:31 | ok | test_taint | request.content_params.get(..) |
|
||||
| taint_test.py:35 | ok | test_taint | request.GET |
|
||||
| taint_test.py:36 | ok | test_taint | request.GET["key"] |
|
||||
| taint_test.py:37 | ok | test_taint | request.GET.get(..) |
|
||||
| taint_test.py:38 | fail | test_taint | request.GET.getlist(..) |
|
||||
| taint_test.py:39 | fail | test_taint | request.GET.getlist(..)[0] |
|
||||
| taint_test.py:40 | ok | test_taint | request.GET.pop(..) |
|
||||
| taint_test.py:41 | ok | test_taint | request.GET.pop(..)[0] |
|
||||
| taint_test.py:42 | ok | test_taint | request.GET.popitem()[0] |
|
||||
| taint_test.py:43 | ok | test_taint | request.GET.popitem()[1] |
|
||||
| taint_test.py:44 | ok | test_taint | request.GET.popitem()[1][0] |
|
||||
| taint_test.py:45 | fail | test_taint | request.GET.dict() |
|
||||
| taint_test.py:46 | fail | test_taint | request.GET.dict()["key"] |
|
||||
| taint_test.py:47 | fail | test_taint | request.GET.urlencode() |
|
||||
| taint_test.py:50 | ok | test_taint | request.POST |
|
||||
| taint_test.py:53 | ok | test_taint | request.COOKIES |
|
||||
| taint_test.py:54 | ok | test_taint | request.COOKIES["key"] |
|
||||
| taint_test.py:55 | ok | test_taint | request.COOKIES.get(..) |
|
||||
| taint_test.py:58 | ok | test_taint | request.FILES |
|
||||
| taint_test.py:59 | ok | test_taint | request.FILES["key"] |
|
||||
| taint_test.py:60 | fail | test_taint | request.FILES["key"].content_type |
|
||||
| taint_test.py:61 | fail | test_taint | request.FILES["key"].content_type_extra |
|
||||
| taint_test.py:62 | fail | test_taint | request.FILES["key"].content_type_extra["key"] |
|
||||
| taint_test.py:63 | fail | test_taint | request.FILES["key"].charset |
|
||||
| taint_test.py:64 | fail | test_taint | request.FILES["key"].name |
|
||||
| taint_test.py:65 | fail | test_taint | request.FILES["key"].file |
|
||||
| taint_test.py:66 | fail | test_taint | request.FILES["key"].file.read() |
|
||||
| taint_test.py:68 | ok | test_taint | request.FILES.get(..) |
|
||||
| taint_test.py:69 | fail | test_taint | request.FILES.get(..).name |
|
||||
| taint_test.py:70 | fail | test_taint | request.FILES.getlist(..) |
|
||||
| taint_test.py:71 | fail | test_taint | request.FILES.getlist(..)[0] |
|
||||
| taint_test.py:72 | fail | test_taint | request.FILES.getlist(..)[0].name |
|
||||
| taint_test.py:73 | fail | test_taint | request.FILES.dict() |
|
||||
| taint_test.py:74 | fail | test_taint | request.FILES.dict()["key"] |
|
||||
| taint_test.py:75 | fail | test_taint | request.FILES.dict()["key"].name |
|
||||
| taint_test.py:78 | ok | test_taint | request.META |
|
||||
| taint_test.py:79 | ok | test_taint | request.META["HTTP_USER_AGENT"] |
|
||||
| taint_test.py:80 | ok | test_taint | request.META.get(..) |
|
||||
| taint_test.py:83 | ok | test_taint | request.headers |
|
||||
| taint_test.py:84 | ok | test_taint | request.headers["user-agent"] |
|
||||
| taint_test.py:85 | ok | test_taint | request.headers["USER_AGENT"] |
|
||||
| taint_test.py:88 | ok | test_taint | request.resolver_match |
|
||||
| taint_test.py:89 | fail | test_taint | request.resolver_match.args |
|
||||
| taint_test.py:90 | fail | test_taint | request.resolver_match.args[0] |
|
||||
| taint_test.py:91 | fail | test_taint | request.resolver_match.kwargs |
|
||||
| taint_test.py:92 | fail | test_taint | request.resolver_match.kwargs["key"] |
|
||||
| taint_test.py:94 | fail | test_taint | request.get_full_path() |
|
||||
| taint_test.py:95 | fail | test_taint | request.get_full_path_info() |
|
||||
| taint_test.py:99 | fail | test_taint | request.read() |
|
||||
| taint_test.py:100 | fail | test_taint | request.readline() |
|
||||
| taint_test.py:101 | fail | test_taint | request.readlines() |
|
||||
| taint_test.py:102 | fail | test_taint | request.readlines()[0] |
|
||||
| taint_test.py:103 | fail | test_taint | ListComp |
|
||||
| taint_test.py:109 | ok | test_taint | args |
|
||||
| taint_test.py:110 | ok | test_taint | args[0] |
|
||||
| taint_test.py:111 | ok | test_taint | kwargs |
|
||||
| taint_test.py:112 | ok | test_taint | kwargs["key"] |
|
||||
| taint_test.py:116 | ok | test_taint | request.current_app |
|
||||
| taint_test.py:121 | ok | test_taint | request.get_host() |
|
||||
| taint_test.py:122 | ok | test_taint | request.get_port() |
|
||||
| taint_test.py:129 | fail | test_taint | request.build_absolute_uri() |
|
||||
| taint_test.py:130 | fail | test_taint | request.build_absolute_uri(..) |
|
||||
| taint_test.py:131 | fail | test_taint | request.build_absolute_uri(..) |
|
||||
| taint_test.py:134 | ok | test_taint | request.build_absolute_uri(..) |
|
||||
| taint_test.py:135 | ok | test_taint | request.build_absolute_uri(..) |
|
||||
| taint_test.py:143 | ok | test_taint | request.get_signed_cookie(..) |
|
||||
| taint_test.py:144 | ok | test_taint | request.get_signed_cookie(..) |
|
||||
| taint_test.py:145 | ok | test_taint | request.get_signed_cookie(..) |
|
||||
| taint_test.py:149 | fail | test_taint | request.get_signed_cookie(..) |
|
||||
| taint_test.py:150 | fail | test_taint | request.get_signed_cookie(..) |
|
||||
| taint_test.py:157 | ok | some_method | self.request |
|
||||
| taint_test.py:158 | ok | some_method | self.request.GET["key"] |
|
||||
| taint_test.py:160 | ok | some_method | self.args |
|
||||
| taint_test.py:161 | ok | some_method | self.args[0] |
|
||||
| taint_test.py:163 | ok | some_method | self.kwargs |
|
||||
| taint_test.py:164 | ok | some_method | self.kwargs["key"] |
|
||||
@@ -1,6 +0,0 @@
|
||||
import experimental.dataflow.tainttracking.TestTaintLib
|
||||
import semmle.python.dataflow.new.RemoteFlowSources
|
||||
|
||||
class RemoteFlowTestTaintConfiguration extends TestTaintTrackingConfiguration {
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
}
|
||||
@@ -67,7 +67,7 @@ def redirect_shortcut(request):
|
||||
class CustomRedirectView(RedirectView):
|
||||
|
||||
def get_redirect_url(self, foo): # $ requestHandler routedParameter=foo
|
||||
ensure_tainted(foo)
|
||||
ensure_tainted(foo) # $ tainted
|
||||
next = "https://example.com/{}".format(foo)
|
||||
return next # $ HttpResponse HttpRedirectResponse redirectLocation=next
|
||||
|
||||
|
||||
@@ -3,16 +3,16 @@ import django.forms
|
||||
|
||||
class MyField(django.forms.Field):
|
||||
def to_python(self, value):
|
||||
ensure_tainted(value)
|
||||
ensure_tainted(value) # $ tainted
|
||||
|
||||
def validate(self, value):
|
||||
ensure_tainted(value)
|
||||
ensure_tainted(value) # $ tainted
|
||||
|
||||
def run_validators(self, value):
|
||||
ensure_tainted(value)
|
||||
ensure_tainted(value) # $ tainted
|
||||
|
||||
def clean(self, value):
|
||||
ensure_tainted(value)
|
||||
ensure_tainted(value) # $ tainted
|
||||
|
||||
# # Base definition of `clean` looks like the following, so there is actually
|
||||
# # _data flow_ from the methods, but we will ignore for simplicity.
|
||||
@@ -30,17 +30,17 @@ class MyForm(django.forms.Form):
|
||||
cleaned_data = super().clean()
|
||||
|
||||
ensure_tainted(
|
||||
cleaned_data,
|
||||
cleaned_data["key"],
|
||||
cleaned_data.get("key"),
|
||||
cleaned_data, # $ tainted
|
||||
cleaned_data["key"], # $ tainted
|
||||
cleaned_data.get("key"), # $ tainted
|
||||
)
|
||||
|
||||
ensure_tainted(
|
||||
self.cleaned_data,
|
||||
self.cleaned_data["key"],
|
||||
self.cleaned_data.get("key"),
|
||||
self.cleaned_data, # $ tainted
|
||||
self.cleaned_data["key"], # $ tainted
|
||||
self.cleaned_data.get("key"), # $ tainted
|
||||
)
|
||||
|
||||
def clean_foo(self):
|
||||
# This method is supposed to clean the `foo` field in context of this form.
|
||||
ensure_tainted(self.cleaned_data)
|
||||
ensure_tainted(self.cleaned_data) # $ tainted
|
||||
|
||||
@@ -5,111 +5,114 @@ from django.views import View
|
||||
|
||||
|
||||
def test_taint(request: HttpRequest, foo, bar, baz=None): # $requestHandler routedParameter=foo routedParameter=bar
|
||||
ensure_tainted(foo, bar)
|
||||
ensure_tainted(foo, bar) # $ tainted
|
||||
ensure_not_tainted(baz)
|
||||
|
||||
# Manually inspected all fields of the HttpRequest object
|
||||
# https://docs.djangoproject.com/en/3.0/ref/request-response/#httprequest-objects
|
||||
|
||||
ensure_tainted(
|
||||
request,
|
||||
request, # $ tainted
|
||||
|
||||
request.body,
|
||||
request.path,
|
||||
request.path_info,
|
||||
request.body, # $ tainted
|
||||
request.path, # $ tainted
|
||||
request.path_info, # $ tainted
|
||||
|
||||
# With CSRF middleware disabled, it's possible to use custom methods,
|
||||
# for example by `curl -X FOO <url>`
|
||||
request.method,
|
||||
request.method, # $ tainted
|
||||
|
||||
request.encoding,
|
||||
request.content_type,
|
||||
request.encoding, # $ tainted
|
||||
request.content_type, # $ tainted
|
||||
|
||||
# Dict[str, str]
|
||||
request.content_params,
|
||||
request.content_params["key"],
|
||||
request.content_params.get("key"),
|
||||
request.content_params, # $ tainted
|
||||
request.content_params["key"], # $ tainted
|
||||
request.content_params.get("key"), # $ tainted
|
||||
|
||||
# django.http.QueryDict
|
||||
# see https://docs.djangoproject.com/en/3.0/ref/request-response/#querydict-objects
|
||||
request.GET,
|
||||
request.GET["key"],
|
||||
request.GET.get("key"),
|
||||
request.GET.getlist("key"),
|
||||
request.GET.getlist("key")[0],
|
||||
request.GET.pop("key"),
|
||||
request.GET.pop("key")[0],
|
||||
request.GET.popitem()[0], # key
|
||||
request.GET.popitem()[1], # values
|
||||
request.GET.popitem()[1][0], # values[0]
|
||||
request.GET.dict(),
|
||||
request.GET.dict()["key"],
|
||||
request.GET.urlencode(),
|
||||
request.GET, # $ tainted
|
||||
request.GET["key"], # $ tainted
|
||||
request.GET.get("key"), # $ tainted
|
||||
request.GET.getlist("key"), # $ MISSING: tainted
|
||||
request.GET.getlist("key")[0], # $ MISSING: tainted
|
||||
request.GET.pop("key"), # $ tainted
|
||||
request.GET.pop("key")[0], # $ tainted
|
||||
# key
|
||||
request.GET.popitem()[0], # $ tainted
|
||||
# values
|
||||
request.GET.popitem()[1], # $ tainted
|
||||
# values[0]
|
||||
request.GET.popitem()[1][0], # $ tainted
|
||||
request.GET.dict(), # $ MISSING: tainted
|
||||
request.GET.dict()["key"], # $ MISSING: tainted
|
||||
request.GET.urlencode(), # $ MISSING: tainted
|
||||
|
||||
# django.http.QueryDict (same as above, did not duplicate tests)
|
||||
request.POST,
|
||||
request.POST, # $ tainted
|
||||
|
||||
# Dict[str, str]
|
||||
request.COOKIES,
|
||||
request.COOKIES["key"],
|
||||
request.COOKIES.get("key"),
|
||||
request.COOKIES, # $ tainted
|
||||
request.COOKIES["key"], # $ tainted
|
||||
request.COOKIES.get("key"), # $ tainted
|
||||
|
||||
# MultiValueDict[str, UploadedFile]
|
||||
request.FILES,
|
||||
request.FILES["key"],
|
||||
request.FILES["key"].content_type,
|
||||
request.FILES["key"].content_type_extra,
|
||||
request.FILES["key"].content_type_extra["key"],
|
||||
request.FILES["key"].charset,
|
||||
request.FILES["key"].name,
|
||||
request.FILES["key"].file,
|
||||
request.FILES["key"].file.read(),
|
||||
request.FILES, # $ tainted
|
||||
request.FILES["key"], # $ tainted
|
||||
request.FILES["key"].content_type, # $ MISSING: tainted
|
||||
request.FILES["key"].content_type_extra, # $ MISSING: tainted
|
||||
request.FILES["key"].content_type_extra["key"], # $ MISSING: tainted
|
||||
request.FILES["key"].charset, # $ MISSING: tainted
|
||||
request.FILES["key"].name, # $ MISSING: tainted
|
||||
request.FILES["key"].file, # $ MISSING: tainted
|
||||
request.FILES["key"].file.read(), # $ MISSING: tainted
|
||||
|
||||
request.FILES.get("key"),
|
||||
request.FILES.get("key").name,
|
||||
request.FILES.getlist("key"),
|
||||
request.FILES.getlist("key")[0],
|
||||
request.FILES.getlist("key")[0].name,
|
||||
request.FILES.dict(),
|
||||
request.FILES.dict()["key"],
|
||||
request.FILES.dict()["key"].name,
|
||||
request.FILES.get("key"), # $ tainted
|
||||
request.FILES.get("key").name, # $ MISSING: tainted
|
||||
request.FILES.getlist("key"), # $ MISSING: tainted
|
||||
request.FILES.getlist("key")[0], # $ MISSING: tainted
|
||||
request.FILES.getlist("key")[0].name, # $ MISSING: tainted
|
||||
request.FILES.dict(), # $ MISSING: tainted
|
||||
request.FILES.dict()["key"], # $ MISSING: tainted
|
||||
request.FILES.dict()["key"].name, # $ MISSING: tainted
|
||||
|
||||
# Dict[str, Any]
|
||||
request.META,
|
||||
request.META["HTTP_USER_AGENT"],
|
||||
request.META.get("HTTP_USER_AGENT"),
|
||||
request.META, # $ tainted
|
||||
request.META["HTTP_USER_AGENT"], # $ tainted
|
||||
request.META.get("HTTP_USER_AGENT"), # $ tainted
|
||||
|
||||
# HttpHeaders (case insensitive dict-like)
|
||||
request.headers,
|
||||
request.headers["user-agent"],
|
||||
request.headers["USER_AGENT"],
|
||||
request.headers, # $ tainted
|
||||
request.headers["user-agent"], # $ tainted
|
||||
request.headers["USER_AGENT"], # $ tainted
|
||||
|
||||
# django.urls.ResolverMatch
|
||||
request.resolver_match,
|
||||
request.resolver_match.args,
|
||||
request.resolver_match.args[0],
|
||||
request.resolver_match.kwargs,
|
||||
request.resolver_match.kwargs["key"],
|
||||
request.resolver_match, # $ tainted
|
||||
request.resolver_match.args, # $ MISSING: tainted
|
||||
request.resolver_match.args[0], # $ MISSING: tainted
|
||||
request.resolver_match.kwargs, # $ MISSING: tainted
|
||||
request.resolver_match.kwargs["key"], # $ MISSING: tainted
|
||||
|
||||
request.get_full_path(),
|
||||
request.get_full_path_info(),
|
||||
request.get_full_path(), # $ MISSING: tainted
|
||||
request.get_full_path_info(), # $ MISSING: tainted
|
||||
# build_absolute_uri handled below
|
||||
# get_signed_cookie handled below
|
||||
|
||||
request.read(),
|
||||
request.readline(),
|
||||
request.readlines(),
|
||||
request.readlines()[0],
|
||||
[line for line in request],
|
||||
request.read(), # $ MISSING: tainted
|
||||
request.readline(), # $ MISSING: tainted
|
||||
request.readlines(), # $ MISSING: tainted
|
||||
request.readlines()[0], # $ MISSING: tainted
|
||||
[line for line in request], # $ MISSING: tainted
|
||||
)
|
||||
|
||||
# django.urls.ResolverMatch also supports iterable unpacking
|
||||
_view, args, kwargs = request.resolver_match
|
||||
ensure_tainted(
|
||||
args,
|
||||
args[0],
|
||||
kwargs,
|
||||
kwargs["key"],
|
||||
args, # $ tainted
|
||||
args[0], # $ tainted
|
||||
kwargs, # $ tainted
|
||||
kwargs["key"], # $ tainted
|
||||
)
|
||||
|
||||
ensure_not_tainted(
|
||||
@@ -126,9 +129,9 @@ def test_taint(request: HttpRequest, foo, bar, baz=None): # $requestHandler rou
|
||||
# build_absolute_uri
|
||||
####################################
|
||||
ensure_tainted(
|
||||
request.build_absolute_uri(),
|
||||
request.build_absolute_uri(request.GET["key"]),
|
||||
request.build_absolute_uri(location=request.GET["key"]),
|
||||
request.build_absolute_uri(), # $ MISSING: tainted
|
||||
request.build_absolute_uri(request.GET["key"]), # $ MISSING: tainted
|
||||
request.build_absolute_uri(location=request.GET["key"]), # $ MISSING: tainted
|
||||
)
|
||||
ensure_not_tainted(
|
||||
request.build_absolute_uri("/hardcoded/"),
|
||||
@@ -146,22 +149,22 @@ def test_taint(request: HttpRequest, foo, bar, baz=None): # $requestHandler rou
|
||||
)
|
||||
# However, providing tainted default value might result in taint
|
||||
ensure_tainted(
|
||||
request.get_signed_cookie("key", request.COOKIES["key"]),
|
||||
request.get_signed_cookie("key", default=request.COOKIES["key"]),
|
||||
request.get_signed_cookie("key", request.COOKIES["key"]), # $ MISSING: tainted
|
||||
request.get_signed_cookie("key", default=request.COOKIES["key"]), # $ MISSING: tainted
|
||||
)
|
||||
|
||||
|
||||
class ClassView(View):
|
||||
def some_method(self):
|
||||
ensure_tainted(
|
||||
self.request,
|
||||
self.request.GET["key"],
|
||||
self.request, # $ tainted
|
||||
self.request.GET["key"], # $ tainted
|
||||
|
||||
self.args,
|
||||
self.args[0],
|
||||
self.args, # $ tainted
|
||||
self.args[0], # $ tainted
|
||||
|
||||
self.kwargs,
|
||||
self.kwargs["key"],
|
||||
self.kwargs, # $ tainted
|
||||
self.kwargs["key"], # $ tainted
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
argumentToEnsureNotTaintedNotMarkedAsSpurious
|
||||
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
|
||||
failures
|
||||
@@ -0,0 +1 @@
|
||||
import experimental.meta.InlineTaintTest
|
||||
@@ -1,10 +0,0 @@
|
||||
| fabric_v1_execute.py:7 | fail | unsafe | cmd |
|
||||
| fabric_v1_execute.py:7 | fail | unsafe | cmd2 |
|
||||
| fabric_v1_execute.py:8 | ok | unsafe | safe_arg |
|
||||
| fabric_v1_execute.py:8 | ok | unsafe | safe_optional |
|
||||
| fabric_v1_execute.py:14 | fail | unsafe | cmd |
|
||||
| fabric_v1_execute.py:14 | fail | unsafe | cmd2 |
|
||||
| fabric_v1_execute.py:15 | ok | unsafe | safe_arg |
|
||||
| fabric_v1_execute.py:15 | ok | unsafe | safe_optional |
|
||||
| fabric_v1_execute.py:21 | ok | some_http_handler | cmd |
|
||||
| fabric_v1_execute.py:21 | ok | some_http_handler | cmd2 |
|
||||
@@ -1 +0,0 @@
|
||||
import experimental.dataflow.tainttracking.TestTaintLib
|
||||
@@ -4,21 +4,21 @@ from fabric.api import run, execute
|
||||
|
||||
|
||||
def unsafe(cmd, safe_arg, cmd2=None, safe_optional=5):
|
||||
ensure_tainted(cmd, cmd2)
|
||||
ensure_tainted(cmd, cmd2) # $ MISSING: tainted
|
||||
ensure_not_tainted(safe_arg, safe_optional)
|
||||
|
||||
|
||||
class Foo(object):
|
||||
|
||||
def unsafe(self, cmd, safe_arg, cmd2=None, safe_optional=5):
|
||||
ensure_tainted(cmd, cmd2)
|
||||
ensure_tainted(cmd, cmd2) # $ MISSING: tainted
|
||||
ensure_not_tainted(safe_arg, safe_optional)
|
||||
|
||||
|
||||
def some_http_handler():
|
||||
cmd = TAINTED_STRING
|
||||
cmd2 = TAINTED_STRING
|
||||
ensure_tainted(cmd, cmd2)
|
||||
ensure_tainted(cmd, cmd2) # $ tainted
|
||||
|
||||
execute(unsafe, cmd=cmd, safe_arg='safe_arg', cmd2=cmd2)
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
argumentToEnsureNotTaintedNotMarkedAsSpurious
|
||||
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
|
||||
failures
|
||||
@@ -0,0 +1 @@
|
||||
import experimental.meta.InlineTaintTest
|
||||
@@ -1,106 +0,0 @@
|
||||
| taint_test.py:6 | ok | test_taint | name |
|
||||
| taint_test.py:6 | ok | test_taint | number |
|
||||
| taint_test.py:7 | ok | test_taint | foo |
|
||||
| taint_test.py:14 | ok | test_taint | request.environ |
|
||||
| taint_test.py:15 | ok | test_taint | request.environ.get(..) |
|
||||
| taint_test.py:17 | ok | test_taint | request.path |
|
||||
| taint_test.py:18 | ok | test_taint | request.full_path |
|
||||
| taint_test.py:19 | ok | test_taint | request.base_url |
|
||||
| taint_test.py:20 | ok | test_taint | request.url |
|
||||
| taint_test.py:23 | fail | test_taint | request.accept_charsets.best |
|
||||
| taint_test.py:24 | fail | test_taint | request.accept_charsets.best_match(..) |
|
||||
| taint_test.py:25 | ok | test_taint | request.accept_charsets[0] |
|
||||
| taint_test.py:26 | ok | test_taint | request.accept_encodings |
|
||||
| taint_test.py:27 | ok | test_taint | request.accept_languages |
|
||||
| taint_test.py:28 | ok | test_taint | request.accept_mimetypes |
|
||||
| taint_test.py:31 | ok | test_taint | request.access_control_request_headers |
|
||||
| taint_test.py:33 | ok | test_taint | request.access_control_request_method |
|
||||
| taint_test.py:35 | ok | test_taint | request.access_route |
|
||||
| taint_test.py:36 | ok | test_taint | request.access_route[0] |
|
||||
| taint_test.py:39 | ok | test_taint | request.args |
|
||||
| taint_test.py:40 | ok | test_taint | request.args['key'] |
|
||||
| taint_test.py:41 | ok | test_taint | request.args.get(..) |
|
||||
| taint_test.py:42 | ok | test_taint | request.args.getlist(..) |
|
||||
| taint_test.py:45 | ok | test_taint | request.authorization |
|
||||
| taint_test.py:46 | ok | test_taint | request.authorization['username'] |
|
||||
| taint_test.py:47 | fail | test_taint | request.authorization.username |
|
||||
| taint_test.py:50 | ok | test_taint | request.cache_control |
|
||||
| taint_test.py:52 | fail | test_taint | request.cache_control.max_age |
|
||||
| taint_test.py:53 | fail | test_taint | request.cache_control.max_stale |
|
||||
| taint_test.py:54 | fail | test_taint | request.cache_control.min_fresh |
|
||||
| taint_test.py:56 | ok | test_taint | request.content_encoding |
|
||||
| taint_test.py:58 | ok | test_taint | request.content_md5 |
|
||||
| taint_test.py:60 | ok | test_taint | request.content_type |
|
||||
| taint_test.py:63 | ok | test_taint | request.cookies |
|
||||
| taint_test.py:64 | ok | test_taint | request.cookies['key'] |
|
||||
| taint_test.py:66 | ok | test_taint | request.data |
|
||||
| taint_test.py:69 | ok | test_taint | request.files |
|
||||
| taint_test.py:70 | ok | test_taint | request.files['key'] |
|
||||
| taint_test.py:71 | fail | test_taint | request.files['key'].filename |
|
||||
| taint_test.py:72 | fail | test_taint | request.files['key'].stream |
|
||||
| taint_test.py:73 | ok | test_taint | request.files.get(..) |
|
||||
| taint_test.py:74 | fail | test_taint | request.files.get(..).filename |
|
||||
| taint_test.py:75 | fail | test_taint | request.files.get(..).stream |
|
||||
| taint_test.py:76 | ok | test_taint | request.files.getlist(..) |
|
||||
| taint_test.py:77 | fail | test_taint | request.files.getlist(..)[0].filename |
|
||||
| taint_test.py:78 | fail | test_taint | request.files.getlist(..)[0].stream |
|
||||
| taint_test.py:81 | ok | test_taint | request.form |
|
||||
| taint_test.py:82 | ok | test_taint | request.form['key'] |
|
||||
| taint_test.py:83 | ok | test_taint | request.form.get(..) |
|
||||
| taint_test.py:84 | ok | test_taint | request.form.getlist(..) |
|
||||
| taint_test.py:86 | ok | test_taint | request.get_data() |
|
||||
| taint_test.py:88 | ok | test_taint | request.get_json() |
|
||||
| taint_test.py:89 | ok | test_taint | request.get_json()['foo'] |
|
||||
| taint_test.py:90 | ok | test_taint | request.get_json()['foo']['bar'] |
|
||||
| taint_test.py:94 | ok | test_taint | request.headers |
|
||||
| taint_test.py:95 | ok | test_taint | request.headers['key'] |
|
||||
| taint_test.py:96 | ok | test_taint | request.headers.get(..) |
|
||||
| taint_test.py:97 | fail | test_taint | request.headers.get_all(..) |
|
||||
| taint_test.py:98 | fail | test_taint | request.headers.getlist(..) |
|
||||
| taint_test.py:99 | ok | test_taint | list(..) |
|
||||
| taint_test.py:100 | fail | test_taint | request.headers.to_wsgi_list() |
|
||||
| taint_test.py:102 | ok | test_taint | request.json |
|
||||
| taint_test.py:103 | ok | test_taint | request.json['foo'] |
|
||||
| taint_test.py:104 | ok | test_taint | request.json['foo']['bar'] |
|
||||
| taint_test.py:106 | ok | test_taint | request.method |
|
||||
| taint_test.py:108 | ok | test_taint | request.mimetype |
|
||||
| taint_test.py:110 | ok | test_taint | request.mimetype_params |
|
||||
| taint_test.py:112 | ok | test_taint | request.origin |
|
||||
| taint_test.py:115 | ok | test_taint | request.pragma |
|
||||
| taint_test.py:117 | ok | test_taint | request.query_string |
|
||||
| taint_test.py:119 | ok | test_taint | request.referrer |
|
||||
| taint_test.py:121 | ok | test_taint | request.remote_addr |
|
||||
| taint_test.py:123 | ok | test_taint | request.remote_user |
|
||||
| taint_test.py:126 | ok | test_taint | request.stream |
|
||||
| taint_test.py:127 | ok | test_taint | request.input_stream |
|
||||
| taint_test.py:129 | ok | test_taint | request.url |
|
||||
| taint_test.py:131 | ok | test_taint | request.user_agent |
|
||||
| taint_test.py:134 | ok | test_taint | request.values |
|
||||
| taint_test.py:135 | ok | test_taint | request.values['key'] |
|
||||
| taint_test.py:136 | ok | test_taint | request.values.get(..) |
|
||||
| taint_test.py:137 | ok | test_taint | request.values.getlist(..) |
|
||||
| taint_test.py:140 | ok | test_taint | request.view_args |
|
||||
| taint_test.py:141 | ok | test_taint | request.view_args['key'] |
|
||||
| taint_test.py:142 | ok | test_taint | request.view_args.get(..) |
|
||||
| taint_test.py:146 | ok | test_taint | request.script_root |
|
||||
| taint_test.py:147 | ok | test_taint | request.url_root |
|
||||
| taint_test.py:151 | ok | test_taint | request.charset |
|
||||
| taint_test.py:152 | ok | test_taint | request.url_charset |
|
||||
| taint_test.py:156 | ok | test_taint | request.date |
|
||||
| taint_test.py:159 | ok | test_taint | request.endpoint |
|
||||
| taint_test.py:164 | ok | test_taint | request.host |
|
||||
| taint_test.py:165 | ok | test_taint | request.host_url |
|
||||
| taint_test.py:167 | ok | test_taint | request.scheme |
|
||||
| taint_test.py:169 | ok | test_taint | request.script_root |
|
||||
| taint_test.py:177 | ok | test_taint | request.args |
|
||||
| taint_test.py:178 | ok | test_taint | a |
|
||||
| taint_test.py:179 | ok | test_taint | b |
|
||||
| taint_test.py:181 | ok | test_taint | request.args['key'] |
|
||||
| taint_test.py:182 | ok | test_taint | a['key'] |
|
||||
| taint_test.py:183 | ok | test_taint | b['key'] |
|
||||
| taint_test.py:185 | ok | test_taint | request.args.getlist(..) |
|
||||
| taint_test.py:186 | ok | test_taint | a.getlist(..) |
|
||||
| taint_test.py:187 | ok | test_taint | b.getlist(..) |
|
||||
| taint_test.py:188 | ok | test_taint | gl(..) |
|
||||
| taint_test.py:195 | ok | test_taint | req.path |
|
||||
| taint_test.py:196 | ok | test_taint | gd() |
|
||||
@@ -1,6 +0,0 @@
|
||||
import experimental.dataflow.tainttracking.TestTaintLib
|
||||
import semmle.python.dataflow.new.RemoteFlowSources
|
||||
|
||||
class RemoteFlowTestTaintConfiguration extends TestTaintTrackingConfiguration {
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
}
|
||||
@@ -3,7 +3,7 @@ app = Flask(__name__)
|
||||
|
||||
@app.route("/test_taint/<name>/<int:number>") # $routeSetup="/test_taint/<name>/<int:number>"
|
||||
def test_taint(name = "World!", number="0", foo="foo"): # $requestHandler routedParameter=name routedParameter=number
|
||||
ensure_tainted(name, number)
|
||||
ensure_tainted(name, number) # $ tainted
|
||||
ensure_not_tainted(foo)
|
||||
|
||||
# Manually inspected all fields of the Request object
|
||||
@@ -11,135 +11,136 @@ def test_taint(name = "World!", number="0", foo="foo"): # $requestHandler route
|
||||
|
||||
ensure_tainted(
|
||||
|
||||
request.environ,
|
||||
request.environ.get('HTTP_AUTHORIZATION'),
|
||||
request.environ, # $ tainted
|
||||
request.environ.get('HTTP_AUTHORIZATION'), # $ tainted
|
||||
|
||||
request.path,
|
||||
request.full_path,
|
||||
request.base_url,
|
||||
request.url,
|
||||
request.path, # $ tainted
|
||||
request.full_path, # $ tainted
|
||||
request.base_url, # $ tainted
|
||||
request.url, # $ tainted
|
||||
|
||||
# These request.accept_* properties are instances of subclasses of werkzeug.datastructures.Accept
|
||||
request.accept_charsets.best,
|
||||
request.accept_charsets.best_match(["utf-8", "utf-16"]),
|
||||
request.accept_charsets[0],
|
||||
request.accept_encodings,
|
||||
request.accept_languages,
|
||||
request.accept_mimetypes,
|
||||
request.accept_charsets.best, # $ MISSING: tainted
|
||||
request.accept_charsets.best_match(["utf-8", "utf-16"]), # $ MISSING: tainted
|
||||
request.accept_charsets[0], # $ tainted
|
||||
request.accept_encodings, # $ tainted
|
||||
request.accept_languages, # $ tainted
|
||||
request.accept_mimetypes, # $ tainted
|
||||
|
||||
# werkzeug.datastructures.HeaderSet (subclass of collections_abc.MutableSet)
|
||||
request.access_control_request_headers,
|
||||
request.access_control_request_headers, # $ tainted
|
||||
|
||||
request.access_control_request_method,
|
||||
request.access_control_request_method, # $ tainted
|
||||
|
||||
request.access_route,
|
||||
request.access_route[0],
|
||||
request.access_route, # $ tainted
|
||||
request.access_route[0], # $ tainted
|
||||
|
||||
# By default werkzeug.datastructures.ImmutableMultiDict -- although can be changed :\
|
||||
request.args,
|
||||
request.args['key'],
|
||||
request.args.get('key'),
|
||||
request.args.getlist('key'),
|
||||
request.args, # $ tainted
|
||||
request.args['key'], # $ tainted
|
||||
request.args.get('key'), # $ tainted
|
||||
request.args.getlist('key'), # $ tainted
|
||||
|
||||
# werkzeug.datastructures.Authorization (a dict, with some properties)
|
||||
request.authorization,
|
||||
request.authorization['username'],
|
||||
request.authorization.username,
|
||||
request.authorization, # $ tainted
|
||||
request.authorization['username'], # $ tainted
|
||||
request.authorization.username, # $ MISSING: tainted
|
||||
|
||||
# werkzeug.datastructures.RequestCacheControl
|
||||
request.cache_control,
|
||||
request.cache_control, # $ tainted
|
||||
# These should be `int`s, but can be strings... see debug method below
|
||||
request.cache_control.max_age,
|
||||
request.cache_control.max_stale,
|
||||
request.cache_control.min_fresh,
|
||||
request.cache_control.max_age, # $ MISSING: tainted
|
||||
request.cache_control.max_stale, # $ MISSING: tainted
|
||||
request.cache_control.min_fresh, # $ MISSING: tainted
|
||||
|
||||
request.content_encoding,
|
||||
request.content_encoding, # $ tainted
|
||||
|
||||
request.content_md5,
|
||||
request.content_md5, # $ tainted
|
||||
|
||||
request.content_type,
|
||||
request.content_type, # $ tainted
|
||||
|
||||
# werkzeug.datastructures.ImmutableTypeConversionDict (which is basically just a dict)
|
||||
request.cookies,
|
||||
request.cookies['key'],
|
||||
request.cookies, # $ tainted
|
||||
request.cookies['key'], # $ tainted
|
||||
|
||||
request.data,
|
||||
request.data, # $ tainted
|
||||
|
||||
# a werkzeug.datastructures.MultiDict, mapping [str, werkzeug.datastructures.FileStorage]
|
||||
request.files,
|
||||
request.files['key'],
|
||||
request.files['key'].filename,
|
||||
request.files['key'].stream,
|
||||
request.files.get('key'),
|
||||
request.files.get('key').filename,
|
||||
request.files.get('key').stream,
|
||||
request.files.getlist('key'),
|
||||
request.files.getlist('key')[0].filename,
|
||||
request.files.getlist('key')[0].stream,
|
||||
request.files, # $ tainted
|
||||
request.files['key'], # $ tainted
|
||||
request.files['key'].filename, # $ MISSING: tainted
|
||||
request.files['key'].stream, # $ MISSING: tainted
|
||||
request.files.get('key'), # $ tainted
|
||||
request.files.get('key').filename, # $ MISSING: tainted
|
||||
request.files.get('key').stream, # $ MISSING: tainted
|
||||
request.files.getlist('key'), # $ tainted
|
||||
request.files.getlist('key')[0].filename, # $ MISSING: tainted
|
||||
request.files.getlist('key')[0].stream, # $ MISSING: tainted
|
||||
|
||||
# By default werkzeug.datastructures.ImmutableMultiDict -- although can be changed :\
|
||||
request.form,
|
||||
request.form['key'],
|
||||
request.form.get('key'),
|
||||
request.form.getlist('key'),
|
||||
request.form, # $ tainted
|
||||
request.form['key'], # $ tainted
|
||||
request.form.get('key'), # $ tainted
|
||||
request.form.getlist('key'), # $ tainted
|
||||
|
||||
request.get_data(),
|
||||
request.get_data(), # $ tainted
|
||||
|
||||
request.get_json(),
|
||||
request.get_json()['foo'],
|
||||
request.get_json()['foo']['bar'],
|
||||
request.get_json(), # $ tainted
|
||||
request.get_json()['foo'], # $ tainted
|
||||
request.get_json()['foo']['bar'], # $ tainted
|
||||
|
||||
# werkzeug.datastructures.EnvironHeaders,
|
||||
# which has same interface as werkzeug.datastructures.Headers
|
||||
request.headers,
|
||||
request.headers['key'],
|
||||
request.headers.get('key'),
|
||||
request.headers.get_all('key'),
|
||||
request.headers.getlist('key'),
|
||||
list(request.headers), # (k, v) list
|
||||
request.headers.to_wsgi_list(), # (k, v) list
|
||||
request.headers, # $ tainted
|
||||
request.headers['key'], # $ tainted
|
||||
request.headers.get('key'), # $ tainted
|
||||
request.headers.get_all('key'), # $ MISSING: tainted
|
||||
request.headers.getlist('key'), # $ MISSING: tainted
|
||||
# two ways to get (k, v) lists
|
||||
list(request.headers), # $ tainted
|
||||
request.headers.to_wsgi_list(), # $ MISSING: tainted
|
||||
|
||||
request.json,
|
||||
request.json['foo'],
|
||||
request.json['foo']['bar'],
|
||||
request.json, # $ tainted
|
||||
request.json['foo'], # $ tainted
|
||||
request.json['foo']['bar'], # $ tainted
|
||||
|
||||
request.method,
|
||||
request.method, # $ tainted
|
||||
|
||||
request.mimetype,
|
||||
request.mimetype, # $ tainted
|
||||
|
||||
request.mimetype_params,
|
||||
request.mimetype_params, # $ tainted
|
||||
|
||||
request.origin,
|
||||
request.origin, # $ tainted
|
||||
|
||||
# werkzeug.datastructures.HeaderSet (subclass of collections_abc.MutableSet)
|
||||
request.pragma,
|
||||
request.pragma, # $ tainted
|
||||
|
||||
request.query_string,
|
||||
request.query_string, # $ tainted
|
||||
|
||||
request.referrer,
|
||||
request.referrer, # $ tainted
|
||||
|
||||
request.remote_addr,
|
||||
request.remote_addr, # $ tainted
|
||||
|
||||
request.remote_user,
|
||||
request.remote_user, # $ tainted
|
||||
|
||||
# file-like object
|
||||
request.stream,
|
||||
request.input_stream,
|
||||
request.stream, # $ tainted
|
||||
request.input_stream, # $ tainted
|
||||
|
||||
request.url,
|
||||
request.url, # $ tainted
|
||||
|
||||
request.user_agent,
|
||||
request.user_agent, # $ tainted
|
||||
|
||||
# werkzeug.datastructures.CombinedMultiDict, which is basically just a werkzeug.datastructures.MultiDict
|
||||
request.values,
|
||||
request.values['key'],
|
||||
request.values.get('key'),
|
||||
request.values.getlist('key'),
|
||||
request.values, # $ tainted
|
||||
request.values['key'], # $ tainted
|
||||
request.values.get('key'), # $ tainted
|
||||
request.values.getlist('key'), # $ tainted
|
||||
|
||||
# dict
|
||||
request.view_args,
|
||||
request.view_args['key'],
|
||||
request.view_args.get('key'),
|
||||
request.view_args, # $ tainted
|
||||
request.view_args['key'], # $ tainted
|
||||
request.view_args.get('key'), # $ tainted
|
||||
)
|
||||
|
||||
ensure_not_tainted(
|
||||
@@ -174,26 +175,26 @@ def test_taint(name = "World!", number="0", foo="foo"): # $requestHandler route
|
||||
b = a
|
||||
gl = b.getlist
|
||||
ensure_tainted(
|
||||
request.args,
|
||||
a,
|
||||
b,
|
||||
request.args, # $ tainted
|
||||
a, # $ tainted
|
||||
b, # $ tainted
|
||||
|
||||
request.args['key'],
|
||||
a['key'],
|
||||
b['key'],
|
||||
request.args['key'], # $ tainted
|
||||
a['key'], # $ tainted
|
||||
b['key'], # $ tainted
|
||||
|
||||
request.args.getlist('key'),
|
||||
a.getlist('key'),
|
||||
b.getlist('key'),
|
||||
gl('key'),
|
||||
request.args.getlist('key'), # $ tainted
|
||||
a.getlist('key'), # $ tainted
|
||||
b.getlist('key'), # $ tainted
|
||||
gl('key'), # $ tainted
|
||||
)
|
||||
|
||||
# aliasing tests
|
||||
req = request
|
||||
gd = request.get_data
|
||||
ensure_tainted(
|
||||
req.path,
|
||||
gd(),
|
||||
req.path, # $ tainted
|
||||
gd(), # $ tainted
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ private import semmle.python.dataflow.new.TaintTracking
|
||||
/** A data-flow Node representing an instance of MyClass. */
|
||||
abstract class MyClass extends DataFlow::Node { }
|
||||
|
||||
private DataFlow::Node myClassGetValue(MyClass qualifier, DataFlow::TypeTracker t) {
|
||||
private DataFlow::LocalSourceNode myClassGetValue(MyClass qualifier, DataFlow::TypeTracker t) {
|
||||
t.startInAttr("get_value") and
|
||||
result = qualifier
|
||||
or
|
||||
@@ -14,7 +14,7 @@ private DataFlow::Node myClassGetValue(MyClass qualifier, DataFlow::TypeTracker
|
||||
}
|
||||
|
||||
DataFlow::Node myClassGetValue(MyClass qualifier) {
|
||||
result = myClassGetValue(qualifier, DataFlow::TypeTracker::end())
|
||||
myClassGetValue(qualifier, DataFlow::TypeTracker::end()).flowsTo(result)
|
||||
}
|
||||
|
||||
// Config
|
||||
|
||||
@@ -32,8 +32,8 @@ def test_additional_taint():
|
||||
cmd3 = builtins.compile(src, "<filename>", "exec")
|
||||
|
||||
ensure_tainted(
|
||||
src,
|
||||
cmd1,
|
||||
cmd2,
|
||||
cmd3,
|
||||
src, # $ tainted
|
||||
cmd1, # $ tainted
|
||||
cmd2, # $ tainted
|
||||
cmd3, # $ tainted
|
||||
)
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
argumentToEnsureNotTaintedNotMarkedAsSpurious
|
||||
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
|
||||
failures
|
||||
@@ -0,0 +1 @@
|
||||
import experimental.meta.InlineTaintTest
|
||||
@@ -1,37 +0,0 @@
|
||||
| CodeExecution.py:35 | ok | test_additional_taint | src |
|
||||
| CodeExecution.py:36 | ok | test_additional_taint | cmd1 |
|
||||
| CodeExecution.py:37 | ok | test_additional_taint | cmd2 |
|
||||
| CodeExecution.py:38 | ok | test_additional_taint | cmd3 |
|
||||
| http_server.py:22 | ok | test_cgi_FieldStorage_taint | form |
|
||||
| http_server.py:24 | ok | test_cgi_FieldStorage_taint | form['key'] |
|
||||
| http_server.py:25 | ok | test_cgi_FieldStorage_taint | form['key'].value |
|
||||
| http_server.py:26 | ok | test_cgi_FieldStorage_taint | form['key'].file |
|
||||
| http_server.py:27 | ok | test_cgi_FieldStorage_taint | form['key'].filename |
|
||||
| http_server.py:28 | ok | test_cgi_FieldStorage_taint | form['key'][0] |
|
||||
| http_server.py:29 | ok | test_cgi_FieldStorage_taint | form['key'][0].value |
|
||||
| http_server.py:30 | ok | test_cgi_FieldStorage_taint | form['key'][0].file |
|
||||
| http_server.py:31 | ok | test_cgi_FieldStorage_taint | form['key'][0].filename |
|
||||
| http_server.py:32 | fail | test_cgi_FieldStorage_taint | ListComp |
|
||||
| http_server.py:34 | ok | test_cgi_FieldStorage_taint | form.getvalue(..) |
|
||||
| http_server.py:35 | ok | test_cgi_FieldStorage_taint | form.getvalue(..)[0] |
|
||||
| http_server.py:37 | ok | test_cgi_FieldStorage_taint | form.getfirst(..) |
|
||||
| http_server.py:39 | ok | test_cgi_FieldStorage_taint | form.getlist(..) |
|
||||
| http_server.py:40 | ok | test_cgi_FieldStorage_taint | form.getlist(..)[0] |
|
||||
| http_server.py:41 | fail | test_cgi_FieldStorage_taint | ListComp |
|
||||
| http_server.py:50 | ok | taint_sources | self |
|
||||
| http_server.py:52 | ok | taint_sources | self.requestline |
|
||||
| http_server.py:54 | ok | taint_sources | self.path |
|
||||
| http_server.py:56 | ok | taint_sources | self.headers |
|
||||
| http_server.py:57 | ok | taint_sources | self.headers['Foo'] |
|
||||
| http_server.py:58 | ok | taint_sources | self.headers.get(..) |
|
||||
| http_server.py:59 | fail | taint_sources | self.headers.get_all(..) |
|
||||
| http_server.py:60 | fail | taint_sources | self.headers.keys() |
|
||||
| http_server.py:61 | ok | taint_sources | self.headers.values() |
|
||||
| http_server.py:62 | ok | taint_sources | self.headers.items() |
|
||||
| http_server.py:63 | fail | taint_sources | self.headers.as_bytes() |
|
||||
| http_server.py:64 | fail | taint_sources | self.headers.as_string() |
|
||||
| http_server.py:65 | ok | taint_sources | str(..) |
|
||||
| http_server.py:66 | ok | taint_sources | bytes(..) |
|
||||
| http_server.py:68 | ok | taint_sources | self.rfile |
|
||||
| http_server.py:69 | fail | taint_sources | self.rfile.read() |
|
||||
| http_server.py:78 | ok | taint_sources | form |
|
||||
@@ -1,9 +0,0 @@
|
||||
import experimental.dataflow.tainttracking.TestTaintLib
|
||||
import semmle.python.dataflow.new.RemoteFlowSources
|
||||
|
||||
class WithRemoteFlowSources extends TestTaintTrackingConfiguration {
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
super.isSource(source) or
|
||||
source instanceof RemoteFlowSource
|
||||
}
|
||||
}
|
||||
@@ -19,26 +19,28 @@ def test_cgi_FieldStorage_taint():
|
||||
form = cgi.FieldStorage()
|
||||
|
||||
ensure_tainted(
|
||||
form,
|
||||
form, # $ tainted
|
||||
|
||||
form['key'], # will be a list, if multiple fields named "key" are provided
|
||||
form['key'].value,
|
||||
form['key'].file,
|
||||
form['key'].filename,
|
||||
form['key'][0],
|
||||
form['key'][0].value,
|
||||
form['key'][0].file,
|
||||
form['key'][0].filename,
|
||||
[field.value for field in form['key']],
|
||||
# `form['key']` will be a list, if multiple fields named "key" are provided
|
||||
form['key'], # $ tainted
|
||||
form['key'].value, # $ tainted
|
||||
form['key'].file, # $ tainted
|
||||
form['key'].filename, # $ tainted
|
||||
form['key'][0], # $ tainted
|
||||
form['key'][0].value, # $ tainted
|
||||
form['key'][0].file, # $ tainted
|
||||
form['key'][0].filename, # $ tainted
|
||||
[field.value for field in form['key']], # $ MISSING: tainted
|
||||
|
||||
form.getvalue('key'), # will be a list, if multiple fields named "key" are provided
|
||||
form.getvalue('key')[0],
|
||||
# `form.getvalue('key')` will be a list, if multiple fields named "key" are provided
|
||||
form.getvalue('key'), # $ tainted
|
||||
form.getvalue('key')[0], # $ tainted
|
||||
|
||||
form.getfirst('key'),
|
||||
form.getfirst('key'), # $ tainted
|
||||
|
||||
form.getlist('key'),
|
||||
form.getlist('key')[0],
|
||||
[field.value for field in form.getlist('key')],
|
||||
form.getlist('key'), # $ tainted
|
||||
form.getlist('key')[0], # $ tainted
|
||||
[field.value for field in form.getlist('key')], # $ MISSING: tainted
|
||||
)
|
||||
|
||||
|
||||
@@ -47,26 +49,26 @@ class MyHandler(BaseHTTPRequestHandler):
|
||||
def taint_sources(self):
|
||||
|
||||
ensure_tainted(
|
||||
self,
|
||||
self, # $ tainted
|
||||
|
||||
self.requestline,
|
||||
self.requestline, # $ tainted
|
||||
|
||||
self.path,
|
||||
self.path, # $ tainted
|
||||
|
||||
self.headers,
|
||||
self.headers['Foo'],
|
||||
self.headers.get('Foo'),
|
||||
self.headers.get_all('Foo'),
|
||||
self.headers.keys(),
|
||||
self.headers.values(),
|
||||
self.headers.items(),
|
||||
self.headers.as_bytes(),
|
||||
self.headers.as_string(),
|
||||
str(self.headers),
|
||||
bytes(self.headers),
|
||||
self.headers, # $ tainted
|
||||
self.headers['Foo'], # $ tainted
|
||||
self.headers.get('Foo'), # $ tainted
|
||||
self.headers.get_all('Foo'), # $ MISSING: tainted
|
||||
self.headers.keys(), # $ MISSING: tainted
|
||||
self.headers.values(), # $ tainted
|
||||
self.headers.items(), # $ tainted
|
||||
self.headers.as_bytes(), # $ MISSING: tainted
|
||||
self.headers.as_string(), # $ MISSING: tainted
|
||||
str(self.headers), # $ tainted
|
||||
bytes(self.headers), # $ tainted
|
||||
|
||||
self.rfile,
|
||||
self.rfile.read(),
|
||||
self.rfile, # $ tainted
|
||||
self.rfile.read(), # $ MISSING: tainted
|
||||
)
|
||||
|
||||
form = cgi.FieldStorage(
|
||||
@@ -75,7 +77,7 @@ class MyHandler(BaseHTTPRequestHandler):
|
||||
environ={'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': self.headers.get('content-type')},
|
||||
)
|
||||
|
||||
ensure_tainted(form)
|
||||
ensure_tainted(form) # $ tainted
|
||||
|
||||
|
||||
def do_GET(self): # $ requestHandler
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
argumentToEnsureNotTaintedNotMarkedAsSpurious
|
||||
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
|
||||
failures
|
||||
@@ -0,0 +1 @@
|
||||
import experimental.meta.InlineTaintTest
|
||||
@@ -1,41 +0,0 @@
|
||||
| taint_test.py:6 | ok | get | name |
|
||||
| taint_test.py:6 | ok | get | number |
|
||||
| taint_test.py:7 | ok | get | foo |
|
||||
| taint_test.py:11 | ok | get | self.get_argument(..) |
|
||||
| taint_test.py:12 | ok | get | self.get_arguments(..) |
|
||||
| taint_test.py:13 | ok | get | self.get_arguments(..)[0] |
|
||||
| taint_test.py:15 | ok | get | self.get_body_argument(..) |
|
||||
| taint_test.py:16 | ok | get | self.get_body_arguments(..) |
|
||||
| taint_test.py:17 | ok | get | self.get_body_arguments(..)[0] |
|
||||
| taint_test.py:19 | ok | get | self.get_query_argument(..) |
|
||||
| taint_test.py:20 | ok | get | self.get_query_arguments(..) |
|
||||
| taint_test.py:21 | ok | get | self.get_query_arguments(..)[0] |
|
||||
| taint_test.py:23 | ok | get | self.path_args |
|
||||
| taint_test.py:24 | ok | get | self.path_args[0] |
|
||||
| taint_test.py:26 | ok | get | self.path_kwargs |
|
||||
| taint_test.py:27 | ok | get | self.path_kwargs["name"] |
|
||||
| taint_test.py:34 | ok | get | request |
|
||||
| taint_test.py:40 | ok | get | request.uri |
|
||||
| taint_test.py:41 | ok | get | request.path |
|
||||
| taint_test.py:42 | ok | get | request.query |
|
||||
| taint_test.py:43 | ok | get | request.full_url() |
|
||||
| taint_test.py:45 | ok | get | request.remote_ip |
|
||||
| taint_test.py:47 | ok | get | request.body |
|
||||
| taint_test.py:49 | ok | get | request.arguments |
|
||||
| taint_test.py:50 | ok | get | request.arguments["name"] |
|
||||
| taint_test.py:51 | ok | get | request.arguments["name"][0] |
|
||||
| taint_test.py:53 | ok | get | request.query_arguments |
|
||||
| taint_test.py:54 | ok | get | request.query_arguments["name"] |
|
||||
| taint_test.py:55 | ok | get | request.query_arguments["name"][0] |
|
||||
| taint_test.py:57 | ok | get | request.body_arguments |
|
||||
| taint_test.py:58 | ok | get | request.body_arguments["name"] |
|
||||
| taint_test.py:59 | ok | get | request.body_arguments["name"][0] |
|
||||
| taint_test.py:62 | ok | get | request.headers |
|
||||
| taint_test.py:63 | ok | get | request.headers["header-name"] |
|
||||
| taint_test.py:64 | fail | get | request.headers.get_list(..) |
|
||||
| taint_test.py:65 | fail | get | request.headers.get_all() |
|
||||
| taint_test.py:66 | fail | get | ListComp |
|
||||
| taint_test.py:69 | ok | get | request.cookies |
|
||||
| taint_test.py:70 | ok | get | request.cookies["cookie-name"] |
|
||||
| taint_test.py:71 | fail | get | request.cookies["cookie-name"].key |
|
||||
| taint_test.py:72 | fail | get | request.cookies["cookie-name"].value |
|
||||
@@ -1,6 +0,0 @@
|
||||
import experimental.dataflow.tainttracking.TestTaintLib
|
||||
import semmle.python.dataflow.new.RemoteFlowSources
|
||||
|
||||
class RemoteFlowTestTaintConfiguration extends TestTaintTrackingConfiguration {
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
}
|
||||
@@ -3,73 +3,73 @@ import tornado.web
|
||||
|
||||
class TaintTest(tornado.web.RequestHandler):
|
||||
def get(self, name = "World!", number="0", foo="foo"): # $ requestHandler routedParameter=name routedParameter=number
|
||||
ensure_tainted(name, number)
|
||||
ensure_tainted(name, number) # $ tainted
|
||||
ensure_not_tainted(foo)
|
||||
|
||||
ensure_tainted(
|
||||
# see https://www.tornadoweb.org/en/stable/web.html#input
|
||||
self.get_argument("name"),
|
||||
self.get_arguments("name"),
|
||||
self.get_arguments("name")[0],
|
||||
self.get_argument("name"), # $ tainted
|
||||
self.get_arguments("name"), # $ tainted
|
||||
self.get_arguments("name")[0], # $ tainted
|
||||
|
||||
self.get_body_argument("name"),
|
||||
self.get_body_arguments("name"),
|
||||
self.get_body_arguments("name")[0],
|
||||
self.get_body_argument("name"), # $ tainted
|
||||
self.get_body_arguments("name"), # $ tainted
|
||||
self.get_body_arguments("name")[0], # $ tainted
|
||||
|
||||
self.get_query_argument("name"),
|
||||
self.get_query_arguments("name"),
|
||||
self.get_query_arguments("name")[0],
|
||||
self.get_query_argument("name"), # $ tainted
|
||||
self.get_query_arguments("name"), # $ tainted
|
||||
self.get_query_arguments("name")[0], # $ tainted
|
||||
|
||||
self.path_args,
|
||||
self.path_args[0],
|
||||
self.path_args, # $ tainted
|
||||
self.path_args[0], # $ tainted
|
||||
|
||||
self.path_kwargs,
|
||||
self.path_kwargs["name"],
|
||||
self.path_kwargs, # $ tainted
|
||||
self.path_kwargs["name"], # $ tainted
|
||||
)
|
||||
|
||||
request = self.request
|
||||
|
||||
ensure_tainted(
|
||||
# see https://www.tornadoweb.org/en/stable/httputil.html#tornado.httputil.HTTPServerRequest
|
||||
request,
|
||||
request, # $ tainted
|
||||
|
||||
# For the URL https:://example.com/foo/bar?baz=42
|
||||
# request.uri="/foo/bar?baz=42"
|
||||
# request.path="/foo/bar"
|
||||
# request.query="baz=42"
|
||||
request.uri,
|
||||
request.path,
|
||||
request.query,
|
||||
request.full_url(),
|
||||
request.uri, # $ tainted
|
||||
request.path, # $ tainted
|
||||
request.query, # $ tainted
|
||||
request.full_url(), # $ tainted
|
||||
|
||||
request.remote_ip,
|
||||
request.remote_ip, # $ tainted
|
||||
|
||||
request.body,
|
||||
request.body, # $ tainted
|
||||
|
||||
request.arguments,
|
||||
request.arguments["name"],
|
||||
request.arguments["name"][0],
|
||||
request.arguments, # $ tainted
|
||||
request.arguments["name"], # $ tainted
|
||||
request.arguments["name"][0], # $ tainted
|
||||
|
||||
request.query_arguments,
|
||||
request.query_arguments["name"],
|
||||
request.query_arguments["name"][0],
|
||||
request.query_arguments, # $ tainted
|
||||
request.query_arguments["name"], # $ tainted
|
||||
request.query_arguments["name"][0], # $ tainted
|
||||
|
||||
request.body_arguments,
|
||||
request.body_arguments["name"],
|
||||
request.body_arguments["name"][0],
|
||||
request.body_arguments, # $ tainted
|
||||
request.body_arguments["name"], # $ tainted
|
||||
request.body_arguments["name"][0], # $ tainted
|
||||
|
||||
# dict-like, see https://www.tornadoweb.org/en/stable/httputil.html#tornado.httputil.HTTPHeaders
|
||||
request.headers,
|
||||
request.headers["header-name"],
|
||||
request.headers.get_list("header-name"),
|
||||
request.headers.get_all(),
|
||||
[(k, v) for (k, v) in request.headers.get_all()],
|
||||
request.headers, # $ tainted
|
||||
request.headers["header-name"], # $ tainted
|
||||
request.headers.get_list("header-name"), # $ MISSING: tainted
|
||||
request.headers.get_all(), # $ MISSING: tainted
|
||||
[(k, v) for (k, v) in request.headers.get_all()], # $ MISSING: tainted
|
||||
|
||||
# Dict[str, http.cookies.Morsel]
|
||||
request.cookies,
|
||||
request.cookies["cookie-name"],
|
||||
request.cookies["cookie-name"].key,
|
||||
request.cookies["cookie-name"].value,
|
||||
request.cookies, # $ tainted
|
||||
request.cookies["cookie-name"], # $ tainted
|
||||
request.cookies["cookie-name"].key, # $ MISSING: tainted
|
||||
request.cookies["cookie-name"].value, # $ MISSING: tainted
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user