mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
Merge branch 'main' of github.com:github/codeql into python-support-pathlib
This commit is contained in:
8
.github/workflows/close-stale.yml
vendored
8
.github/workflows/close-stale.yml
vendored
@@ -15,16 +15,16 @@ jobs:
|
||||
- uses: actions/stale@v3
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: 'This issue is stale because it has been open 14 days with no activity. Comment or remove the `stale` label in order to avoid having this issue closed in 7 days.'
|
||||
stale-issue-message: 'This issue is stale because it has been open 14 days with no activity. Comment or remove the `Stale` label in order to avoid having this issue closed in 7 days.'
|
||||
close-issue-message: 'This issue was closed because it has been inactive for 7 days.'
|
||||
days-before-stale: 14
|
||||
days-before-close: 7
|
||||
only-labels: question
|
||||
|
||||
only-labels: awaiting-response
|
||||
|
||||
# do not mark PRs as stale
|
||||
days-before-pr-stale: -1
|
||||
days-before-pr-close: -1
|
||||
|
||||
|
||||
# Uncomment for dry-run
|
||||
# debug-only: true
|
||||
# operations-per-run: 1000
|
||||
|
||||
2
cpp/change-notes/2021-04-13-arithmetic-queries.md
Normal file
2
cpp/change-notes/2021-04-13-arithmetic-queries.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm
|
||||
* The queries cpp/tainted-arithmetic, cpp/uncontrolled-arithmetic, and cpp/arithmetic-with-extreme-values have been improved to produce fewer false positives.
|
||||
@@ -0,0 +1,2 @@
|
||||
codescanning
|
||||
* The 'Pointer to stack object used as return value' (cpp/return-stack-allocated-object) query has been deprecated, and any uses should be replaced with `Returning stack-allocated memory` (cpp/return-stack-allocated-memory).
|
||||
@@ -7,6 +7,8 @@
|
||||
* @tags reliability
|
||||
* security
|
||||
* external/cwe/cwe-562
|
||||
* @deprecated This query is not suitable for production use and has been deprecated. Use
|
||||
* cpp/return-stack-allocated-memory instead.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.pointsto.PointsTo
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.EscapesTree
|
||||
import semmle.code.cpp.models.interfaces.PointerWrapper
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
|
||||
/**
|
||||
@@ -39,6 +40,10 @@ predicate hasNontrivialConversion(Expr e) {
|
||||
e instanceof ParenthesisExpr
|
||||
)
|
||||
or
|
||||
// A smart pointer can be stack-allocated while the data it points to is heap-allocated.
|
||||
// So we exclude such "conversions" from this predicate.
|
||||
e = any(PointerWrapper wrapper).getAnUnwrapperFunction().getACallToThisFunction()
|
||||
or
|
||||
hasNontrivialConversion(e.getConversion())
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
@@ -11,54 +11,32 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.models.implementations.Strcat
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
/**
|
||||
* A call to `strncat` of the form `strncat(buff, str, someExpr - strlen(buf))`, for some expression `someExpr` equal to `sizeof(buff)`.
|
||||
* Holds if `call` is a call to `strncat` such that `sizeArg` and `destArg` are the size and
|
||||
* destination arguments, respectively.
|
||||
*/
|
||||
class WrongCallStrncat extends FunctionCall {
|
||||
Expr leftsomeExpr;
|
||||
|
||||
WrongCallStrncat() {
|
||||
this.getTarget().hasGlobalOrStdName("strncat") and
|
||||
// the expression of the first argument in `strncat` and `strnlen` is identical
|
||||
globalValueNumber(this.getArgument(0)) =
|
||||
globalValueNumber(this.getArgument(2).(SubExpr).getRightOperand().(StrlenCall).getStringExpr()) and
|
||||
// using a string constant often speaks of manually calculating the length of the required buffer.
|
||||
(
|
||||
not this.getArgument(1) instanceof StringLiteral and
|
||||
not this.getArgument(1) instanceof CharLiteral
|
||||
) and
|
||||
// for use in predicates
|
||||
leftsomeExpr = this.getArgument(2).(SubExpr).getLeftOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the left side of the expression `someExpr` equal to `sizeof(buf)`.
|
||||
*/
|
||||
predicate isExpressionEqualSizeof() {
|
||||
// the left side of the expression `someExpr` is `sizeof(buf)`.
|
||||
globalValueNumber(this.getArgument(0)) =
|
||||
globalValueNumber(leftsomeExpr.(SizeofExprOperator).getExprOperand())
|
||||
or
|
||||
// value of the left side of the expression `someExpr` equal `sizeof(buf)` value, and `buf` is array.
|
||||
leftsomeExpr.getValue().toInt() = this.getArgument(0).getType().getSize()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the left side of the expression `someExpr` equal to variable containing the length of the memory allocated for the buffer.
|
||||
*/
|
||||
predicate isVariableEqualValueSizegBuffer() {
|
||||
// the left side of expression `someExpr` is the variable that was used in the function of allocating memory for the buffer`.
|
||||
exists(AllocationExpr alc |
|
||||
leftsomeExpr.(VariableAccess).getTarget() =
|
||||
alc.(FunctionCall).getArgument(0).(VariableAccess).getTarget()
|
||||
)
|
||||
}
|
||||
predicate interestringCallWithArgs(Call call, Expr sizeArg, Expr destArg) {
|
||||
exists(StrcatFunction strcat |
|
||||
strcat = call.getTarget() and
|
||||
sizeArg = call.getArgument(strcat.getParamSize()) and
|
||||
destArg = call.getArgument(strcat.getParamDest())
|
||||
)
|
||||
}
|
||||
|
||||
from WrongCallStrncat sc
|
||||
from FunctionCall call, Expr sizeArg, Expr destArg, SubExpr sub, int n
|
||||
where
|
||||
sc.isExpressionEqualSizeof() or
|
||||
sc.isVariableEqualValueSizegBuffer()
|
||||
select sc, "if the used buffer is full, writing out of the buffer is possible"
|
||||
interestringCallWithArgs(call, sizeArg, destArg) and
|
||||
// The destination buffer is an array of size n
|
||||
destArg.getUnspecifiedType().(ArrayType).getSize() = n and
|
||||
// The size argument is equivalent to a subtraction
|
||||
globalValueNumber(sizeArg).getAnExpr() = sub and
|
||||
// ... where the left side of the subtraction is the constant n
|
||||
globalValueNumber(sub.getLeftOperand()).getAnExpr().getValue().toInt() = n and
|
||||
// ... and the right side of the subtraction is a call to `strlen` where the argument is the
|
||||
// destination buffer.
|
||||
globalValueNumber(sub.getRightOperand()).getAnExpr().(StrlenCall).getStringExpr() =
|
||||
globalValueNumber(destArg).getAnExpr()
|
||||
select call, "Possible out-of-bounds write due to incorrect size argument."
|
||||
|
||||
@@ -18,20 +18,20 @@ private class UniqueOrSharedPtr extends Class, PointerWrapper {
|
||||
}
|
||||
|
||||
/** Any function that unwraps a pointer wrapper class to reveal the underlying pointer. */
|
||||
private class PointerWrapperDataFlow extends DataFlowFunction {
|
||||
PointerWrapperDataFlow() {
|
||||
private class PointerWrapperFlow extends TaintFunction, DataFlowFunction {
|
||||
PointerWrapperFlow() {
|
||||
this = any(PointerWrapper wrapper).getAnUnwrapperFunction() and
|
||||
not this.getUnspecifiedType() instanceof ReferenceType
|
||||
}
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierAddress() and output.isReturnValue()
|
||||
or
|
||||
input.isQualifierObject() and output.isReturnValueDeref()
|
||||
or
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isReturnValueDeref() and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierObject() and output.isReturnValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.controlflow.Dominance
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
|
||||
|
||||
/**
|
||||
* Holds if the value of `use` is guarded using `abs`.
|
||||
@@ -94,9 +96,15 @@ predicate guardedGreater(Operation e, Expr use) {
|
||||
VariableAccess varUse(LocalScopeVariable v) { result = v.getAnAccess() }
|
||||
|
||||
/**
|
||||
* Holds if `e` is not guarded against overflow by `use`.
|
||||
* Holds if `e` potentially overflows and `use` is an operand of `e` that is not guarded.
|
||||
*/
|
||||
predicate missingGuardAgainstOverflow(Operation e, VariableAccess use) {
|
||||
(
|
||||
convertedExprMightOverflowPositively(e)
|
||||
or
|
||||
// Ensure that the predicate holds when range analysis cannot determine an upper bound
|
||||
upperBound(e.getFullyConverted()) = exprMaxVal(e.getFullyConverted())
|
||||
) and
|
||||
use = e.getAnOperand() and
|
||||
exists(LocalScopeVariable v | use.getTarget() = v |
|
||||
// overflow possible if large
|
||||
@@ -115,9 +123,15 @@ predicate missingGuardAgainstOverflow(Operation e, VariableAccess use) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` is not guarded against underflow by `use`.
|
||||
* Holds if `e` potentially underflows and `use` is an operand of `e` that is not guarded.
|
||||
*/
|
||||
predicate missingGuardAgainstUnderflow(Operation e, VariableAccess use) {
|
||||
(
|
||||
convertedExprMightOverflowNegatively(e)
|
||||
or
|
||||
// Ensure that the predicate holds when range analysis cannot determine a lower bound
|
||||
lowerBound(e.getFullyConverted()) = exprMinVal(e.getFullyConverted())
|
||||
) and
|
||||
use = e.getAnOperand() and
|
||||
exists(LocalScopeVariable v | use.getTarget() = v |
|
||||
// underflow possible if use is left operand and small
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
| test.c:42:3:42:24 | ... = ... | potential unsafe or redundant assignment. |
|
||||
| test.c:43:3:43:40 | ... = ... | potential unsafe or redundant assignment. |
|
||||
| test.c:44:3:44:40 | ... = ... | potential unsafe or redundant assignment. |
|
||||
| test.c:45:3:45:44 | ... = ... | potential unsafe or redundant assignment. |
|
||||
| test.c:46:3:46:44 | ... = ... | potential unsafe or redundant assignment. |
|
||||
| test.c:47:3:47:48 | ... = ... | potential unsafe or redundant assignment. |
|
||||
| test.c:48:3:48:48 | ... = ... | potential unsafe or redundant assignment. |
|
||||
| test.c:49:3:49:50 | ... = ... | potential unsafe or redundant assignment. |
|
||||
| test.c:50:3:50:50 | ... = ... | potential unsafe or redundant assignment. |
|
||||
| test.c:54:3:54:24 | ... = ... | potential unsafe or redundant assignment. |
|
||||
| test.c:55:3:55:40 | ... = ... | potential unsafe or redundant assignment. |
|
||||
| test.c:56:3:56:44 | ... = ... | potential unsafe or redundant assignment. |
|
||||
| test.c:57:3:57:44 | ... = ... | potential unsafe or redundant assignment. |
|
||||
| test.c:58:3:58:48 | ... = ... | potential unsafe or redundant assignment. |
|
||||
| test.c:59:3:59:48 | ... = ... | potential unsafe or redundant assignment. |
|
||||
| test.c:60:3:60:52 | ... = ... | potential unsafe or redundant assignment. |
|
||||
| test.c:61:3:61:50 | ... = ... | potential unsafe or redundant assignment. |
|
||||
| test.c:62:3:62:54 | ... = ... | potential unsafe or redundant assignment. |
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
| test.c:4:3:4:9 | call to strncat | if the used buffer is full, writing out of the buffer is possible |
|
||||
| test.c:11:3:11:9 | call to strncat | if the used buffer is full, writing out of the buffer is possible |
|
||||
| test.c:19:3:19:9 | call to strncat | if the used buffer is full, writing out of the buffer is possible |
|
||||
| test.c:8:3:8:9 | call to strncat | Possible out-of-bounds write due to incorrect size argument. |
|
||||
| test.c:9:3:9:9 | call to strncat | Possible out-of-bounds write due to incorrect size argument. |
|
||||
| test.c:17:3:17:9 | call to strncat | Possible out-of-bounds write due to incorrect size argument. |
|
||||
| test.c:18:3:18:9 | call to strncat | Possible out-of-bounds write due to incorrect size argument. |
|
||||
| test.c:46:3:46:9 | call to strncat | Possible out-of-bounds write due to incorrect size argument. |
|
||||
|
||||
@@ -1,70 +1,84 @@
|
||||
void workFunction_0(char *s) {
|
||||
char * strncat(char*, const char*, unsigned);
|
||||
unsigned strlen(const char*);
|
||||
void* malloc(unsigned);
|
||||
|
||||
void strncat_test1(char *s) {
|
||||
char buf[80];
|
||||
strncat(buf, s, sizeof(buf)-strlen(buf)-1); // GOOD
|
||||
strncat(buf, s, sizeof(buf)-strlen(buf)); // BAD
|
||||
strncat(buf, "fix", sizeof(buf)-strlen(buf)); // BAD [NOT DETECTED]
|
||||
strncat(buf, s, sizeof(buf) - strlen(buf) - 1); // GOOD
|
||||
strncat(buf, s, sizeof(buf) - strlen(buf)); // BAD
|
||||
strncat(buf, "fix", sizeof(buf)-strlen(buf)); // BAD
|
||||
}
|
||||
void workFunction_1(char *s) {
|
||||
|
||||
#define MAX_SIZE 80
|
||||
|
||||
void strncat_test2(char *s) {
|
||||
char buf[MAX_SIZE];
|
||||
strncat(buf, s, MAX_SIZE-strlen(buf)-1); // GOOD
|
||||
strncat(buf, s, MAX_SIZE-strlen(buf)); // BAD
|
||||
strncat(buf, "fix", MAX_SIZE-strlen(buf)); // BAD [NOT DETECTED]
|
||||
strncat(buf, s, MAX_SIZE - strlen(buf) - 1); // GOOD
|
||||
strncat(buf, s, MAX_SIZE - strlen(buf)); // BAD
|
||||
strncat(buf, "fix", MAX_SIZE - strlen(buf)); // BAD
|
||||
}
|
||||
void workFunction_2_0(char *s) {
|
||||
char * buf;
|
||||
int len=80;
|
||||
buf = (char *) malloc(len);
|
||||
strncat(buf, s, len-strlen(buf)-1); // GOOD
|
||||
strncat(buf, s, len-strlen(buf)); // BAD
|
||||
strncat(buf, "fix", len-strlen(buf)); // BAD [NOT DETECTED]
|
||||
|
||||
void strncat_test3(char *s) {
|
||||
int len = 80;
|
||||
char* buf = (char *) malloc(len);
|
||||
strncat(buf, s, len - strlen(buf) - 1); // GOOD
|
||||
strncat(buf, s, len - strlen(buf)); // BAD [NOT DETECTED]
|
||||
strncat(buf, "fix", len - strlen(buf)); // BAD [NOT DETECTED]
|
||||
}
|
||||
void workFunction_2_1(char *s) {
|
||||
char * buf;
|
||||
int len=80;
|
||||
buf = (char *) malloc(len+1);
|
||||
strncat(buf, s, len-strlen(buf)-1); // GOOD
|
||||
strncat(buf, s, len-strlen(buf)); // GOOD
|
||||
|
||||
void strncat_test4(char *s) {
|
||||
int len = 80;
|
||||
char* buf = (char *) malloc(len + 1);
|
||||
strncat(buf, s, len - strlen(buf) - 1); // GOOD
|
||||
strncat(buf, s, len - strlen(buf)); // GOOD
|
||||
}
|
||||
|
||||
struct buffers
|
||||
{
|
||||
unsigned char buff1[50];
|
||||
unsigned char *buff2;
|
||||
unsigned char array[50];
|
||||
unsigned char *pointer;
|
||||
} globalBuff1,*globalBuff2,globalBuff1_c,*globalBuff2_c;
|
||||
|
||||
void strncat_test5(char* s, struct buffers* buffers) {
|
||||
unsigned len_array = strlen(buffers->array);
|
||||
unsigned max_size = sizeof(buffers->array);
|
||||
unsigned free_size = max_size - len_array;
|
||||
strncat(buffers->array, s, free_size); // BAD
|
||||
}
|
||||
|
||||
void badFunc0(){
|
||||
void strlen_test1(){
|
||||
unsigned char buff1[12];
|
||||
struct buffers buffAll;
|
||||
struct buffers * buffAll1;
|
||||
|
||||
buff1[strlen(buff1)]=0; // BAD
|
||||
buffAll.buff1[strlen(buffAll.buff1)]=0; // BAD
|
||||
buffAll.buff2[strlen(buffAll.buff2)]=0; // BAD
|
||||
buffAll1->buff1[strlen(buffAll1->buff1)]=0; // BAD
|
||||
buffAll1->buff2[strlen(buffAll1->buff2)]=0; // BAD
|
||||
globalBuff1.buff1[strlen(globalBuff1.buff1)]=0; // BAD
|
||||
globalBuff1.buff2[strlen(globalBuff1.buff2)]=0; // BAD
|
||||
globalBuff2->buff1[strlen(globalBuff2->buff1)]=0; // BAD
|
||||
globalBuff2->buff2[strlen(globalBuff2->buff2)]=0; // BAD
|
||||
buffAll.array[strlen(buffAll.array)]=0; // BAD
|
||||
buffAll.pointer[strlen(buffAll.pointer)]=0; // BAD
|
||||
buffAll1->array[strlen(buffAll1->array)]=0; // BAD
|
||||
buffAll1->pointer[strlen(buffAll1->pointer)]=0; // BAD
|
||||
globalBuff1.array[strlen(globalBuff1.array)]=0; // BAD
|
||||
globalBuff1.pointer[strlen(globalBuff1.pointer)]=0; // BAD
|
||||
globalBuff2->array[strlen(globalBuff2->array)]=0; // BAD
|
||||
globalBuff2->pointer[strlen(globalBuff2->pointer)]=0; // BAD
|
||||
}
|
||||
void noBadFunc0(){
|
||||
|
||||
void strlen_test2(){
|
||||
unsigned char buff1[12],buff1_c[12];
|
||||
struct buffers buffAll,buffAll_c;
|
||||
struct buffers * buffAll1,*buffAll1_c;
|
||||
|
||||
buff1[strlen(buff1_c)]=0; // GOOD
|
||||
buffAll.buff1[strlen(buffAll_c.buff1)]=0; // GOOD
|
||||
buffAll.buff2[strlen(buffAll.buff1)]=0; // GOOD
|
||||
buffAll1->buff1[strlen(buffAll1_c->buff1)]=0; // GOOD
|
||||
buffAll1->buff2[strlen(buffAll1->buff1)]=0; // GOOD
|
||||
globalBuff1.buff1[strlen(globalBuff1_c.buff1)]=0; // GOOD
|
||||
globalBuff1.buff2[strlen(globalBuff1.buff1)]=0; // GOOD
|
||||
globalBuff2->buff1[strlen(globalBuff2_c->buff1)]=0; // GOOD
|
||||
globalBuff2->buff2[strlen(globalBuff2->buff1)]=0; // GOOD
|
||||
buffAll.array[strlen(buffAll_c.array)]=0; // GOOD
|
||||
buffAll.pointer[strlen(buffAll.array)]=0; // GOOD
|
||||
buffAll1->array[strlen(buffAll1_c->array)]=0; // GOOD
|
||||
buffAll1->pointer[strlen(buffAll1->array)]=0; // GOOD
|
||||
globalBuff1.array[strlen(globalBuff1_c.array)]=0; // GOOD
|
||||
globalBuff1.pointer[strlen(globalBuff1.array)]=0; // GOOD
|
||||
globalBuff2->array[strlen(globalBuff2_c->array)]=0; // GOOD
|
||||
globalBuff2->pointer[strlen(globalBuff2->array)]=0; // GOOD
|
||||
}
|
||||
void goodFunc0(){
|
||||
|
||||
void strlen_test3(){
|
||||
unsigned char buffer[12];
|
||||
int i;
|
||||
for(i = 0; i < 6; i++)
|
||||
|
||||
@@ -3275,11 +3275,11 @@
|
||||
| smart_pointer.cpp:51:30:51:50 | call to make_shared | smart_pointer.cpp:52:10:52:10 | p | |
|
||||
| smart_pointer.cpp:51:52:51:57 | call to source | smart_pointer.cpp:51:30:51:50 | call to make_shared | TAINT |
|
||||
| smart_pointer.cpp:52:10:52:10 | p | smart_pointer.cpp:52:12:52:14 | call to get | |
|
||||
| smart_pointer.cpp:52:12:52:14 | ref arg call to get | smart_pointer.cpp:52:10:52:10 | ref arg p | |
|
||||
| smart_pointer.cpp:52:12:52:14 | ref arg call to get | smart_pointer.cpp:52:10:52:10 | ref arg p | TAINT |
|
||||
| smart_pointer.cpp:56:30:56:50 | call to make_unique | smart_pointer.cpp:57:10:57:10 | p | |
|
||||
| smart_pointer.cpp:56:52:56:57 | call to source | smart_pointer.cpp:56:30:56:50 | call to make_unique | TAINT |
|
||||
| smart_pointer.cpp:57:10:57:10 | p | smart_pointer.cpp:57:12:57:14 | call to get | |
|
||||
| smart_pointer.cpp:57:12:57:14 | ref arg call to get | smart_pointer.cpp:57:10:57:10 | ref arg p | |
|
||||
| smart_pointer.cpp:57:12:57:14 | ref arg call to get | smart_pointer.cpp:57:10:57:10 | ref arg p | TAINT |
|
||||
| smart_pointer.cpp:65:28:65:46 | call to make_unique | smart_pointer.cpp:66:10:66:10 | p | |
|
||||
| smart_pointer.cpp:65:28:65:46 | call to make_unique | smart_pointer.cpp:67:10:67:10 | p | |
|
||||
| smart_pointer.cpp:65:48:65:53 | call to source | smart_pointer.cpp:65:28:65:46 | call to make_unique | TAINT |
|
||||
@@ -3319,7 +3319,7 @@
|
||||
| smart_pointer.cpp:87:3:87:3 | ref arg p | smart_pointer.cpp:89:8:89:8 | p | |
|
||||
| smart_pointer.cpp:87:3:87:17 | ... = ... | smart_pointer.cpp:87:6:87:6 | x [post update] | |
|
||||
| smart_pointer.cpp:87:3:87:17 | ... = ... | smart_pointer.cpp:88:11:88:11 | x | |
|
||||
| smart_pointer.cpp:87:4:87:4 | call to operator-> [post update] | smart_pointer.cpp:87:3:87:3 | ref arg p | |
|
||||
| smart_pointer.cpp:87:4:87:4 | call to operator-> [post update] | smart_pointer.cpp:87:3:87:3 | ref arg p | TAINT |
|
||||
| smart_pointer.cpp:87:10:87:15 | call to source | smart_pointer.cpp:87:3:87:17 | ... = ... | |
|
||||
| smart_pointer.cpp:88:8:88:8 | p | smart_pointer.cpp:88:9:88:9 | call to operator-> | |
|
||||
| smart_pointer.cpp:88:8:88:8 | ref arg p | smart_pointer.cpp:86:45:86:45 | p | |
|
||||
@@ -3333,7 +3333,7 @@
|
||||
| smart_pointer.cpp:91:3:91:3 | ref arg q | smart_pointer.cpp:94:8:94:8 | q | |
|
||||
| smart_pointer.cpp:91:3:91:20 | ... = ... | smart_pointer.cpp:91:9:91:9 | x [post update] | |
|
||||
| smart_pointer.cpp:91:3:91:20 | ... = ... | smart_pointer.cpp:92:14:92:14 | x | |
|
||||
| smart_pointer.cpp:91:4:91:4 | call to operator-> [post update] | smart_pointer.cpp:91:3:91:3 | ref arg q | |
|
||||
| smart_pointer.cpp:91:4:91:4 | call to operator-> [post update] | smart_pointer.cpp:91:3:91:3 | ref arg q | TAINT |
|
||||
| smart_pointer.cpp:91:13:91:18 | call to source | smart_pointer.cpp:91:3:91:20 | ... = ... | |
|
||||
| smart_pointer.cpp:92:8:92:8 | q | smart_pointer.cpp:92:9:92:9 | call to operator-> | |
|
||||
| smart_pointer.cpp:92:8:92:8 | ref arg q | smart_pointer.cpp:86:67:86:67 | q | |
|
||||
@@ -3351,21 +3351,21 @@
|
||||
| smart_pointer.cpp:102:25:102:50 | call to unique_ptr | smart_pointer.cpp:104:8:104:8 | p | |
|
||||
| smart_pointer.cpp:103:11:103:11 | p | smart_pointer.cpp:103:13:103:15 | call to get | |
|
||||
| smart_pointer.cpp:103:11:103:11 | ref arg p | smart_pointer.cpp:104:8:104:8 | p | |
|
||||
| smart_pointer.cpp:103:13:103:15 | ref arg call to get | smart_pointer.cpp:103:11:103:11 | ref arg p | |
|
||||
| smart_pointer.cpp:103:13:103:15 | ref arg call to get | smart_pointer.cpp:103:11:103:11 | ref arg p | TAINT |
|
||||
| smart_pointer.cpp:104:8:104:8 | p | smart_pointer.cpp:104:9:104:9 | call to operator-> | |
|
||||
| smart_pointer.cpp:112:40:112:42 | ptr | smart_pointer.cpp:112:40:112:42 | ptr | |
|
||||
| smart_pointer.cpp:112:40:112:42 | ptr | smart_pointer.cpp:113:2:113:4 | ptr | |
|
||||
| smart_pointer.cpp:113:2:113:4 | ptr | smart_pointer.cpp:113:5:113:5 | call to operator-> | |
|
||||
| smart_pointer.cpp:113:2:113:4 | ref arg ptr | smart_pointer.cpp:112:40:112:42 | ptr | |
|
||||
| smart_pointer.cpp:113:2:113:18 | ... = ... | smart_pointer.cpp:113:7:113:7 | x [post update] | |
|
||||
| smart_pointer.cpp:113:5:113:5 | call to operator-> [post update] | smart_pointer.cpp:113:2:113:4 | ref arg ptr | |
|
||||
| smart_pointer.cpp:113:5:113:5 | call to operator-> [post update] | smart_pointer.cpp:113:2:113:4 | ref arg ptr | TAINT |
|
||||
| smart_pointer.cpp:113:11:113:16 | call to source | smart_pointer.cpp:113:2:113:18 | ... = ... | |
|
||||
| smart_pointer.cpp:116:52:116:54 | ptr | smart_pointer.cpp:116:52:116:54 | ptr | |
|
||||
| smart_pointer.cpp:116:52:116:54 | ptr | smart_pointer.cpp:117:2:117:4 | ptr | |
|
||||
| smart_pointer.cpp:117:2:117:4 | ptr | smart_pointer.cpp:117:5:117:5 | call to operator-> | |
|
||||
| smart_pointer.cpp:117:2:117:4 | ref arg ptr | smart_pointer.cpp:116:52:116:54 | ptr | |
|
||||
| smart_pointer.cpp:117:2:117:18 | ... = ... | smart_pointer.cpp:117:7:117:7 | x [post update] | |
|
||||
| smart_pointer.cpp:117:5:117:5 | call to operator-> [post update] | smart_pointer.cpp:117:2:117:4 | ref arg ptr | |
|
||||
| smart_pointer.cpp:117:5:117:5 | call to operator-> [post update] | smart_pointer.cpp:117:2:117:4 | ref arg ptr | TAINT |
|
||||
| smart_pointer.cpp:117:11:117:16 | call to source | smart_pointer.cpp:117:2:117:18 | ... = ... | |
|
||||
| smart_pointer.cpp:120:48:120:50 | ptr | smart_pointer.cpp:120:48:120:50 | ptr | |
|
||||
| smart_pointer.cpp:120:48:120:50 | ptr | smart_pointer.cpp:121:4:121:6 | ptr | |
|
||||
@@ -3386,12 +3386,12 @@
|
||||
| smart_pointer.cpp:125:18:125:19 | ref arg p1 | smart_pointer.cpp:124:48:124:49 | p1 | |
|
||||
| smart_pointer.cpp:125:18:125:19 | ref arg p1 | smart_pointer.cpp:126:8:126:9 | p1 | |
|
||||
| smart_pointer.cpp:125:18:125:22 | ref arg call to shared_ptr | smart_pointer.cpp:125:22:125:22 | q [inner post update] | |
|
||||
| smart_pointer.cpp:125:20:125:20 | call to operator-> [post update] | smart_pointer.cpp:125:18:125:19 | ref arg p1 | |
|
||||
| smart_pointer.cpp:125:20:125:20 | call to operator-> [post update] | smart_pointer.cpp:125:18:125:19 | ref arg p1 | TAINT |
|
||||
| smart_pointer.cpp:125:22:125:22 | q | smart_pointer.cpp:125:18:125:22 | call to shared_ptr | |
|
||||
| smart_pointer.cpp:125:22:125:22 | ref arg q | smart_pointer.cpp:125:22:125:22 | q [inner post update] | |
|
||||
| smart_pointer.cpp:126:8:126:9 | p1 | smart_pointer.cpp:126:10:126:10 | call to operator-> | |
|
||||
| smart_pointer.cpp:126:8:126:9 | ref arg p1 | smart_pointer.cpp:124:48:124:49 | p1 | |
|
||||
| smart_pointer.cpp:126:10:126:10 | call to operator-> [post update] | smart_pointer.cpp:126:8:126:9 | ref arg p1 | |
|
||||
| smart_pointer.cpp:126:10:126:10 | call to operator-> [post update] | smart_pointer.cpp:126:8:126:9 | ref arg p1 | TAINT |
|
||||
| smart_pointer.cpp:126:12:126:12 | q | smart_pointer.cpp:126:13:126:13 | call to operator-> | |
|
||||
| smart_pointer.cpp:128:13:128:13 | call to operator* | smart_pointer.cpp:128:13:128:15 | call to shared_ptr | TAINT |
|
||||
| smart_pointer.cpp:128:13:128:13 | ref arg call to operator* | smart_pointer.cpp:124:90:124:91 | p2 | |
|
||||
@@ -3422,10 +3422,10 @@
|
||||
| smart_pointer.cpp:133:23:133:24 | p1 | smart_pointer.cpp:133:25:133:25 | call to operator-> | |
|
||||
| smart_pointer.cpp:133:23:133:24 | ref arg p1 | smart_pointer.cpp:132:53:132:54 | p1 | |
|
||||
| smart_pointer.cpp:133:23:133:24 | ref arg p1 | smart_pointer.cpp:134:8:134:9 | p1 | |
|
||||
| smart_pointer.cpp:133:25:133:25 | call to operator-> [post update] | smart_pointer.cpp:133:23:133:24 | ref arg p1 | |
|
||||
| smart_pointer.cpp:133:25:133:25 | call to operator-> [post update] | smart_pointer.cpp:133:23:133:24 | ref arg p1 | TAINT |
|
||||
| smart_pointer.cpp:134:8:134:9 | p1 | smart_pointer.cpp:134:10:134:10 | call to operator-> | |
|
||||
| smart_pointer.cpp:134:8:134:9 | ref arg p1 | smart_pointer.cpp:132:53:132:54 | p1 | |
|
||||
| smart_pointer.cpp:134:10:134:10 | call to operator-> [post update] | smart_pointer.cpp:134:8:134:9 | ref arg p1 | |
|
||||
| smart_pointer.cpp:134:10:134:10 | call to operator-> [post update] | smart_pointer.cpp:134:8:134:9 | ref arg p1 | TAINT |
|
||||
| smart_pointer.cpp:134:12:134:12 | q | smart_pointer.cpp:134:13:134:13 | call to operator-> | |
|
||||
| smart_pointer.cpp:136:17:136:17 | ref arg call to operator* | smart_pointer.cpp:132:95:132:96 | p2 | |
|
||||
| smart_pointer.cpp:136:17:136:17 | ref arg call to operator* | smart_pointer.cpp:136:18:136:19 | p2 [inner post update] | |
|
||||
|
||||
@@ -189,3 +189,30 @@ int *&conversionInFlow() {
|
||||
int *&pRef = p; // has conversion in the middle of data flow
|
||||
return pRef; // BAD [NOT DETECTED]
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template<typename T>
|
||||
class shared_ptr {
|
||||
public:
|
||||
shared_ptr() noexcept;
|
||||
explicit shared_ptr(T*);
|
||||
shared_ptr(const shared_ptr&) noexcept;
|
||||
template<class U> shared_ptr(const shared_ptr<U>&) noexcept;
|
||||
template<class U> shared_ptr(shared_ptr<U>&&) noexcept;
|
||||
|
||||
shared_ptr<T>& operator=(const shared_ptr<T>&) noexcept;
|
||||
shared_ptr<T>& operator=(shared_ptr<T>&&) noexcept;
|
||||
|
||||
T& operator*() const noexcept;
|
||||
T* operator->() const noexcept;
|
||||
|
||||
T* get() const noexcept;
|
||||
};
|
||||
}
|
||||
|
||||
auto make_read_port()
|
||||
{
|
||||
auto port = std::shared_ptr<int>(new int);
|
||||
auto ptr = port.get();
|
||||
return ptr; // GOOD
|
||||
}
|
||||
@@ -4,5 +4,4 @@
|
||||
| test.c:59:3:59:5 | sc6 | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:58:9:58:16 | 127 | Extreme value |
|
||||
| test.c:63:3:63:5 | sc8 | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:62:9:62:16 | - ... | Extreme value |
|
||||
| test.c:75:3:75:5 | sc1 | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:74:9:74:16 | 127 | Extreme value |
|
||||
| test.c:76:3:76:5 | sc1 | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:74:9:74:16 | 127 | Extreme value |
|
||||
| test.c:124:9:124:9 | x | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:118:17:118:23 | 2147483647 | Extreme value |
|
||||
|
||||
@@ -73,7 +73,7 @@ void test_negatives() {
|
||||
|
||||
sc1 = CHAR_MAX;
|
||||
sc1 += 0; // GOOD [FALSE POSITIVE]
|
||||
sc1 += -1; // GOOD [FALSE POSITIVE]
|
||||
sc1 += -1; // GOOD
|
||||
sc2 = CHAR_MIN;
|
||||
sc2 += -1; // BAD [NOT DETECTED]
|
||||
sc3 = CHAR_MIN;
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
| test2.cpp:14:11:14:11 | v | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test2.cpp:25:22:25:23 | & ... | User-provided value |
|
||||
| test2.cpp:14:11:14:11 | v | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test2.cpp:25:22:25:23 | & ... | User-provided value |
|
||||
| test3.c:15:10:15:10 | x | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test3.c:11:15:11:18 | argv | User-provided value |
|
||||
| test3.c:15:14:15:14 | y | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test3.c:11:15:11:18 | argv | User-provided value |
|
||||
| test3.c:15:18:15:18 | z | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test3.c:11:15:11:18 | argv | User-provided value |
|
||||
| test5.cpp:17:6:17:18 | call to getTaintedInt | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test5.cpp:9:7:9:9 | buf | User-provided value |
|
||||
| test5.cpp:19:6:19:6 | y | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test5.cpp:9:7:9:9 | buf | User-provided value |
|
||||
| test5.cpp:19:6:19:6 | y | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test5.cpp:9:7:9:9 | buf | User-provided value |
|
||||
|
||||
2
csharp/change-notes/2021-04-14-customizations.md
Normal file
2
csharp/change-notes/2021-04-14-customizations.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* A new library, `Customizations.qll`, has been added, which allows for global customizations that affect all queries.
|
||||
@@ -193,6 +193,16 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
return Default.CreateGenerated(cx, parent, childIndex, location, parameter.Type.IsReferenceType ? ValueAsString(null) : null);
|
||||
}
|
||||
|
||||
if (parameter.Type.SpecialType == SpecialType.System_Object)
|
||||
{
|
||||
// this can happen in VB.NET
|
||||
cx.ExtractionError($"Extracting default argument value 'object {parameter.Name} = default' instead of 'object {parameter.Name} = {defaultValue}'. The latter is not supported in C#.",
|
||||
null, null, severity: Util.Logging.Severity.Warning);
|
||||
|
||||
// we're generating a default expression:
|
||||
return Default.CreateGenerated(cx, parent, childIndex, location, ValueAsString(null));
|
||||
}
|
||||
|
||||
// const literal:
|
||||
return Literal.CreateGenerated(cx, parent, childIndex, parameter.Type, defaultValue, location);
|
||||
}
|
||||
|
||||
@@ -18,36 +18,6 @@ import csharp
|
||||
import Dispose
|
||||
import semmle.code.csharp.frameworks.System
|
||||
|
||||
/**
|
||||
* Gets an exception type that may be thrown during the execution of method `m`.
|
||||
* Assumes any exception may be thrown by library types.
|
||||
*/
|
||||
Class getAThrownException(Method m) {
|
||||
m.fromLibrary() and
|
||||
result = any(SystemExceptionClass sc)
|
||||
or
|
||||
exists(ControlFlowElement cfe |
|
||||
cfe = any(ThrowElement te | result = te.getExpr().getType()) or
|
||||
cfe = any(MethodCall mc | result = getAThrownException(mc.getARuntimeTarget()))
|
||||
|
|
||||
cfe.getEnclosingCallable() = m and
|
||||
not isTriedAgainstException(cfe, result)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if control flow element is tried against throwing an exception of type
|
||||
* `ec`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
predicate isTriedAgainstException(ControlFlowElement cfe, ExceptionClass ec) {
|
||||
(cfe instanceof ThrowElement or cfe instanceof MethodCall) and
|
||||
exists(TryStmt ts |
|
||||
ts.getATriedElement() = cfe and
|
||||
exists(ts.getAnExceptionHandler(ec))
|
||||
)
|
||||
}
|
||||
|
||||
private class DisposeCall extends MethodCall {
|
||||
DisposeCall() { this.getTarget() instanceof DisposeMethod }
|
||||
}
|
||||
@@ -78,8 +48,17 @@ predicate disposeReachableFromDisposableCreation(DisposeCall disposeCall, Expr d
|
||||
reachesDisposeCall(disposeCall, DataFlow::exprNode(disposableCreation))
|
||||
}
|
||||
|
||||
class MethodCallThatMayThrow extends MethodCall {
|
||||
MethodCallThatMayThrow() { exists(getAThrownException(this.getARuntimeTarget())) }
|
||||
/**
|
||||
* Holds if control flow element is tried against throwing an exception of type
|
||||
* `ec`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
predicate isTriedAgainstException(ControlFlowElement cfe, ExceptionClass ec) {
|
||||
(cfe instanceof ThrowElement or cfe instanceof MethodCall) and
|
||||
exists(TryStmt ts |
|
||||
ts.getATriedElement() = cfe and
|
||||
exists(ts.getAnExceptionHandler(ec))
|
||||
)
|
||||
}
|
||||
|
||||
ControlFlowElement getACatchOrFinallyClauseChild() {
|
||||
@@ -88,15 +67,71 @@ ControlFlowElement getACatchOrFinallyClauseChild() {
|
||||
result = getACatchOrFinallyClauseChild().getAChild()
|
||||
}
|
||||
|
||||
from DisposeCall disposeCall, Expr disposableCreation, MethodCallThatMayThrow callThatThrows
|
||||
where
|
||||
private predicate candidate(DisposeCall disposeCall, Call call, Expr disposableCreation) {
|
||||
disposeReachableFromDisposableCreation(disposeCall, disposableCreation) and
|
||||
// The dispose call is not, itself, within a dispose method.
|
||||
not disposeCall.getEnclosingCallable() instanceof DisposeMethod and
|
||||
// Dispose call not within a finally or catch block
|
||||
not getACatchOrFinallyClauseChild() = disposeCall and
|
||||
// At least one method call exists between the allocation and disposal that could throw
|
||||
disposableCreation.getAReachableElement() = callThatThrows and
|
||||
callThatThrows.getAReachableElement() = disposeCall
|
||||
disposableCreation.getAReachableElement() = call and
|
||||
call.getAReachableElement() = disposeCall
|
||||
}
|
||||
|
||||
private class RelevantMethod extends Method {
|
||||
RelevantMethod() {
|
||||
exists(Call call |
|
||||
candidate(_, call, _) and
|
||||
this = call.getARuntimeTarget()
|
||||
)
|
||||
or
|
||||
exists(RelevantMethod other | other.calls(this))
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private RelevantMethod callsNoTry() {
|
||||
exists(MethodCall mc |
|
||||
result = mc.getARuntimeTarget() and
|
||||
not isTriedAgainstException(mc, _) and
|
||||
mc.getEnclosingCallable() = this
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private RelevantMethod callsInTry(MethodCall mc) {
|
||||
result = mc.getARuntimeTarget() and
|
||||
isTriedAgainstException(mc, _) and
|
||||
mc.getEnclosingCallable() = this
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an exception type that may be thrown during the execution of this method.
|
||||
* Assumes any exception may be thrown by library types.
|
||||
*/
|
||||
Class getAThrownException() {
|
||||
this.fromLibrary() and
|
||||
result instanceof SystemExceptionClass
|
||||
or
|
||||
exists(ControlFlowElement cfe |
|
||||
result = cfe.(ThrowElement).getExpr().getType() and
|
||||
cfe.getEnclosingCallable() = this
|
||||
or
|
||||
result = this.callsInTry(cfe).getAThrownException()
|
||||
|
|
||||
not isTriedAgainstException(cfe, result)
|
||||
)
|
||||
or
|
||||
result = this.callsNoTry().getAThrownException()
|
||||
}
|
||||
}
|
||||
|
||||
class MethodCallThatMayThrow extends MethodCall {
|
||||
MethodCallThatMayThrow() {
|
||||
exists(this.getARuntimeTarget().(RelevantMethod).getAThrownException())
|
||||
}
|
||||
}
|
||||
|
||||
from DisposeCall disposeCall, Expr disposableCreation, MethodCallThatMayThrow callThatThrows
|
||||
where candidate(disposeCall, callThatThrows, disposableCreation)
|
||||
select disposeCall, "Dispose missed if exception is thrown by $@.", callThatThrows,
|
||||
callThatThrows.toString()
|
||||
|
||||
12
csharp/ql/src/Customizations.qll
Normal file
12
csharp/ql/src/Customizations.qll
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Contains customizations to the standard library.
|
||||
*
|
||||
* This module is imported by `csharp.qll`, so any customizations defined here automatically
|
||||
* apply to all queries.
|
||||
*
|
||||
* Typical examples of customizations include adding new subclasses of abstract classes such as
|
||||
* the `RemoteFlowSource` and `SummarizedCallable` classes associated with the security queries
|
||||
* to model frameworks that are not covered by the standard library.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
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())
|
||||
@@ -2,6 +2,7 @@
|
||||
* The default C# QL library.
|
||||
*/
|
||||
|
||||
import Customizations
|
||||
import semmle.code.csharp.Attribute
|
||||
import semmle.code.csharp.Callable
|
||||
import semmle.code.csharp.Comments
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -49,6 +49,7 @@ module Stages {
|
||||
|
||||
cached
|
||||
module DataFlowStage {
|
||||
private import semmle.code.csharp.dataflow.internal.DataFlowDispatch
|
||||
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.csharp.dataflow.internal.DataFlowImplCommon
|
||||
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
|
||||
@@ -78,6 +79,8 @@ module Stages {
|
||||
or
|
||||
exists(CallContext cc)
|
||||
or
|
||||
exists(any(DataFlowCall c).getEnclosingCallable())
|
||||
or
|
||||
forceCachingInSameStageRev()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -4,6 +4,7 @@ private import dotnet
|
||||
private import DataFlowPublic
|
||||
private import DataFlowPrivate
|
||||
private import FlowSummaryImpl as FlowSummaryImpl
|
||||
private import semmle.code.csharp.Caching
|
||||
private import semmle.code.csharp.dataflow.FlowSummary
|
||||
private import semmle.code.csharp.dispatch.Dispatch
|
||||
private import semmle.code.csharp.frameworks.system.Collections
|
||||
@@ -69,8 +70,6 @@ private predicate transitiveCapturedCallTarget(ControlFlow::Nodes::ElementNode c
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
private import semmle.code.csharp.Caching
|
||||
|
||||
cached
|
||||
newtype TReturnKind =
|
||||
TNormalReturnKind() { Stages::DataFlowStage::forceCachingInSameStage() } or
|
||||
@@ -247,6 +246,7 @@ abstract class DataFlowCall extends TDataFlowCall {
|
||||
abstract DataFlow::Node getNode();
|
||||
|
||||
/** Gets the enclosing callable of this call. */
|
||||
cached
|
||||
abstract DataFlowCallable getEnclosingCallable();
|
||||
|
||||
/** Gets the underlying expression, if any. */
|
||||
@@ -280,7 +280,10 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall {
|
||||
|
||||
override DataFlow::ExprNode getNode() { result.getControlFlowNode() = cfn }
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() { result = cfn.getEnclosingCallable() }
|
||||
override DataFlowCallable getEnclosingCallable() {
|
||||
Stages::DataFlowStage::forceCachingInSameStage() and
|
||||
result = cfn.getEnclosingCallable()
|
||||
}
|
||||
|
||||
override string toString() { result = cfn.toString() }
|
||||
|
||||
|
||||
@@ -21,9 +21,11 @@ private import semmle.code.csharp.frameworks.system.threading.Tasks
|
||||
|
||||
abstract class NodeImpl extends Node {
|
||||
/** Do not call: use `getEnclosingCallable()` instead. */
|
||||
cached
|
||||
abstract DataFlowCallable getEnclosingCallableImpl();
|
||||
|
||||
/** Do not call: use `getType()` instead. */
|
||||
cached
|
||||
abstract DotNet::Type getTypeImpl();
|
||||
|
||||
/** Gets the type of this node used for type pruning. */
|
||||
@@ -39,27 +41,39 @@ abstract class NodeImpl extends Node {
|
||||
}
|
||||
|
||||
/** Do not call: use `getControlFlowNode()` instead. */
|
||||
cached
|
||||
abstract ControlFlow::Node getControlFlowNodeImpl();
|
||||
|
||||
/** Do not call: use `getLocation()` instead. */
|
||||
cached
|
||||
abstract Location getLocationImpl();
|
||||
|
||||
/** Do not call: use `toString()` instead. */
|
||||
cached
|
||||
abstract string toStringImpl();
|
||||
}
|
||||
|
||||
private class ExprNodeImpl extends ExprNode, NodeImpl {
|
||||
override DataFlowCallable getEnclosingCallableImpl() {
|
||||
Stages::DataFlowStage::forceCachingInSameStage() and
|
||||
result = this.getExpr().getEnclosingCallable()
|
||||
}
|
||||
|
||||
override DotNet::Type getTypeImpl() { result = this.getExpr().getType() }
|
||||
override DotNet::Type getTypeImpl() {
|
||||
Stages::DataFlowStage::forceCachingInSameStage() and
|
||||
result = this.getExpr().getType()
|
||||
}
|
||||
|
||||
override ControlFlow::Nodes::ElementNode getControlFlowNodeImpl() { this = TExprNode(result) }
|
||||
override ControlFlow::Nodes::ElementNode getControlFlowNodeImpl() {
|
||||
Stages::DataFlowStage::forceCachingInSameStage() and this = TExprNode(result)
|
||||
}
|
||||
|
||||
override Location getLocationImpl() { result = this.getExpr().getLocation() }
|
||||
override Location getLocationImpl() {
|
||||
Stages::DataFlowStage::forceCachingInSameStage() and result = this.getExpr().getLocation()
|
||||
}
|
||||
|
||||
override string toStringImpl() {
|
||||
Stages::DataFlowStage::forceCachingInSameStage() and
|
||||
result = this.getControlFlowNode().toString()
|
||||
or
|
||||
exists(CIL::Expr e |
|
||||
@@ -967,6 +981,16 @@ private module Cached {
|
||||
or
|
||||
n.asExpr() = any(WithExpr we).getInitializer()
|
||||
}
|
||||
|
||||
cached
|
||||
predicate parameterNode(Node n, DataFlowCallable c, int i) {
|
||||
n.(ParameterNodeImpl).isParameterOf(c, i)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate argumentNode(Node n, DataFlowCall call, int pos) {
|
||||
n.(ArgumentNodeImpl).argumentOf(call, pos)
|
||||
}
|
||||
}
|
||||
|
||||
import Cached
|
||||
@@ -992,8 +1016,6 @@ class SsaDefinitionNode extends NodeImpl, TSsaDefinitionNode {
|
||||
}
|
||||
|
||||
abstract class ParameterNodeImpl extends NodeImpl {
|
||||
abstract DotNet::Parameter getParameter();
|
||||
|
||||
abstract predicate isParameterOf(DataFlowCallable c, int i);
|
||||
}
|
||||
|
||||
@@ -1010,11 +1032,9 @@ private module ParameterNodes {
|
||||
/** Gets the SSA definition corresponding to this parameter, if any. */
|
||||
Ssa::ExplicitDefinition getSsaDefinition() {
|
||||
result.getADefinition().(AssignableDefinitions::ImplicitParameterDefinition).getParameter() =
|
||||
this.getParameter()
|
||||
parameter
|
||||
}
|
||||
|
||||
override DotNet::Parameter getParameter() { result = parameter }
|
||||
|
||||
override predicate isParameterOf(DataFlowCallable c, int i) { c.getParameter(i) = parameter }
|
||||
|
||||
override DataFlowCallable getEnclosingCallableImpl() { result = parameter.getCallable() }
|
||||
@@ -1037,8 +1057,6 @@ private module ParameterNodes {
|
||||
/** Gets the callable containing this implicit instance parameter. */
|
||||
Callable getCallable() { result = callable }
|
||||
|
||||
override DotNet::Parameter getParameter() { none() }
|
||||
|
||||
override predicate isParameterOf(DataFlowCallable c, int pos) { callable = c and pos = -1 }
|
||||
|
||||
override DataFlowCallable getEnclosingCallableImpl() { result = callable }
|
||||
@@ -1113,8 +1131,6 @@ private module ParameterNodes {
|
||||
/** Gets the captured variable that this implicit parameter models. */
|
||||
LocalScopeVariable getVariable() { result = def.getVariable() }
|
||||
|
||||
override DotNet::Parameter getParameter() { none() }
|
||||
|
||||
override predicate isParameterOf(DataFlowCallable c, int i) {
|
||||
i = getParameterPosition(def) and
|
||||
c = this.getEnclosingCallable()
|
||||
@@ -1125,13 +1141,15 @@ private module ParameterNodes {
|
||||
import ParameterNodes
|
||||
|
||||
/** A data-flow node that represents a call argument. */
|
||||
abstract class ArgumentNode extends Node {
|
||||
/** Holds if this argument occurs at the given position in the given call. */
|
||||
cached
|
||||
abstract predicate argumentOf(DataFlowCall call, int pos);
|
||||
class ArgumentNode extends Node {
|
||||
ArgumentNode() { argumentNode(this, _, _) }
|
||||
|
||||
/** Gets the call in which this node is an argument. */
|
||||
final DataFlowCall getCall() { this.argumentOf(result, _) }
|
||||
/** Holds if this argument occurs at the given position in the given call. */
|
||||
final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) }
|
||||
}
|
||||
|
||||
abstract private class ArgumentNodeImpl extends Node {
|
||||
abstract predicate argumentOf(DataFlowCall call, int pos);
|
||||
}
|
||||
|
||||
private module ArgumentNodes {
|
||||
@@ -1149,7 +1167,7 @@ private module ArgumentNodes {
|
||||
}
|
||||
|
||||
/** A data-flow node that represents an explicit call argument. */
|
||||
class ExplicitArgumentNode extends ArgumentNode {
|
||||
class ExplicitArgumentNode extends ArgumentNodeImpl {
|
||||
ExplicitArgumentNode() {
|
||||
this.asExpr() instanceof Argument
|
||||
or
|
||||
@@ -1157,7 +1175,6 @@ private module ArgumentNodes {
|
||||
}
|
||||
|
||||
override predicate argumentOf(DataFlowCall call, int pos) {
|
||||
Stages::DataFlowStage::forceCachingInSameStage() and
|
||||
exists(ArgumentConfiguration x, Expr c, Argument arg |
|
||||
arg = this.asExpr() and
|
||||
c = call.getExpr() and
|
||||
@@ -1189,7 +1206,8 @@ private module ArgumentNodes {
|
||||
* } }
|
||||
* ```
|
||||
*/
|
||||
class ImplicitCapturedArgumentNode extends ArgumentNode, NodeImpl, TImplicitCapturedArgumentNode {
|
||||
class ImplicitCapturedArgumentNode extends ArgumentNodeImpl, NodeImpl,
|
||||
TImplicitCapturedArgumentNode {
|
||||
private LocalScopeVariable v;
|
||||
private ControlFlow::Nodes::ElementNode cfn;
|
||||
|
||||
@@ -1231,7 +1249,7 @@ private module ArgumentNodes {
|
||||
* A node that corresponds to the value of an object creation (`new C()`) before
|
||||
* the constructor has run.
|
||||
*/
|
||||
class MallocNode extends ArgumentNode, NodeImpl, TMallocNode {
|
||||
class MallocNode extends ArgumentNodeImpl, NodeImpl, TMallocNode {
|
||||
private ControlFlow::Nodes::ElementNode cfn;
|
||||
|
||||
MallocNode() { this = TMallocNode(cfn) }
|
||||
@@ -1266,7 +1284,7 @@ private module ArgumentNodes {
|
||||
* and that argument is itself a compatible array, for example
|
||||
* `Foo(new[] { "a", "b", "c" })`.
|
||||
*/
|
||||
class ParamsArgumentNode extends ArgumentNode, NodeImpl, TParamsArgumentNode {
|
||||
class ParamsArgumentNode extends ArgumentNodeImpl, NodeImpl, TParamsArgumentNode {
|
||||
private ControlFlow::Node callCfn;
|
||||
|
||||
ParamsArgumentNode() { this = TParamsArgumentNode(callCfn) }
|
||||
@@ -1291,7 +1309,7 @@ private module ArgumentNodes {
|
||||
override string toStringImpl() { result = "[implicit array creation] " + callCfn }
|
||||
}
|
||||
|
||||
private class SummaryArgumentNode extends SummaryNode, ArgumentNode {
|
||||
private class SummaryArgumentNode extends SummaryNode, ArgumentNodeImpl {
|
||||
private DataFlowCall c;
|
||||
private int i;
|
||||
|
||||
@@ -1324,10 +1342,7 @@ private module ReturnNodes {
|
||||
)
|
||||
}
|
||||
|
||||
override NormalReturnKind getKind() {
|
||||
any(DotNet::Callable c).canReturn(this.getExpr()) and
|
||||
exists(result)
|
||||
}
|
||||
override NormalReturnKind getKind() { exists(result) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1744,7 +1759,10 @@ class DataFlowType extends Gvn::GvnType {
|
||||
}
|
||||
|
||||
/** Gets the type of `n` used for type pruning. */
|
||||
DataFlowType getNodeType(NodeImpl n) { result = n.getDataFlowType() }
|
||||
pragma[inline]
|
||||
Gvn::GvnType getNodeType(NodeImpl n) {
|
||||
pragma[only_bind_into](result) = pragma[only_bind_out](n).getDataFlowType()
|
||||
}
|
||||
|
||||
/** Gets a string representation of a `DataFlowType`. */
|
||||
string ppReprType(DataFlowType t) { result = t.toString() }
|
||||
@@ -1819,7 +1837,8 @@ private module PostUpdateNodes {
|
||||
* Such a node acts as both a post-update node for the `MallocNode`, as well as
|
||||
* a pre-update node for the `ObjectCreationNode`.
|
||||
*/
|
||||
class ObjectInitializerNode extends PostUpdateNode, NodeImpl, ArgumentNode, TObjectInitializerNode {
|
||||
class ObjectInitializerNode extends PostUpdateNode, NodeImpl, ArgumentNodeImpl,
|
||||
TObjectInitializerNode {
|
||||
private ObjectCreation oc;
|
||||
private ControlFlow::Nodes::ElementNode cfn;
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ private import cil
|
||||
private import dotnet
|
||||
private import DataFlowDispatch
|
||||
private import DataFlowPrivate
|
||||
private import semmle.code.csharp.Caching
|
||||
private import semmle.code.csharp.controlflow.Guards
|
||||
private import semmle.code.csharp.Unification
|
||||
|
||||
@@ -38,38 +37,21 @@ class Node extends TNode {
|
||||
}
|
||||
|
||||
/** Gets the type of this node. */
|
||||
cached
|
||||
final DotNet::Type getType() {
|
||||
Stages::DataFlowStage::forceCachingInSameStage() and result = this.(NodeImpl).getTypeImpl()
|
||||
}
|
||||
final DotNet::Type getType() { result = this.(NodeImpl).getTypeImpl() }
|
||||
|
||||
/** Gets the enclosing callable of this node. */
|
||||
cached
|
||||
final DataFlowCallable getEnclosingCallable() {
|
||||
Stages::DataFlowStage::forceCachingInSameStage() and
|
||||
result = this.(NodeImpl).getEnclosingCallableImpl()
|
||||
}
|
||||
|
||||
/** Gets the control flow node corresponding to this node, if any. */
|
||||
cached
|
||||
final ControlFlow::Node getControlFlowNode() {
|
||||
Stages::DataFlowStage::forceCachingInSameStage() and
|
||||
result = this.(NodeImpl).getControlFlowNodeImpl()
|
||||
}
|
||||
final ControlFlow::Node getControlFlowNode() { result = this.(NodeImpl).getControlFlowNodeImpl() }
|
||||
|
||||
/** Gets a textual representation of this node. */
|
||||
cached
|
||||
final string toString() {
|
||||
Stages::DataFlowStage::forceCachingInSameStage() and
|
||||
result = this.(NodeImpl).toStringImpl()
|
||||
}
|
||||
final string toString() { result = this.(NodeImpl).toStringImpl() }
|
||||
|
||||
/** Gets the location of this node. */
|
||||
cached
|
||||
final Location getLocation() {
|
||||
Stages::DataFlowStage::forceCachingInSameStage() and
|
||||
result = this.(NodeImpl).getLocationImpl()
|
||||
}
|
||||
final Location getLocation() { result = this.(NodeImpl).getLocationImpl() }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
@@ -81,7 +63,7 @@ class Node extends TNode {
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,18 +99,18 @@ class ExprNode extends Node {
|
||||
* flow graph.
|
||||
*/
|
||||
class ParameterNode extends Node {
|
||||
private ParameterNodeImpl p;
|
||||
|
||||
ParameterNode() { this = p }
|
||||
ParameterNode() { parameterNode(this, _, _) }
|
||||
|
||||
/** Gets the parameter corresponding to this node, if any. */
|
||||
DotNet::Parameter getParameter() { result = p.getParameter() }
|
||||
DotNet::Parameter getParameter() {
|
||||
exists(DataFlowCallable c, int i | this.isParameterOf(c, i) and result = c.getParameter(i))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this node is the parameter of callable `c` at the specified
|
||||
* (zero-based) position.
|
||||
*/
|
||||
predicate isParameterOf(DataFlowCallable c, int i) { p.isParameterOf(c, i) }
|
||||
predicate isParameterOf(DataFlowCallable c, int i) { parameterNode(this, c, i) }
|
||||
}
|
||||
|
||||
/** A definition, viewed as a node in a data flow graph. */
|
||||
@@ -166,6 +148,7 @@ predicate localFlowStep = localFlowStepImpl/2;
|
||||
* Holds if data flows from `source` to `sink` in zero or more local
|
||||
* (intra-procedural) steps.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
|
||||
|
||||
/**
|
||||
|
||||
@@ -268,56 +268,146 @@ private module CallGraph {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple flow step that does not take flow through fields or flow out
|
||||
* of callables into account.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate delegateFlowStep(Expr pred, Expr succ) {
|
||||
Steps::stepClosed(pred, succ)
|
||||
or
|
||||
exists(Call call, Callable callable |
|
||||
callable.getUnboundDeclaration().canReturn(pred) and
|
||||
call = succ
|
||||
|
|
||||
callable = call.getTarget() or
|
||||
callable = call.getTarget().(Method).getAnOverrider+() or
|
||||
callable = call.getTarget().(Method).getAnUltimateImplementor() or
|
||||
callable = getARuntimeDelegateTarget(call, false)
|
||||
)
|
||||
or
|
||||
pred = succ.(DelegateCreation).getArgument()
|
||||
or
|
||||
exists(AssignableDefinition def, Assignable a |
|
||||
a instanceof Field or
|
||||
a instanceof Property
|
||||
|
|
||||
a = def.getTarget() and
|
||||
succ.(AssignableRead) = a.getAnAccess() and
|
||||
pred = def.getSource()
|
||||
)
|
||||
or
|
||||
exists(AddEventExpr ae | succ.(EventAccess).getTarget() = ae.getTarget() |
|
||||
pred = ae.getRValue()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate reachesDelegateCall(Expr e) {
|
||||
delegateCall(_, e, _)
|
||||
private predicate delegateCreationReaches(Callable c, Expr e) {
|
||||
delegateCreation(e, c, _)
|
||||
or
|
||||
exists(Expr mid | reachesDelegateCall(mid) | delegateFlowStep(e, mid))
|
||||
exists(Expr mid |
|
||||
delegateCreationReaches(c, mid) and
|
||||
delegateFlowStep(mid, e)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate delegateFlowStepReaches(Expr pred, Expr succ) {
|
||||
delegateFlowStep(pred, succ) and
|
||||
reachesDelegateCall(succ)
|
||||
private predicate reachesDelegateCall(Expr e, Call c, boolean libraryDelegateCall) {
|
||||
delegateCall(c, e, libraryDelegateCall)
|
||||
or
|
||||
exists(Expr mid |
|
||||
reachesDelegateCall(mid, c, libraryDelegateCall) and
|
||||
delegateFlowStep(e, mid)
|
||||
)
|
||||
}
|
||||
|
||||
private Expr delegateCallSource(Callable c) {
|
||||
delegateCreation(result, c, _)
|
||||
or
|
||||
delegateFlowStepReaches(delegateCallSource(c), result)
|
||||
/**
|
||||
* A "busy" flow element, that is, a node in the data-flow graph that typically
|
||||
* has a large fan-in or a large fan-out (or both).
|
||||
*
|
||||
* For such busy elements, we do not track flow directly from all delegate
|
||||
* creations, but instead we first perform a flow analysis between busy elements,
|
||||
* and then only in the end join up with delegate creations and delegate calls.
|
||||
*/
|
||||
abstract private class BusyFlowElement extends Element {
|
||||
pragma[nomagic]
|
||||
abstract Expr getAnInput();
|
||||
|
||||
pragma[nomagic]
|
||||
abstract Expr getAnOutput();
|
||||
|
||||
/** Holds if this element can be reached from expression `e`. */
|
||||
pragma[nomagic]
|
||||
private predicate exprReaches(Expr e) {
|
||||
this.reachesCall(_) and
|
||||
e = this.getAnInput()
|
||||
or
|
||||
exists(Expr mid |
|
||||
this.exprReaches(mid) and
|
||||
delegateFlowStep(e, mid)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this element can reach a delegate call `c` directly without
|
||||
* passing through another busy element.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate delegateCall(Call c, boolean libraryDelegateCall) {
|
||||
reachesDelegateCall(this.getAnOutput(), c, libraryDelegateCall)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private BusyFlowElement getASuccessor() { result.exprReaches(this.getAnOutput()) }
|
||||
|
||||
/**
|
||||
* Holds if this element reaches another busy element `other`,
|
||||
* which can reach a delegate call directly without passing
|
||||
* through another busy element.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate reachesCall(BusyFlowElement other) {
|
||||
this = other and
|
||||
other.delegateCall(_, _)
|
||||
or
|
||||
this.getASuccessor().reachesCall(other)
|
||||
}
|
||||
|
||||
/** Holds if this element is reached by a delegate creation for `c`. */
|
||||
pragma[nomagic]
|
||||
predicate isReachedBy(Callable c) {
|
||||
exists(BusyFlowElement pred |
|
||||
pred.reachesCall(this) and
|
||||
delegateCreationReaches(c, pred.getAnInput())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class TFieldOrProperty = @field or @property;
|
||||
|
||||
private class FieldOrPropertyFlow extends BusyFlowElement, Assignable, TFieldOrProperty {
|
||||
final override Expr getAnInput() {
|
||||
exists(Assignable target |
|
||||
target = this.getUnboundDeclaration() and
|
||||
result = target.getAnAssignedValue()
|
||||
)
|
||||
}
|
||||
|
||||
final override AssignableRead getAnOutput() {
|
||||
exists(Assignable target |
|
||||
target = this.getUnboundDeclaration() and
|
||||
result = target.getAnAccess()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class CallOutputFlow extends BusyFlowElement, Callable {
|
||||
final override Expr getAnInput() { this.canReturn(result) }
|
||||
|
||||
final override Call getAnOutput() {
|
||||
exists(Callable target | this = target.getUnboundDeclaration() |
|
||||
target = result.getTarget() or
|
||||
target = result.getTarget().(Method).getAnOverrider+() or
|
||||
target = result.getTarget().(Method).getAnUltimateImplementor() or
|
||||
target = getARuntimeDelegateTarget(result, false)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets a run-time target for the delegate call `c`. */
|
||||
pragma[nomagic]
|
||||
Callable getARuntimeDelegateTarget(Call c, boolean libraryDelegateCall) {
|
||||
delegateCall(c, delegateCallSource(result), libraryDelegateCall)
|
||||
// directly resolvable without going through a "busy" element
|
||||
exists(Expr e |
|
||||
delegateCreationReaches(result, e) and
|
||||
delegateCall(c, e, libraryDelegateCall)
|
||||
)
|
||||
or
|
||||
// resolvable by going through one or more "busy" elements
|
||||
exists(BusyFlowElement busy |
|
||||
busy.isReachedBy(result) and
|
||||
busy.delegateCall(c, libraryDelegateCall)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -233,71 +233,61 @@ private module Internal {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate hasOverrider(OverridableCallable oc, ValueOrRefType t) {
|
||||
exists(oc.getAnOverrider(t))
|
||||
private predicate hasOverrider(OverridableCallable oc, Gvn::GvnType t) {
|
||||
exists(oc.getAnOverrider(any(ValueOrRefType t0 | Gvn::getGlobalValueNumber(t0) = t)))
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate hasCallable(OverridableCallable source, ValueOrRefType t, OverridableCallable c) {
|
||||
private predicate hasCallable(OverridableCallable source, Gvn::GvnType t, OverridableCallable c) {
|
||||
c.getUnboundDeclaration() = source and
|
||||
t.hasCallable(c) and
|
||||
any(ValueOrRefType t0 | Gvn::getGlobalValueNumber(t0) = t).hasCallable(c) and
|
||||
hasOverrider(c, t) and
|
||||
hasQualifierTypeOverridden0(t, _) and
|
||||
hasQualifierTypeOverridden1(source, _)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private Unification::ConstrainedTypeParameter getAConstrainedTypeParameterQualifierType(
|
||||
DispatchMethodOrAccessorCall call
|
||||
) {
|
||||
result = getAPossibleType(call.getQualifier(), false)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate constrainedTypeParameterQualifierTypeSubsumes(
|
||||
ValueOrRefType t, Unification::ConstrainedTypeParameter tp
|
||||
) {
|
||||
tp = getAConstrainedTypeParameterQualifierType(_) and
|
||||
tp.subsumes(t)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate hasQualifierTypeOverridden0(ValueOrRefType t, DispatchMethodOrAccessorCall call) {
|
||||
hasOverrider(_, t) and
|
||||
(
|
||||
exists(Type t0, Type t1 |
|
||||
t0 = getAPossibleType(call.getQualifier(), false) and
|
||||
t1 = [t0, t0.(Unification::UnconstrainedTypeParameter).getAnUltimatelySuppliedType()]
|
||||
|
|
||||
t = t1
|
||||
or
|
||||
Unification::subsumes(t1, t)
|
||||
)
|
||||
or
|
||||
constrainedTypeParameterQualifierTypeSubsumes(t,
|
||||
getAConstrainedTypeParameterQualifierType(call))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate hasQualifierTypeOverridden1(
|
||||
OverridableCallable c, DispatchMethodOrAccessorCall call
|
||||
) {
|
||||
exists(OverridableCallable target | call.getAStaticTarget() = target |
|
||||
c = target.getUnboundDeclaration()
|
||||
or
|
||||
c = target.getAnUltimateImplementor().getUnboundDeclaration()
|
||||
)
|
||||
source = any(DispatchMethodOrAccessorCall call).getAStaticTargetExt()
|
||||
}
|
||||
|
||||
abstract private class DispatchMethodOrAccessorCall extends DispatchCallImpl {
|
||||
pragma[noinline]
|
||||
OverridableCallable getAStaticTargetExt() {
|
||||
exists(OverridableCallable target | this.getAStaticTarget() = target |
|
||||
result = target.getUnboundDeclaration()
|
||||
or
|
||||
result = target.getAnUltimateImplementor().getUnboundDeclaration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate hasQualifierTypeInherited(Type t) { t = getAPossibleType(this.getQualifier(), _) }
|
||||
|
||||
pragma[noinline]
|
||||
private predicate hasSubsumedQualifierType(Gvn::GvnType t) {
|
||||
hasOverrider(_, t) and
|
||||
exists(Type t0 |
|
||||
t0 = getAPossibleType(this.getQualifier(), false) and
|
||||
not t0 instanceof TypeParameter
|
||||
|
|
||||
t = Gvn::getGlobalValueNumber(t0)
|
||||
or
|
||||
Gvn::subsumes(Gvn::getGlobalValueNumber(t0), t)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate hasConstrainedTypeParameterQualifierType(
|
||||
Unification::ConstrainedTypeParameter tp
|
||||
) {
|
||||
tp = getAPossibleType(this.getQualifier(), false)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate hasUnconstrainedTypeParameterQualifierType() {
|
||||
getAPossibleType(this.getQualifier(), false) instanceof
|
||||
Unification::UnconstrainedTypeParameter
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate hasQualifierTypeOverridden(ValueOrRefType t, OverridableCallable c) {
|
||||
hasQualifierTypeOverridden0(t, this) and
|
||||
hasCallable(any(OverridableCallable oc | hasQualifierTypeOverridden1(oc, this)), t, c)
|
||||
predicate hasSubsumedQualifierTypeOverridden(Gvn::GvnType t, OverridableCallable c) {
|
||||
this.hasSubsumedQualifierType(t) and
|
||||
hasCallable(any(OverridableCallable oc | oc = this.getAStaticTargetExt()), t, c)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -357,12 +347,33 @@ private module Internal {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Callable getASubsumedStaticTarget0(Type t) {
|
||||
private predicate contextArgHasConstrainedTypeParameterType(
|
||||
DispatchCall ctx, Unification::ConstrainedTypeParameter tp
|
||||
) {
|
||||
this.contextArgHasType(ctx, tp, false)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate contextArgHasUnconstrainedTypeParameterType(DispatchCall ctx) {
|
||||
this.contextArgHasType(ctx, any(Unification::UnconstrainedTypeParameter t), false)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate contextArgHasNonTypeParameterType(DispatchCall ctx, Gvn::GvnType t) {
|
||||
exists(Type t0 |
|
||||
this.contextArgHasType(ctx, t0, false) and
|
||||
not t0 instanceof TypeParameter and
|
||||
t = Gvn::getGlobalValueNumber(t0)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Callable getASubsumedStaticTarget0(Gvn::GvnType t) {
|
||||
exists(Callable staticTarget, Type declType |
|
||||
staticTarget = this.getAStaticTarget() and
|
||||
declType = staticTarget.getDeclaringType() and
|
||||
result = staticTarget.getUnboundDeclaration() and
|
||||
Unification::subsumes(declType, t)
|
||||
Gvn::subsumes(Gvn::getGlobalValueNumber(declType), t)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -375,7 +386,8 @@ private module Internal {
|
||||
private Callable getASubsumedStaticTarget() {
|
||||
result = this.getAStaticTarget()
|
||||
or
|
||||
result.getUnboundDeclaration() = this.getASubsumedStaticTarget0(result.getDeclaringType())
|
||||
result.getUnboundDeclaration() =
|
||||
this.getASubsumedStaticTarget0(Gvn::getGlobalValueNumber(result.getDeclaringType()))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -426,6 +438,12 @@ private module Internal {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
NonConstructedOverridableCallable getAViableOverrider0() {
|
||||
getAPossibleType(this.getQualifier(), false) instanceof TypeParameter and
|
||||
result.getAConstructingCallableOrSelf() = this.getAStaticTargetExt()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a callable that is defined in a subtype of the qualifier type of this
|
||||
* call, and which overrides a static target of this call.
|
||||
@@ -468,9 +486,22 @@ private module Internal {
|
||||
*/
|
||||
private RuntimeCallable getAViableOverrider() {
|
||||
exists(ValueOrRefType t, NonConstructedOverridableCallable c |
|
||||
this.hasQualifierTypeOverridden(t, c.getAConstructingCallableOrSelf()) and
|
||||
this.hasSubsumedQualifierTypeOverridden(Gvn::getGlobalValueNumber(t),
|
||||
c.getAConstructingCallableOrSelf()) and
|
||||
result = c.getAnOverrider(t)
|
||||
)
|
||||
or
|
||||
exists(NonConstructedOverridableCallable c |
|
||||
c = this.getAViableOverrider0() and
|
||||
result = c.getAnOverrider(_)
|
||||
|
|
||||
this.hasUnconstrainedTypeParameterQualifierType()
|
||||
or
|
||||
exists(Unification::ConstrainedTypeParameter tp |
|
||||
this.hasConstrainedTypeParameterQualifierType(tp) and
|
||||
tp.subsumes(result.getDeclaringType())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override RuntimeCallable getADynamicTarget() {
|
||||
@@ -510,36 +541,40 @@ private module Internal {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private RuntimeCallable getAViableOverriderInCallContext0(
|
||||
NonConstructedOverridableCallable c, ValueOrRefType t
|
||||
) {
|
||||
result = this.getAViableOverrider() and
|
||||
this.contextArgHasType(_, _, false) and
|
||||
result = c.getAnOverrider(t)
|
||||
private RuntimeCallable getAViableOverriderInCallContext0(Gvn::GvnType t) {
|
||||
exists(NonConstructedOverridableCallable c |
|
||||
result = this.getAViableOverrider() and
|
||||
this.contextArgHasType(_, _, false) and
|
||||
result = c.getAnOverrider(any(Type t0 | t = Gvn::getGlobalValueNumber(t0))) and
|
||||
this.getAStaticTarget() = c.getAConstructingCallableOrSelf()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private RuntimeCallable getAViableOverriderInCallContext1(
|
||||
NonConstructedOverridableCallable c, DispatchCall ctx
|
||||
) {
|
||||
exists(ValueOrRefType t |
|
||||
result = this.getAViableOverriderInCallContext0(c, t) and
|
||||
exists(Type t0, Type t1 |
|
||||
this.contextArgHasType(ctx, t0, false) and
|
||||
t1 = [t0, t0.(Unification::UnconstrainedTypeParameter).getAnUltimatelySuppliedType()]
|
||||
|
|
||||
t = t1
|
||||
or
|
||||
Unification::subsumes(t1, t)
|
||||
)
|
||||
private predicate contextArgHasSubsumedType(DispatchCall ctx, Gvn::GvnType t) {
|
||||
hasOverrider(_, t) and
|
||||
exists(Gvn::GvnType t0 | this.contextArgHasNonTypeParameterType(ctx, t0) |
|
||||
t = t0
|
||||
or
|
||||
Gvn::subsumes(t0, t)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private RuntimeCallable getAViableOverriderInCallContext(DispatchCall ctx) {
|
||||
exists(NonConstructedOverridableCallable c |
|
||||
result = this.getAViableOverriderInCallContext1(c, ctx) and
|
||||
this.getAStaticTarget() = c.getAConstructingCallableOrSelf()
|
||||
exists(Gvn::GvnType t |
|
||||
result = this.getAViableOverriderInCallContext0(t) and
|
||||
this.contextArgHasSubsumedType(ctx, t)
|
||||
)
|
||||
or
|
||||
result = this.getAViableOverrider() and
|
||||
(
|
||||
this.contextArgHasUnconstrainedTypeParameterType(ctx)
|
||||
or
|
||||
exists(Unification::ConstrainedTypeParameter tp |
|
||||
this.contextArgHasConstrainedTypeParameterType(ctx, tp) and
|
||||
tp.subsumes(result.getDeclaringType())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
compilationMessages
|
||||
extractorMessages
|
||||
| file://:0:0:0:0 | Extracting default argument value 'object RecordNumber = default' instead of 'object RecordNumber = -1'. The latter is not supported in C#. |
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -11,9 +11,11 @@
|
||||
Microsoft extensions (up to VS 2019),
|
||||
|
||||
Arm Compiler 5 [2]_","``.cpp``, ``.c++``, ``.cxx``, ``.hpp``, ``.hh``, ``.h++``, ``.hxx``, ``.c``, ``.cc``, ``.h``"
|
||||
C#,C# up to 8.0,"Microsoft Visual Studio up to 2019 with .NET up to 4.8,
|
||||
C#,C# up to 9.0,"Microsoft Visual Studio up to 2019 with .NET up to 4.8,
|
||||
|
||||
.NET Core up to 3.1","``.sln``, ``.csproj``, ``.cs``, ``.cshtml``, ``.xaml``"
|
||||
.NET Core up to 3.1
|
||||
|
||||
.NET 5","``.sln``, ``.csproj``, ``.cs``, ``.cshtml``, ``.xaml``"
|
||||
Go (aka Golang), "Go up to 1.16", "Go 1.11 or more recent", ``.go``
|
||||
Java,"Java 7 to 15 [3]_","javac (OpenJDK and Oracle JDK),
|
||||
|
||||
@@ -28,5 +30,5 @@
|
||||
.. [2] Support for the Arm Compiler (armcc) is preliminary.
|
||||
.. [3] Builds that execute on Java 7 to 15 can be analyzed. The analysis understands Java 15 standard language features.
|
||||
.. [4] ECJ is supported when the build invokes it via the Maven Compiler plugin or the Takari Lifecycle plugin.
|
||||
.. [5] JSX and Flow code, YAML, JSON, HTML, and XML files may also be analyzed with JavaScript files.
|
||||
.. [6] TypeScript analysis is performed by running the JavaScript extractor with TypeScript enabled. This is the default for LGTM.
|
||||
.. [5] JSX and Flow code, YAML, JSON, HTML, and XML files may also be analyzed with JavaScript files.
|
||||
.. [6] TypeScript analysis is performed by running the JavaScript extractor with TypeScript enabled. This is the default for LGTM.
|
||||
|
||||
70
java/ql/src/Diagnostics/DiagnosticsReporting.qll
Normal file
70
java/ql/src/Diagnostics/DiagnosticsReporting.qll
Normal file
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Provides classes and predicates for reporting extractor diagnostics to end users.
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
/** Gets the SARIF severity level that indicates an error. */
|
||||
private int getErrorSeverity() { result = 2 }
|
||||
|
||||
/** Gets the SARIF severity level that indicates a warning. */
|
||||
private int getWarnSeverity() { result = 1 }
|
||||
|
||||
private predicate knownWarnings(@diagnostic d, string msg, int sev) {
|
||||
exists(string filename |
|
||||
diagnostics(d, 2, _, "Skipping Lombok-ed source file: " + filename, _, _) and
|
||||
msg = "Use of Lombok detected. Skipping file: " + filename and
|
||||
sev = getWarnSeverity()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate knownErrors(@diagnostic d, string msg, int sev) {
|
||||
exists(string numErr, Location l |
|
||||
diagnostics(d, 6, _, numErr, _, l) and
|
||||
msg = "Frontend errors in file: " + l.getFile().getAbsolutePath() + " (" + numErr + ")" and
|
||||
sev = getErrorSeverity()
|
||||
)
|
||||
or
|
||||
exists(string filename, Location l |
|
||||
diagnostics(d, 7, _, "Exception compiling file " + filename, _, l) and
|
||||
msg = "Extraction incomplete in file: " + filename and
|
||||
sev = getErrorSeverity()
|
||||
)
|
||||
or
|
||||
exists(string errMsg, Location l |
|
||||
diagnostics(d, 8, _, errMsg, _, l) and
|
||||
msg = "Severe error: " + errMsg and
|
||||
sev = getErrorSeverity()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate unknownErrors(@diagnostic d, string msg, int sev) {
|
||||
not knownErrors(d, _, _) and
|
||||
exists(Location l, File f, int diagSev |
|
||||
diagnostics(d, diagSev, _, _, _, l) and l.getFile() = f and diagSev > 3
|
||||
|
|
||||
exists(f.getRelativePath()) and
|
||||
msg = "Unknown errors in file: " + f.getAbsolutePath() + " (" + diagSev + ")" and
|
||||
sev = getErrorSeverity()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an extraction error or warning occurred that should be reported to end users,
|
||||
* with the error message `msg` and SARIF severity `sev`.
|
||||
*/
|
||||
predicate reportableDiagnostics(@diagnostic d, string msg, int sev) {
|
||||
knownWarnings(d, msg, sev) or knownErrors(d, msg, sev) or unknownErrors(d, msg, sev)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if compilation unit `f` is a source file that has
|
||||
* no relevant extraction diagnostics associated with it.
|
||||
*/
|
||||
predicate successfullyExtracted(CompilationUnit f) {
|
||||
not exists(@diagnostic d, Location l |
|
||||
reportableDiagnostics(d, _, _) and diagnostics(d, _, _, _, _, l) and l.getFile() = f
|
||||
) and
|
||||
exists(f.getRelativePath()) and
|
||||
f.fromSource()
|
||||
}
|
||||
13
java/ql/src/Diagnostics/ExtractionErrors.ql
Normal file
13
java/ql/src/Diagnostics/ExtractionErrors.ql
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @name Extraction errors
|
||||
* @description A list of extraction errors for files in the source code directory.
|
||||
* @kind diagnostic
|
||||
* @id java/diagnostics/extraction-errors
|
||||
*/
|
||||
|
||||
import java
|
||||
import DiagnosticsReporting
|
||||
|
||||
from string msg, int sev
|
||||
where reportableDiagnostics(_, msg, sev)
|
||||
select msg, sev
|
||||
14
java/ql/src/Diagnostics/SuccessfullyExtractedFiles.ql
Normal file
14
java/ql/src/Diagnostics/SuccessfullyExtractedFiles.ql
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @name Successfully extracted files
|
||||
* @description A list of all files in the source code directory that
|
||||
* were extracted without encountering an error in the file.
|
||||
* @kind diagnostic
|
||||
* @id java/diagnostics/successfully-extracted-files
|
||||
*/
|
||||
|
||||
import java
|
||||
import DiagnosticsReporting
|
||||
|
||||
from CompilationUnit f
|
||||
where successfullyExtracted(f)
|
||||
select f, ""
|
||||
14
java/ql/src/experimental/Security/CWE/CWE-094/FlowUtils.qll
Normal file
14
java/ql/src/experimental/Security/CWE/CWE-094/FlowUtils.qll
Normal file
@@ -0,0 +1,14 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
|
||||
/**
|
||||
* Holds if `fromNode` to `toNode` is a dataflow step that returns data from
|
||||
* a bean by calling one of its getters.
|
||||
*/
|
||||
predicate hasGetterFlow(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
exists(MethodAccess ma, Method m | ma.getMethod() = m |
|
||||
m instanceof GetterMethod and
|
||||
ma.getQualifier() = fromNode.asExpr() and
|
||||
ma = toNode.asExpr()
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
Apache Groovy is a powerful, optionally typed and dynamic language,
|
||||
with static-typing and static compilation capabilities.
|
||||
|
||||
It integrates smoothly with any Java program,
|
||||
and immediately delivers to your application powerful features,
|
||||
including scripting capabilities, Domain-Specific Language authoring,
|
||||
runtime and compile-time meta-programming and functional programming.
|
||||
|
||||
If a Groovy script is built using attacker-controlled data,
|
||||
and then evaluated, then it may allow the attacker to achieve RCE.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
It is generally recommended to avoid using untrusted input in a Groovy evaluation.
|
||||
If this is not possible, use a sandbox solution. Developers must also take care that Groovy
|
||||
compile-time metaprogramming can also lead to RCE: it is possible to achieve RCE by compiling
|
||||
a Groovy script (see the article "Abusing Meta Programming for Unauthenticated RCE!" linked below).
|
||||
|
||||
Groovy's <code>SecureASTCustomizer</code> allows securing source code by controlling what code constructs are permitted.
|
||||
This is typically done when using Groovy for its scripting or domain specific language (DSL) features.
|
||||
The fundamental problem is that Groovy is a dynamic language, yet <code>SecureASTCustomizer</code> works by looking at Groovy AST statically.
|
||||
|
||||
This makes it very easy for an attacker to bypass many of the intended checks
|
||||
(see https://kohsuke.org/2012/04/27/groovy-secureastcustomizer-is-harmful/).
|
||||
Therefore, besides <code>SecureASTCustomizer</code>, runtime checks are also necessary before calling Groovy methods
|
||||
(see https://melix.github.io/blog/2015/03/sandboxing.html).
|
||||
|
||||
It is also possible to use a block-list method, excluding unwanted classes from being loaded by the JVM.
|
||||
This method is not always recommended, because block-lists can be bypassed by unexpected values.
|
||||
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
The following example uses untrusted data to evaluate a Groovy script.
|
||||
</p>
|
||||
<sample src="GroovyInjectionBad.java" />
|
||||
|
||||
<p>
|
||||
The following example uses classloader block-list approach to exclude loading dangerous classes.
|
||||
</p>
|
||||
<sample src="GroovyInjectionBlocklist.java" />
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
Orange Tsai:
|
||||
<a href="https://blog.orange.tw/2019/02/abusing-meta-programming-for-unauthenticated-rce.html">Abusing Meta Programming for Unauthenticated RCE!</a>.
|
||||
</li>
|
||||
<li>
|
||||
Cédric Champeau:
|
||||
<a href="https://melix.github.io/blog/2015/03/sandboxing.html">Improved sandboxing of Groovy scripts</a>.
|
||||
</li>
|
||||
<li>
|
||||
Kohsuke Kawaguchi:
|
||||
<a href="https://kohsuke.org/2012/04/27/groovy-secureastcustomizer-is-harmful/">Groovy SecureASTCustomizer is harmful</a>.
|
||||
</li>
|
||||
<li>
|
||||
Welk1n:
|
||||
<a href="https://github.com/welk1n/exploiting-groovy-in-Java/">Groovy Injection payloads</a>.
|
||||
</li>
|
||||
<li>
|
||||
Charles Chan:
|
||||
<a href="https://levelup.gitconnected.com/secure-groovy-script-execution-in-a-sandbox-ea39f80ee87/">Secure Groovy Script Execution in a Sandbox</a>.
|
||||
</li>
|
||||
<li>
|
||||
Eugene:
|
||||
<a href="https://stringconcat.com/en/scripting-and-sandboxing/">Scripting and sandboxing in a JVM environment</a>.
|
||||
</li>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @name Groovy Language injection
|
||||
* @description Evaluation of a user-controlled Groovy script
|
||||
* may lead to arbitrary code execution.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id java/groovy-injection
|
||||
* @tags security
|
||||
* external/cwe/cwe-094
|
||||
*/
|
||||
|
||||
import java
|
||||
import DataFlow::PathGraph
|
||||
import GroovyInjectionLib
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, GroovyInjectionConfig conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Groovy Injection from $@.", source.getNode(),
|
||||
"this user input"
|
||||
@@ -0,0 +1,27 @@
|
||||
public class GroovyInjection {
|
||||
void injectionViaClassLoader(HttpServletRequest request) {
|
||||
String script = request.getParameter("script");
|
||||
final GroovyClassLoader classLoader = new GroovyClassLoader();
|
||||
Class groovy = classLoader.parseClass(script);
|
||||
GroovyObject groovyObj = (GroovyObject) groovy.newInstance();
|
||||
}
|
||||
|
||||
void injectionViaEval(HttpServletRequest request) {
|
||||
String script = request.getParameter("script");
|
||||
Eval.me(script);
|
||||
}
|
||||
|
||||
void injectionViaGroovyShell(HttpServletRequest request) {
|
||||
GroovyShell shell = new GroovyShell();
|
||||
String script = request.getParameter("script");
|
||||
shell.evaluate(script);
|
||||
}
|
||||
|
||||
void injectionViaGroovyShellGroovyCodeSource(HttpServletRequest request) {
|
||||
GroovyShell shell = new GroovyShell();
|
||||
String script = request.getParameter("script");
|
||||
GroovyCodeSource gcs = new GroovyCodeSource(script, "test", "Test");
|
||||
shell.evaluate(gcs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
public class SandboxGroovyClassLoader extends ClassLoader {
|
||||
public SandboxGroovyClassLoader(ClassLoader parent) {
|
||||
super(parent);
|
||||
}
|
||||
|
||||
/* override `loadClass` here to prevent loading sensitive classes, such as `java.lang.Runtime`, `java.lang.ProcessBuilder`, `java.lang.System`, etc. */
|
||||
/* Note we must also block `groovy.transform.ASTTest`, `groovy.lang.GrabConfig` and `org.buildobjects.process.ProcBuilder` to prevent compile-time RCE. */
|
||||
|
||||
static void runWithSandboxGroovyClassLoader() throws Exception {
|
||||
// GOOD: route all class-loading via sand-boxing classloader.
|
||||
SandboxGroovyClassLoader classLoader = new GroovyClassLoader(new SandboxGroovyClassLoader());
|
||||
|
||||
Class<?> scriptClass = classLoader.parseClass(untrusted.getQueryString());
|
||||
Object scriptInstance = scriptClass.newInstance();
|
||||
Object result = scriptClass.getDeclaredMethod("bar", new Class[]{}).invoke(scriptInstance, new Object[]{});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/**
|
||||
* Provides classes and predicates for Groovy Code Injection
|
||||
* taint-tracking configuration.
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
|
||||
/** A data flow sink for Groovy expression injection vulnerabilities. */
|
||||
abstract private class GroovyInjectionSink extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for unsafe user input
|
||||
* that is used to evaluate a Groovy expression.
|
||||
*/
|
||||
class GroovyInjectionConfig extends TaintTracking::Configuration {
|
||||
GroovyInjectionConfig() { this = "GroovyInjectionConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof GroovyInjectionSink }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
groovyCodeSourceTaintStep(fromNode, toNode)
|
||||
}
|
||||
}
|
||||
|
||||
/** The class `groovy.lang.GroovyShell`. */
|
||||
private class TypeGroovyShell extends RefType {
|
||||
TypeGroovyShell() { this.hasQualifiedName("groovy.lang", "GroovyShell") }
|
||||
}
|
||||
|
||||
/** The class `groovy.lang.GroovyCodeSource`. */
|
||||
private class TypeGroovyCodeSource extends RefType {
|
||||
TypeGroovyCodeSource() { this.hasQualifiedName("groovy.lang", "GroovyCodeSource") }
|
||||
}
|
||||
|
||||
/**
|
||||
* Methods in the `GroovyShell` class that evaluate a Groovy expression.
|
||||
*/
|
||||
private class GroovyShellMethod extends Method {
|
||||
GroovyShellMethod() {
|
||||
this.getDeclaringType() instanceof TypeGroovyShell and
|
||||
this.getName() in ["evaluate", "parse", "run"]
|
||||
}
|
||||
}
|
||||
|
||||
private class GroovyShellMethodAccess extends MethodAccess {
|
||||
GroovyShellMethodAccess() { this.getMethod() instanceof GroovyShellMethod }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `fromNode` to `toNode` is a dataflow step from a tainted string to
|
||||
* a `GroovyCodeSource` instance, i.e. `new GroovyCodeSource(tainted, ...)`.
|
||||
*/
|
||||
private predicate groovyCodeSourceTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
exists(ConstructorCall gcscc |
|
||||
gcscc.getConstructedType() instanceof TypeGroovyCodeSource and
|
||||
gcscc = toNode.asExpr() and
|
||||
gcscc.getArgument(0) = fromNode.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A sink for Groovy Injection via the `GroovyShell` class.
|
||||
*
|
||||
* ```
|
||||
* GroovyShell gs = new GroovyShell();
|
||||
* gs.evaluate(sink, ....)
|
||||
* gs.run(sink, ....)
|
||||
* gs.parse(sink,...)
|
||||
* ```
|
||||
*/
|
||||
private class GroovyShellSink extends GroovyInjectionSink {
|
||||
GroovyShellSink() {
|
||||
exists(GroovyShellMethodAccess ma, Argument firstArg |
|
||||
ma.getArgument(0) = firstArg and
|
||||
firstArg = this.asExpr() and
|
||||
(
|
||||
firstArg.getType() instanceof TypeString or
|
||||
firstArg.getType() instanceof TypeGroovyCodeSource
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** The class `groovy.util.Eval`. */
|
||||
private class TypeEval extends RefType {
|
||||
TypeEval() { this.hasQualifiedName("groovy.util", "Eval") }
|
||||
}
|
||||
|
||||
/**
|
||||
* Methods in the `Eval` class that evaluate a Groovy expression.
|
||||
*/
|
||||
private class EvalMethod extends Method {
|
||||
EvalMethod() {
|
||||
this.getDeclaringType() instanceof TypeEval and
|
||||
this.getName() in ["me", "x", "xy", "xyz"]
|
||||
}
|
||||
}
|
||||
|
||||
private class EvalMethodAccess extends MethodAccess {
|
||||
EvalMethodAccess() { this.getMethod() instanceof EvalMethod }
|
||||
|
||||
Expr getArgumentExpr() { result = this.getArgument(this.getNumArgument() - 1) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A sink for Groovy Injection via the `Eval` class.
|
||||
*
|
||||
* ```
|
||||
* Eval.me(sink)
|
||||
* Eval.me("p1", "p2", sink)
|
||||
* Eval.x("p1", sink)
|
||||
* Eval.xy("p1", "p2" sink)
|
||||
* Eval.xyz("p1", "p2", "p3", sink)
|
||||
* ```
|
||||
*/
|
||||
private class EvalSink extends GroovyInjectionSink {
|
||||
EvalSink() { exists(EvalMethodAccess ma | ma.getArgumentExpr() = this.asExpr()) }
|
||||
}
|
||||
|
||||
/** The class `groovy.lang.GroovyClassLoader`. */
|
||||
private class TypeGroovyClassLoader extends RefType {
|
||||
TypeGroovyClassLoader() { this.hasQualifiedName("groovy.lang", "GroovyClassLoader") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A method in the `GroovyClassLoader` class that evaluates a Groovy expression.
|
||||
*/
|
||||
private class GroovyClassLoaderParseClassMethod extends Method {
|
||||
GroovyClassLoaderParseClassMethod() {
|
||||
this.getDeclaringType() instanceof TypeGroovyClassLoader and
|
||||
this.hasName("parseClass")
|
||||
}
|
||||
}
|
||||
|
||||
private class GroovyClassLoaderParseClassMethodAccess extends MethodAccess {
|
||||
GroovyClassLoaderParseClassMethodAccess() {
|
||||
this.getMethod() instanceof GroovyClassLoaderParseClassMethod
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A sink for Groovy Injection via the `GroovyClassLoader` class.
|
||||
*
|
||||
* ```
|
||||
* GroovyClassLoader classLoader = new GroovyClassLoader();
|
||||
* Class groovy = classLoader.parseClass(script);
|
||||
* ```
|
||||
*
|
||||
* Groovy supports compile-time metaprogramming, so just calling the `parseClass`
|
||||
* method is enough to achieve RCE.
|
||||
*/
|
||||
private class GroovyClassLoadParseClassSink extends GroovyInjectionSink {
|
||||
GroovyClassLoadParseClassSink() {
|
||||
exists(GroovyClassLoaderParseClassMethodAccess ma | ma.getArgument(0) = this.asExpr())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
Jakarta Expression Language (EL) is an expression language for Java applications.
|
||||
There is a single language specification and multiple implementations
|
||||
such as Glassfish, Juel, Apache Commons EL, etc.
|
||||
The language allows invocation of methods available in the JVM.
|
||||
If an expression is built using attacker-controlled data,
|
||||
and then evaluated, it may allow the attacker to run arbitrary code.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
It is generally recommended to avoid using untrusted data in an EL expression.
|
||||
Before using untrusted data to build an EL expression, the data should be validated
|
||||
to ensure it is not evaluated as expression language. If the EL implementation offers
|
||||
configuring a sandbox for EL expressions, they should be run in a restrictive sandbox
|
||||
that allows accessing only explicitly allowed classes. If the EL implementation
|
||||
does not support sandboxing, consider using other expression language implementations
|
||||
with sandboxing capabilities such as Apache Commons JEXL or the Spring Expression Language.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
The following example shows how untrusted data is used to build and run an expression
|
||||
using the JUEL interpreter:
|
||||
</p>
|
||||
<sample src="UnsafeExpressionEvaluationWithJuel.java" />
|
||||
|
||||
<p>
|
||||
JUEL does not support running expressions in a sandbox. To prevent running arbitrary code,
|
||||
incoming data has to be checked before including it in an expression. The next example
|
||||
uses a Regex pattern to check whether a user tries to run an allowed expression or not:
|
||||
</p>
|
||||
<sample src="SaferExpressionEvaluationWithJuel.java" />
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
Eclipse Foundation:
|
||||
<a href="https://projects.eclipse.org/projects/ee4j.el">Jakarta Expression Language</a>.
|
||||
</li>
|
||||
<li>
|
||||
Jakarta EE documentation:
|
||||
<a href="https://javadoc.io/doc/jakarta.el/jakarta.el-api/latest/index.html">Jakarta Expression Language API</a>
|
||||
</li>
|
||||
<li>
|
||||
OWASP:
|
||||
<a href="https://owasp.org/www-community/vulnerabilities/Expression_Language_Injection">Expression Language Injection</a>.
|
||||
</li>
|
||||
<li>
|
||||
JUEL:
|
||||
<a href="http://juel.sourceforge.net">Home page</a>
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @name Jakarta Expression Language injection
|
||||
* @description Evaluation of a user-controlled expression
|
||||
* may lead to arbitrary code execution.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id java/javaee-expression-injection
|
||||
* @tags security
|
||||
* external/cwe/cwe-094
|
||||
*/
|
||||
|
||||
import java
|
||||
import JakartaExpressionInjectionLib
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, JakartaExpressionInjectionConfig conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Jakarta Expression Language injection from $@.",
|
||||
source.getNode(), "this user input"
|
||||
@@ -0,0 +1,108 @@
|
||||
import java
|
||||
import FlowUtils
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for unsafe user input
|
||||
* that is used to construct and evaluate an expression.
|
||||
*/
|
||||
class JakartaExpressionInjectionConfig extends TaintTracking::Configuration {
|
||||
JakartaExpressionInjectionConfig() { this = "JakartaExpressionInjectionConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof ExpressionEvaluationSink }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
any(TaintPropagatingCall c).taintFlow(fromNode, toNode) or
|
||||
hasGetterFlow(fromNode, toNode)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A sink for Expresssion Language injection vulnerabilities,
|
||||
* i.e. method calls that run evaluation of an expression.
|
||||
*/
|
||||
private class ExpressionEvaluationSink extends DataFlow::ExprNode {
|
||||
ExpressionEvaluationSink() {
|
||||
exists(MethodAccess ma, Method m, Expr taintFrom |
|
||||
ma.getMethod() = m and taintFrom = this.asExpr()
|
||||
|
|
||||
m.getDeclaringType() instanceof ValueExpression and
|
||||
m.hasName(["getValue", "setValue"]) and
|
||||
ma.getQualifier() = taintFrom
|
||||
or
|
||||
m.getDeclaringType() instanceof MethodExpression and
|
||||
m.hasName("invoke") and
|
||||
ma.getQualifier() = taintFrom
|
||||
or
|
||||
m.getDeclaringType() instanceof LambdaExpression and
|
||||
m.hasName("invoke") and
|
||||
ma.getQualifier() = taintFrom
|
||||
or
|
||||
m.getDeclaringType() instanceof ELProcessor and
|
||||
m.hasName(["eval", "getValue", "setValue"]) and
|
||||
ma.getArgument(0) = taintFrom
|
||||
or
|
||||
m.getDeclaringType() instanceof ELProcessor and
|
||||
m.hasName("setVariable") and
|
||||
ma.getArgument(1) = taintFrom
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines method calls that propagate tainted expressions.
|
||||
*/
|
||||
private class TaintPropagatingCall extends Call {
|
||||
Expr taintFromExpr;
|
||||
|
||||
TaintPropagatingCall() {
|
||||
taintFromExpr = this.getArgument(1) and
|
||||
(
|
||||
exists(Method m | this.(MethodAccess).getMethod() = m |
|
||||
m.getDeclaringType() instanceof ExpressionFactory and
|
||||
m.hasName(["createValueExpression", "createMethodExpression"]) and
|
||||
taintFromExpr.getType() instanceof TypeString
|
||||
)
|
||||
or
|
||||
exists(Constructor c | this.(ConstructorCall).getConstructor() = c |
|
||||
c.getDeclaringType() instanceof LambdaExpression and
|
||||
taintFromExpr.getType() instanceof ValueExpression
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `fromNode` to `toNode` is a dataflow step that propagates
|
||||
* tainted data.
|
||||
*/
|
||||
predicate taintFlow(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
fromNode.asExpr() = taintFromExpr and toNode.asExpr() = this
|
||||
}
|
||||
}
|
||||
|
||||
private class JakartaType extends RefType {
|
||||
JakartaType() { getPackage().hasName(["javax.el", "jakarta.el"]) }
|
||||
}
|
||||
|
||||
private class ELProcessor extends JakartaType {
|
||||
ELProcessor() { hasName("ELProcessor") }
|
||||
}
|
||||
|
||||
private class ExpressionFactory extends JakartaType {
|
||||
ExpressionFactory() { hasName("ExpressionFactory") }
|
||||
}
|
||||
|
||||
private class ValueExpression extends JakartaType {
|
||||
ValueExpression() { hasName("ValueExpression") }
|
||||
}
|
||||
|
||||
private class MethodExpression extends JakartaType {
|
||||
MethodExpression() { hasName("MethodExpression") }
|
||||
}
|
||||
|
||||
private class LambdaExpression extends JakartaType {
|
||||
LambdaExpression() { hasName("LambdaExpression") }
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import java
|
||||
import FlowUtils
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
|
||||
@@ -16,7 +17,7 @@ class JexlInjectionConfig extends TaintTracking::Configuration {
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
any(TaintPropagatingJexlMethodCall c).taintFlow(fromNode, toNode) or
|
||||
returnsDataFromBean(fromNode, toNode)
|
||||
hasGetterFlow(fromNode, toNode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,18 +153,6 @@ private predicate createsJexlEngine(DataFlow::Node fromNode, DataFlow::Node toNo
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `fromNode` to `toNode` is a dataflow step that returns data from
|
||||
* a bean by calling one of its getters.
|
||||
*/
|
||||
private predicate returnsDataFromBean(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
exists(MethodAccess ma, Method m | ma.getMethod() = m |
|
||||
m instanceof GetterMethod and
|
||||
ma.getQualifier() = fromNode.asExpr() and
|
||||
ma = toNode.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A methods in the `JexlEngine` class that gets or sets a property with a JEXL expression.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
String input = getRemoteUserInput();
|
||||
String pattern = "(inside|outside)\\.(temperature|humidity)";
|
||||
if (!input.matches(pattern)) {
|
||||
throw new IllegalArgumentException("Unexpected expression");
|
||||
}
|
||||
String expression = "${" + input + "}";
|
||||
ExpressionFactory factory = new de.odysseus.el.ExpressionFactoryImpl();
|
||||
ValueExpression e = factory.createValueExpression(context, expression, Object.class);
|
||||
SimpleContext context = getContext();
|
||||
Object result = e.getValue(context);
|
||||
@@ -0,0 +1,5 @@
|
||||
String expression = "${" + getRemoteUserInput() + "}";
|
||||
ExpressionFactory factory = new de.odysseus.el.ExpressionFactoryImpl();
|
||||
ValueExpression e = factory.createValueExpression(context, expression, Object.class);
|
||||
SimpleContext context = getContext();
|
||||
Object result = e.getValue(context);
|
||||
@@ -0,0 +1,57 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/** Json string type data. */
|
||||
abstract class JsonStringSource extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* Convert to String using Gson library. *
|
||||
*
|
||||
* For example, in the method access `Gson.toJson(...)`,
|
||||
* the `Object` type data is converted to the `String` type data.
|
||||
*/
|
||||
private class GsonString extends JsonStringSource {
|
||||
GsonString() {
|
||||
exists(MethodAccess ma, Method m | ma.getMethod() = m |
|
||||
m.hasName("toJson") and
|
||||
m.getDeclaringType().getASupertype*().hasQualifiedName("com.google.gson", "Gson") and
|
||||
this.asExpr() = ma
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to String using Fastjson library.
|
||||
*
|
||||
* For example, in the method access `JSON.toJSONString(...)`,
|
||||
* the `Object` type data is converted to the `String` type data.
|
||||
*/
|
||||
private class FastjsonString extends JsonStringSource {
|
||||
FastjsonString() {
|
||||
exists(MethodAccess ma, Method m | ma.getMethod() = m |
|
||||
m.hasName("toJSONString") and
|
||||
m.getDeclaringType().getASupertype*().hasQualifiedName("com.alibaba.fastjson", "JSON") and
|
||||
this.asExpr() = ma
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to String using Jackson library.
|
||||
*
|
||||
* For example, in the method access `ObjectMapper.writeValueAsString(...)`,
|
||||
* the `Object` type data is converted to the `String` type data.
|
||||
*/
|
||||
private class JacksonString extends JsonStringSource {
|
||||
JacksonString() {
|
||||
exists(MethodAccess ma, Method m | ma.getMethod() = m |
|
||||
m.hasName("writeValueAsString") and
|
||||
m.getDeclaringType()
|
||||
.getASupertype*()
|
||||
.hasQualifiedName("com.fasterxml.jackson.databind", "ObjectMapper") and
|
||||
this.asExpr() = ma
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.gson.Gson;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.HashMap;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@Controller
|
||||
public class JsonpInjection {
|
||||
|
||||
private static HashMap hashMap = new HashMap();
|
||||
|
||||
static {
|
||||
hashMap.put("username","admin");
|
||||
hashMap.put("password","123456");
|
||||
}
|
||||
|
||||
@GetMapping(value = "jsonp1")
|
||||
@ResponseBody
|
||||
public String bad1(HttpServletRequest request) {
|
||||
String resultStr = null;
|
||||
String jsonpCallback = request.getParameter("jsonpCallback");
|
||||
Gson gson = new Gson();
|
||||
String result = gson.toJson(hashMap);
|
||||
resultStr = jsonpCallback + "(" + result + ")";
|
||||
return resultStr;
|
||||
}
|
||||
|
||||
@GetMapping(value = "jsonp2")
|
||||
@ResponseBody
|
||||
public String bad2(HttpServletRequest request) {
|
||||
String resultStr = null;
|
||||
String jsonpCallback = request.getParameter("jsonpCallback");
|
||||
resultStr = jsonpCallback + "(" + JSONObject.toJSONString(hashMap) + ")";
|
||||
return resultStr;
|
||||
}
|
||||
|
||||
@GetMapping(value = "jsonp3")
|
||||
@ResponseBody
|
||||
public String bad3(HttpServletRequest request) {
|
||||
String resultStr = null;
|
||||
String jsonpCallback = request.getParameter("jsonpCallback");
|
||||
String jsonStr = getJsonStr(hashMap);
|
||||
resultStr = jsonpCallback + "(" + jsonStr + ")";
|
||||
return resultStr;
|
||||
}
|
||||
|
||||
@GetMapping(value = "jsonp4")
|
||||
@ResponseBody
|
||||
public String bad4(HttpServletRequest request) {
|
||||
String resultStr = null;
|
||||
String jsonpCallback = request.getParameter("jsonpCallback");
|
||||
String restr = JSONObject.toJSONString(hashMap);
|
||||
resultStr = jsonpCallback + "(" + restr + ");";
|
||||
return resultStr;
|
||||
}
|
||||
|
||||
@GetMapping(value = "jsonp5")
|
||||
@ResponseBody
|
||||
public void bad5(HttpServletRequest request,
|
||||
HttpServletResponse response) throws Exception {
|
||||
String jsonpCallback = request.getParameter("jsonpCallback");
|
||||
PrintWriter pw = null;
|
||||
Gson gson = new Gson();
|
||||
String result = gson.toJson(hashMap);
|
||||
String resultStr = null;
|
||||
pw = response.getWriter();
|
||||
resultStr = jsonpCallback + "(" + result + ")";
|
||||
pw.println(resultStr);
|
||||
}
|
||||
|
||||
@GetMapping(value = "jsonp6")
|
||||
@ResponseBody
|
||||
public void bad6(HttpServletRequest request,
|
||||
HttpServletResponse response) throws Exception {
|
||||
String jsonpCallback = request.getParameter("jsonpCallback");
|
||||
PrintWriter pw = null;
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
String result = mapper.writeValueAsString(hashMap);
|
||||
String resultStr = null;
|
||||
pw = response.getWriter();
|
||||
resultStr = jsonpCallback + "(" + result + ")";
|
||||
pw.println(resultStr);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "jsonp7", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public String bad7(HttpServletRequest request) {
|
||||
String resultStr = null;
|
||||
String jsonpCallback = request.getParameter("jsonpCallback");
|
||||
Gson gson = new Gson();
|
||||
String result = gson.toJson(hashMap);
|
||||
resultStr = jsonpCallback + "(" + result + ")";
|
||||
return resultStr;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "jsonp11")
|
||||
@ResponseBody
|
||||
public String good1(HttpServletRequest request) {
|
||||
JSONObject parameterObj = readToJSONObect(request);
|
||||
String resultStr = null;
|
||||
String jsonpCallback = request.getParameter("jsonpCallback");
|
||||
String restr = JSONObject.toJSONString(hashMap);
|
||||
resultStr = jsonpCallback + "(" + restr + ");";
|
||||
return resultStr;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "jsonp12")
|
||||
@ResponseBody
|
||||
public String good2(@RequestParam("file") MultipartFile file,HttpServletRequest request) {
|
||||
if(null == file){
|
||||
return "upload file error";
|
||||
}
|
||||
String fileName = file.getOriginalFilename();
|
||||
System.out.println("file operations");
|
||||
String resultStr = null;
|
||||
String jsonpCallback = request.getParameter("jsonpCallback");
|
||||
String restr = JSONObject.toJSONString(hashMap);
|
||||
resultStr = jsonpCallback + "(" + restr + ");";
|
||||
return resultStr;
|
||||
}
|
||||
|
||||
public static JSONObject readToJSONObect(HttpServletRequest request){
|
||||
String jsonText = readPostContent(request);
|
||||
JSONObject jsonObj = JSONObject.parseObject(jsonText, JSONObject.class);
|
||||
return jsonObj;
|
||||
}
|
||||
|
||||
public static String readPostContent(HttpServletRequest request){
|
||||
BufferedReader in= null;
|
||||
String content = null;
|
||||
String line = null;
|
||||
try {
|
||||
in = new BufferedReader(new InputStreamReader(request.getInputStream(),"UTF-8"));
|
||||
StringBuilder buf = new StringBuilder();
|
||||
while ((line = in.readLine()) != null) {
|
||||
buf.append(line);
|
||||
}
|
||||
content = buf.toString();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
String uri = request.getRequestURI();
|
||||
return content;
|
||||
}
|
||||
|
||||
public static String getJsonStr(Object result) {
|
||||
return JSONObject.toJSONString(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>The software uses external input as the function name to wrap JSON data and returns it to the client as a request response.
|
||||
When there is a cross-domain problem, this could lead to information leakage.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>Adding <code>Referer</code>/<code>Origin</code> or random <code>token</code> verification processing can effectively prevent the leakage of sensitive information.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>The following examples show the bad case and the good case respectively. Bad cases, such as <code>bad1</code> to <code>bad7</code>,
|
||||
will cause information leakage when there are cross-domain problems. In a good case, for example, in the <code>good1</code>
|
||||
method and the <code>good2</code> method, When these two methods process the request, there must be a request body in the request, which does not meet the conditions of Jsonp injection.</p>
|
||||
|
||||
<sample src="JsonpInjection.java" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
OWASPLondon20161124_JSON_Hijacking_Gareth_Heyes:
|
||||
<a href="https://owasp.org/www-chapter-london/assets/slides/OWASPLondon20161124_JSON_Hijacking_Gareth_Heyes.pdf">JSON hijacking</a>.
|
||||
</li>
|
||||
<li>
|
||||
Practical JSONP Injection:
|
||||
<a href="https://securitycafe.ro/2017/01/18/practical-jsonp-injection">
|
||||
Completely controllable from the URL (GET variable)
|
||||
</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* @name JSONP Injection
|
||||
* @description User-controlled callback function names that are not verified are vulnerable
|
||||
* to jsonp injection attacks.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id java/jsonp-injection
|
||||
* @tags security
|
||||
* external/cwe/cwe-352
|
||||
*/
|
||||
|
||||
import java
|
||||
import JsonpInjectionLib
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.deadcode.WebEntryPoints
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/** Taint-tracking configuration tracing flow from get method request sources to output jsonp data. */
|
||||
class RequestResponseFlowConfig extends TaintTracking::Configuration {
|
||||
RequestResponseFlowConfig() { this = "RequestResponseFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source instanceof RemoteFlowSource and
|
||||
any(RequestGetMethod m).polyCalls*(source.getEnclosingCallable())
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink instanceof XssSink and
|
||||
any(RequestGetMethod m).polyCalls*(sink.getEnclosingCallable())
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(MethodAccess ma |
|
||||
isRequestGetParamMethod(ma) and pred.asExpr() = ma.getQualifier() and succ.asExpr() = ma
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, RequestResponseFlowConfig conf
|
||||
where
|
||||
conf.hasFlowPath(source, sink) and
|
||||
exists(JsonpInjectionFlowConfig jhfc | jhfc.hasFlowTo(sink.getNode()))
|
||||
select sink.getNode(), source, sink, "Jsonp response might include code from $@.", source.getNode(),
|
||||
"this user input"
|
||||
@@ -0,0 +1,118 @@
|
||||
import java
|
||||
import DataFlow
|
||||
import JsonStringLib
|
||||
import semmle.code.java.security.XSS
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.DataFlow3
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.frameworks.spring.SpringController
|
||||
|
||||
/**
|
||||
* A method that is called to handle an HTTP GET request.
|
||||
*/
|
||||
abstract class RequestGetMethod extends Method {
|
||||
RequestGetMethod() {
|
||||
not exists(MethodAccess ma |
|
||||
// Exclude apparent GET handlers that read a request entity, because this likely indicates this is not in fact a GET handler.
|
||||
// This is particularly a problem with Spring handlers, which can sometimes neglect to specify a request method.
|
||||
// Even if it is in fact a GET handler, such a request method will be unusable in the context `<script src="...">`,
|
||||
// which is the typical use-case for JSONP but cannot supply a request body.
|
||||
ma.getMethod() instanceof ServletRequestGetBodyMethod and
|
||||
this.polyCalls*(ma.getEnclosingCallable())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Override method of `doGet` of `Servlet` subclass. */
|
||||
private class ServletGetMethod extends RequestGetMethod {
|
||||
ServletGetMethod() { isServletRequestMethod(this) and this.getName() = "doGet" }
|
||||
}
|
||||
|
||||
/** The method of SpringController class processing `get` request. */
|
||||
abstract class SpringControllerGetMethod extends RequestGetMethod { }
|
||||
|
||||
/** Method using `GetMapping` annotation in SpringController class. */
|
||||
class SpringControllerGetMappingGetMethod extends SpringControllerGetMethod {
|
||||
SpringControllerGetMappingGetMethod() {
|
||||
this.getAnAnnotation()
|
||||
.getType()
|
||||
.hasQualifiedName("org.springframework.web.bind.annotation", "GetMapping")
|
||||
}
|
||||
}
|
||||
|
||||
/** The method that uses the `RequestMapping` annotation in the SpringController class and only handles the get request. */
|
||||
class SpringControllerRequestMappingGetMethod extends SpringControllerGetMethod {
|
||||
SpringControllerRequestMappingGetMethod() {
|
||||
this.getAnAnnotation()
|
||||
.getType()
|
||||
.hasQualifiedName("org.springframework.web.bind.annotation", "RequestMapping") and
|
||||
(
|
||||
this.getAnAnnotation().getValue("method").(VarAccess).getVariable().getName() = "GET" or
|
||||
this.getAnAnnotation().getValue("method").(ArrayInit).getSize() = 0 //Java code example: @RequestMapping(value = "test")
|
||||
) and
|
||||
not this.getAParamType().getName() = "MultipartFile"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A concatenate expression using `(` and `)` or `);`.
|
||||
*
|
||||
* E.g: `functionName + "(" + json + ")"` or `functionName + "(" + json + ");"`
|
||||
*/
|
||||
class JsonpBuilderExpr extends AddExpr {
|
||||
JsonpBuilderExpr() {
|
||||
getRightOperand().(CompileTimeConstantExpr).getStringValue().regexpMatch("\\);?") and
|
||||
getLeftOperand()
|
||||
.(AddExpr)
|
||||
.getLeftOperand()
|
||||
.(AddExpr)
|
||||
.getRightOperand()
|
||||
.(CompileTimeConstantExpr)
|
||||
.getStringValue() = "("
|
||||
}
|
||||
|
||||
/** Get the jsonp function name of this expression. */
|
||||
Expr getFunctionName() {
|
||||
result = getLeftOperand().(AddExpr).getLeftOperand().(AddExpr).getLeftOperand()
|
||||
}
|
||||
|
||||
/** Get the json data of this expression. */
|
||||
Expr getJsonExpr() { result = getLeftOperand().(AddExpr).getRightOperand() }
|
||||
}
|
||||
|
||||
/** A data flow configuration tracing flow from remote sources to jsonp function name. */
|
||||
class RemoteFlowConfig extends DataFlow2::Configuration {
|
||||
RemoteFlowConfig() { this = "RemoteFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(JsonpBuilderExpr jhe | jhe.getFunctionName() = sink.asExpr())
|
||||
}
|
||||
}
|
||||
|
||||
/** A data flow configuration tracing flow from json data into the argument `json` of JSONP-like string `someFunctionName + "(" + json + ")"`. */
|
||||
class JsonDataFlowConfig extends DataFlow2::Configuration {
|
||||
JsonDataFlowConfig() { this = "JsonDataFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { src instanceof JsonStringSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(JsonpBuilderExpr jhe | jhe.getJsonExpr() = sink.asExpr())
|
||||
}
|
||||
}
|
||||
|
||||
/** Taint-tracking configuration tracing flow from probable jsonp data with a user-controlled function name to an outgoing HTTP entity. */
|
||||
class JsonpInjectionFlowConfig extends TaintTracking::Configuration {
|
||||
JsonpInjectionFlowConfig() { this = "JsonpInjectionFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) {
|
||||
exists(JsonpBuilderExpr jhe, JsonDataFlowConfig jdfc, RemoteFlowConfig rfc |
|
||||
jhe = src.asExpr() and
|
||||
jdfc.hasFlowTo(DataFlow::exprNode(jhe.getJsonExpr())) and
|
||||
rfc.hasFlowTo(DataFlow::exprNode(jhe.getFunctionName()))
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof XssSink }
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
import Member
|
||||
import semmle.code.java.security.ExternalProcess
|
||||
|
||||
// --- Standard types ---
|
||||
/** The class `java.lang.Object`. */
|
||||
@@ -176,24 +177,37 @@ class TypeFile extends Class {
|
||||
}
|
||||
|
||||
// --- Standard methods ---
|
||||
/**
|
||||
* Any constructor of class `java.lang.ProcessBuilder`.
|
||||
*/
|
||||
class ProcessBuilderConstructor extends Constructor, ExecCallable {
|
||||
ProcessBuilderConstructor() { this.getDeclaringType() instanceof TypeProcessBuilder }
|
||||
|
||||
override int getAnExecutedArgument() { result = 0 }
|
||||
}
|
||||
|
||||
/**
|
||||
* Any of the methods named `command` on class `java.lang.ProcessBuilder`.
|
||||
*/
|
||||
class MethodProcessBuilderCommand extends Method {
|
||||
class MethodProcessBuilderCommand extends Method, ExecCallable {
|
||||
MethodProcessBuilderCommand() {
|
||||
hasName("command") and
|
||||
getDeclaringType() instanceof TypeProcessBuilder
|
||||
}
|
||||
|
||||
override int getAnExecutedArgument() { result = 0 }
|
||||
}
|
||||
|
||||
/**
|
||||
* Any method named `exec` on class `java.lang.Runtime`.
|
||||
*/
|
||||
class MethodRuntimeExec extends Method {
|
||||
class MethodRuntimeExec extends Method, ExecCallable {
|
||||
MethodRuntimeExec() {
|
||||
hasName("exec") and
|
||||
getDeclaringType() instanceof TypeRuntime
|
||||
}
|
||||
|
||||
override int getAnExecutedArgument() { result = 0 }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -67,6 +67,7 @@
|
||||
import java
|
||||
private import semmle.code.java.dataflow.DataFlow::DataFlow
|
||||
private import internal.DataFlowPrivate
|
||||
private import FlowSummary
|
||||
|
||||
/**
|
||||
* A module importing the frameworks that provide external flow data,
|
||||
@@ -76,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) {
|
||||
@@ -459,7 +461,8 @@ module CsvValidation {
|
||||
summaryModel(_, _, _, _, _, _, input, _, _) and pred = "summary"
|
||||
|
|
||||
specSplit(input, part, _) and
|
||||
not part.regexpMatch("|Argument|ReturnValue") and
|
||||
not part.regexpMatch("|ReturnValue|ArrayElement|Element|MapKey|MapValue") and
|
||||
not (part = "Argument" and pred = "sink") and
|
||||
not parseArg(part, _) and
|
||||
msg = "Unrecognized input specification \"" + part + "\" in " + pred + " model."
|
||||
)
|
||||
@@ -470,7 +473,8 @@ module CsvValidation {
|
||||
summaryModel(_, _, _, _, _, _, _, output, _) and pred = "summary"
|
||||
|
|
||||
specSplit(output, part, _) and
|
||||
not part.regexpMatch("|Argument|Parameter|ReturnValue") and
|
||||
not part.regexpMatch("|ReturnValue|ArrayElement|Element|MapKey|MapValue") and
|
||||
not (part = ["Argument", "Parameter"] and pred = "source") and
|
||||
not parseArg(part, _) and
|
||||
not parseParam(part, _) and
|
||||
msg = "Unrecognized output specification \"" + part + "\" in " + pred + " model."
|
||||
@@ -675,6 +679,75 @@ private predicate summaryElementRef(Top ref, string input, string output, string
|
||||
)
|
||||
}
|
||||
|
||||
private SummaryComponent interpretComponent(string c) {
|
||||
specSplit(_, c, _) and
|
||||
(
|
||||
exists(int pos | parseArg(c, pos) and result = SummaryComponent::argument(pos))
|
||||
or
|
||||
exists(int pos | parseParam(c, pos) and result = SummaryComponent::parameter(pos))
|
||||
or
|
||||
c = "ReturnValue" and result = SummaryComponent::return()
|
||||
or
|
||||
c = "ArrayElement" and result = SummaryComponent::content(any(ArrayContent c0))
|
||||
or
|
||||
c = "Element" and result = SummaryComponent::content(any(CollectionContent c0))
|
||||
or
|
||||
c = "MapKey" and result = SummaryComponent::content(any(MapKeyContent c0))
|
||||
or
|
||||
c = "MapValue" and result = SummaryComponent::content(any(MapValueContent c0))
|
||||
)
|
||||
}
|
||||
|
||||
private predicate interpretSpec(string spec, int idx, SummaryComponentStack stack) {
|
||||
exists(string c |
|
||||
summaryElement(_, spec, _, _) or
|
||||
summaryElement(_, _, spec, _)
|
||||
|
|
||||
len(spec, idx + 1) and
|
||||
specSplit(spec, c, idx) and
|
||||
stack = SummaryComponentStack::singleton(interpretComponent(c))
|
||||
)
|
||||
or
|
||||
exists(SummaryComponent head, SummaryComponentStack tail |
|
||||
interpretSpec(spec, idx, head, tail) and
|
||||
stack = SummaryComponentStack::push(head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate interpretSpec(
|
||||
string output, int idx, SummaryComponent head, SummaryComponentStack tail
|
||||
) {
|
||||
exists(string c |
|
||||
interpretSpec(output, idx + 1, tail) and
|
||||
specSplit(output, c, idx) and
|
||||
head = interpretComponent(c)
|
||||
)
|
||||
}
|
||||
|
||||
private class MkStack extends RequiredSummaryComponentStack {
|
||||
MkStack() { interpretSpec(_, _, _, this) }
|
||||
|
||||
override predicate required(SummaryComponent c) { interpretSpec(_, _, c, this) }
|
||||
}
|
||||
|
||||
private class SummarizedCallableExternal extends SummarizedCallable {
|
||||
SummarizedCallableExternal() { summaryElement(this, _, _, _) }
|
||||
|
||||
override predicate propagatesFlow(
|
||||
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
|
||||
) {
|
||||
exists(string inSpec, string outSpec, string kind |
|
||||
summaryElement(this, inSpec, outSpec, kind) and
|
||||
interpretSpec(inSpec, 0, input) and
|
||||
interpretSpec(outSpec, 0, output)
|
||||
|
|
||||
kind = "value" and preservesValue = true
|
||||
or
|
||||
kind = "taint" and preservesValue = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TAstOrNode =
|
||||
TAst(Top t) or
|
||||
TNode(Node n)
|
||||
@@ -761,15 +834,3 @@ predicate sinkNode(Node node, string kind) {
|
||||
interpretInput(input, 0, ref, TNode(node))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node1` to `node2` is specified as a flow step with the given kind
|
||||
* in a CSV flow model.
|
||||
*/
|
||||
predicate summaryStep(Node node1, Node node2, string kind) {
|
||||
exists(Top ref, string input, string output |
|
||||
summaryElementRef(ref, input, output, kind) and
|
||||
interpretInput(input, 0, ref, TNode(node1)) and
|
||||
interpretOutput(output, 0, ref, TNode(node2))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -7,8 +7,10 @@ private import internal.FlowSummaryImpl as Impl
|
||||
private import internal.DataFlowDispatch
|
||||
private import internal.DataFlowPrivate
|
||||
|
||||
// import all instances below
|
||||
private module Summaries { }
|
||||
// import all instances of SummarizedCallable below
|
||||
private module Summaries {
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
}
|
||||
|
||||
class SummaryComponent = Impl::Public::SummaryComponent;
|
||||
|
||||
|
||||
@@ -84,8 +84,10 @@ private predicate instanceFieldAssign(Expr src, FieldAccess fa) {
|
||||
|
||||
private newtype TContent =
|
||||
TFieldContent(InstanceField f) or
|
||||
TArrayContent() or
|
||||
TCollectionContent() or
|
||||
TArrayContent()
|
||||
TMapKeyContent() or
|
||||
TMapValueContent()
|
||||
|
||||
/**
|
||||
* A reference contained in an object. Examples include instance fields, the
|
||||
@@ -114,12 +116,20 @@ class FieldContent extends Content, TFieldContent {
|
||||
}
|
||||
}
|
||||
|
||||
class CollectionContent extends Content, TCollectionContent {
|
||||
override string toString() { result = "collection" }
|
||||
class ArrayContent extends Content, TArrayContent {
|
||||
override string toString() { result = "[]" }
|
||||
}
|
||||
|
||||
class ArrayContent extends Content, TArrayContent {
|
||||
override string toString() { result = "array" }
|
||||
class CollectionContent extends Content, TCollectionContent {
|
||||
override string toString() { result = "<element>" }
|
||||
}
|
||||
|
||||
class MapKeyContent extends Content, TMapKeyContent {
|
||||
override string toString() { result = "<map.key>" }
|
||||
}
|
||||
|
||||
class MapValueContent extends Content, TMapValueContent {
|
||||
override string toString() { result = "<map.value>" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -142,8 +142,6 @@ predicate simpleLocalFlowStep(Node node1, Node node2) {
|
||||
or
|
||||
node2.asExpr().(AssignExpr).getSource() = node1.asExpr()
|
||||
or
|
||||
summaryStep(node1, node2, "value")
|
||||
or
|
||||
exists(MethodAccess ma, ValuePreservingMethod m, int argNo |
|
||||
ma.getCallee().getSourceDeclaration() = m and m.returnsValue(argNo)
|
||||
|
|
||||
@@ -152,6 +150,14 @@ predicate simpleLocalFlowStep(Node node1, Node node2) {
|
||||
)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(node1, node2, true)
|
||||
or
|
||||
// If flow through a method updates a parameter from some input A, and that
|
||||
// parameter also is returned through B, then we'd like a combined flow from A
|
||||
// to B as well. As an example, this simplifies modeling of fluent methods:
|
||||
// for `StringBuilder.append(x)` with a specified value flow from qualifier to
|
||||
// return value and taint flow from argument 0 to the qualifier, then this
|
||||
// allows us to infer taint flow from argument 0 to the return value.
|
||||
node1.(SummaryNode).(PostUpdateNode).getPreUpdateNode().(ParameterNode) = node2
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -46,47 +46,19 @@ predicate localTaintStep(DataFlow::Node src, DataFlow::Node sink) {
|
||||
* different objects.
|
||||
*/
|
||||
predicate localAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
|
||||
localAdditionalBasicTaintStep(src, sink)
|
||||
or
|
||||
composedValueAndTaintModelStep(src, sink)
|
||||
}
|
||||
|
||||
private predicate localAdditionalBasicTaintStep(DataFlow::Node src, DataFlow::Node sink) {
|
||||
localAdditionalTaintExprStep(src.asExpr(), sink.asExpr())
|
||||
or
|
||||
localAdditionalTaintUpdateStep(src.asExpr(),
|
||||
sink.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr())
|
||||
or
|
||||
summaryStep(src, sink, "taint") and
|
||||
not summaryStep(src, sink, "value")
|
||||
or
|
||||
exists(Argument arg |
|
||||
src.asExpr() = arg and
|
||||
arg.isVararg() and
|
||||
sink.(DataFlow::ImplicitVarargsArray).getCall() = arg.getCall()
|
||||
)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(src, sink, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an additional step from `src` to `sink` through a call can be inferred from the
|
||||
* combination of a value-preserving step providing an alias between an input and the output
|
||||
* and a taint step from `src` to one the aliased nodes. For example, if we know that `f(a, b)` returns
|
||||
* the exact value of `a` and also propagates taint from `b` to `a`, then we also know that
|
||||
* the return value is tainted after `f` completes.
|
||||
*/
|
||||
private predicate composedValueAndTaintModelStep(ArgumentNode src, DataFlow::Node sink) {
|
||||
exists(Call call, ArgumentNode valueSource, DataFlow::PostUpdateNode valueSourcePost |
|
||||
src.argumentOf(call, _) and
|
||||
valueSource.argumentOf(call, _) and
|
||||
src != valueSource and
|
||||
valueSourcePost.getPreUpdateNode() = valueSource and
|
||||
// in-x -value-> out-y and in-z -taint-> in-x ==> in-z -taint-> out-y
|
||||
localAdditionalBasicTaintStep(src, valueSourcePost) and
|
||||
DataFlow::localFlowStep(valueSource, DataFlow::exprNode(call)) and
|
||||
sink = DataFlow::exprNode(call)
|
||||
)
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(src, sink, false) and
|
||||
not FlowSummaryImpl::Private::Steps::summaryLocalStep(src, sink, true)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -149,9 +149,8 @@ private class ApacheHttpFlowStep extends SummaryModelCsv {
|
||||
"org.apache.hc.core5.http.message;RequestLine;true;getMethod;();;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.hc.core5.http.message;RequestLine;true;getUri;();;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.hc.core5.http.message;RequestLine;true;toString;();;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.hc.core5.http.message;RequestLine;true;RequestLine;(HttpRequest);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.hc.core5.http.message;RequestLine;true;RequestLine;(String,String,ProtocolVersion);;Argument[1];ReturnValue;taint",
|
||||
"org.apache.hc.core5.http.message;RequestLine;true;RequestLine;(String,String,ProtocolVersion);;Argument[1];ReturnValue;taint",
|
||||
"org.apache.hc.core5.http.message;RequestLine;true;RequestLine;(HttpRequest);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.hc.core5.http.message;RequestLine;true;RequestLine;(String,String,ProtocolVersion);;Argument[1];Argument[-1];taint",
|
||||
"org.apache.hc.core5.function;Supplier;true;get;();;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.hc.core5.net;URIAuthority;true;getHostName;();;Argument[-1];ReturnValue;taint",
|
||||
"org.apache.hc.core5.net;URIAuthority;true;toString;();;Argument[-1];ReturnValue;taint",
|
||||
@@ -181,16 +180,16 @@ private class ApacheHttpFlowStep extends SummaryModelCsv {
|
||||
"org.apache.hc.core5.http.io.entity;HttpEntities;true;withTrailers;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.http.entity;BasicHttpEntity;true;setContent;(InputStream);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.http.entity;BufferedHttpEntity;true;BufferedHttpEntity;(HttpEntity);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.http.entity;ByteArrayEntity;true;ByteArrayEntity;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.http.entity;ByteArrayEntity;true;ByteArrayEntity;;;Argument[0];Argument[-1];taint",
|
||||
"org.apache.http.entity;HttpEntityWrapper;true;HttpEntityWrapper;(HttpEntity);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.http.entity;InputStreamEntity;true;InputStreamEntity;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.http.entity;StringEntity;true;StringEntity;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.http.entity;StringEntity;true;StringEntity;;;Argument[0];Argument[-1];taint",
|
||||
"org.apache.hc.core5.http.io.entity;BasicHttpEntity;true;BasicHttpEntity;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.hc.core5.http.io.entity;BufferedHttpEntity;true;BufferedHttpEntity;(HttpEntity);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.hc.core5.http.io.entity;ByteArrayEntity;true;ByteArrayEntity;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.hc.core5.http.io.entity;ByteArrayEntity;true;ByteArrayEntity;;;Argument[0];Argument[-1];taint",
|
||||
"org.apache.hc.core5.http.io.entity;HttpEntityWrapper;true;HttpEntityWrapper;(HttpEntity);;Argument[0];ReturnValue;taint",
|
||||
"org.apache.hc.core5.http.io.entity;InputStreamEntity;true;InputStreamEntity;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.hc.core5.http.io.entity;StringEntity;true;StringEntity;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.hc.core5.http.io.entity;StringEntity;true;StringEntity;;;Argument[0];Argument[-1];taint",
|
||||
"org.apache.http.util;ByteArrayBuffer;true;append;(byte[],int,int);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.http.util;ByteArrayBuffer;true;append;(char[],int,int);;Argument[0];Argument[-1];taint",
|
||||
"org.apache.http.util;ByteArrayBuffer;true;append;(CharArrayBuffer,int,int);;Argument[0];Argument[-1];taint",
|
||||
|
||||
@@ -1,20 +1,28 @@
|
||||
/* Definitions related to the Apache Commons Exec library. */
|
||||
import semmle.code.java.Type
|
||||
import semmle.code.java.security.ExternalProcess
|
||||
|
||||
library class TypeCommandLine extends Class {
|
||||
/** The class `org.apache.commons.exec.CommandLine`. */
|
||||
private class TypeCommandLine extends Class {
|
||||
TypeCommandLine() { hasQualifiedName("org.apache.commons.exec", "CommandLine") }
|
||||
}
|
||||
|
||||
library class MethodCommandLineParse extends Method {
|
||||
/** The `parse()` method of the class `org.apache.commons.exec.CommandLine`. */
|
||||
private class MethodCommandLineParse extends Method, ExecCallable {
|
||||
MethodCommandLineParse() {
|
||||
getDeclaringType() instanceof TypeCommandLine and
|
||||
hasName("parse")
|
||||
}
|
||||
|
||||
override int getAnExecutedArgument() { result = 0 }
|
||||
}
|
||||
|
||||
library class MethodCommandLineAddArguments extends Method {
|
||||
/** The `addArguments()` method of the class `org.apache.commons.exec.CommandLine`. */
|
||||
private class MethodCommandLineAddArguments extends Method, ExecCallable {
|
||||
MethodCommandLineAddArguments() {
|
||||
getDeclaringType() instanceof TypeCommandLine and
|
||||
hasName("addArguments")
|
||||
}
|
||||
|
||||
override int getAnExecutedArgument() { result = 0 }
|
||||
}
|
||||
|
||||
@@ -626,12 +626,12 @@ private class ApacheObjectUtilsModel extends SummaryModelCsv {
|
||||
"org.apache.commons.lang3;ObjectUtils;false;CONST_BYTE;;;Argument[0];ReturnValue;value",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;CONST_SHORT;;;Argument[0];ReturnValue;value",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;defaultIfNull;;;Argument[0..1];ReturnValue;value",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;firstNonNull;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;firstNonNull;;;ArrayElement of Argument[0];ReturnValue;value",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;getIfNull;;;Argument[0];ReturnValue;value",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;max;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;median;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;min;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;mode;;;Argument[0];ReturnValue;taint",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;max;;;ArrayElement of Argument[0];ReturnValue;value",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;median;;;ArrayElement of Argument[0];ReturnValue;value",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;min;;;ArrayElement of Argument[0];ReturnValue;value",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;mode;;;ArrayElement of Argument[0];ReturnValue;value",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;requireNonEmpty;;;Argument[0];ReturnValue;value",
|
||||
"org.apache.commons.lang3;ObjectUtils;false;toString;(Object,String);;Argument[1];ReturnValue;value"
|
||||
]
|
||||
|
||||
@@ -13,7 +13,8 @@ private class GuavaBaseCsv extends SummaryModelCsv {
|
||||
"com.google.common.base;Strings;false;padStart;(String,int,char);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Strings;false;padEnd;(String,int,char);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Strings;false;repeat;(String,int);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Strings;false;lenientFormat;(String,Object[]);;Argument;ReturnValue;taint",
|
||||
"com.google.common.base;Strings;false;lenientFormat;(String,Object[]);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Strings;false;lenientFormat;(String,Object[]);;ArrayElement of Argument[1];ReturnValue;taint",
|
||||
"com.google.common.base;Joiner;false;on;(String);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Joiner;false;skipNulls;();;Argument[-1];ReturnValue;taint",
|
||||
"com.google.common.base;Joiner;false;useForNull;(String);;Argument[-1];ReturnValue;taint",
|
||||
@@ -22,14 +23,14 @@ private class GuavaBaseCsv extends SummaryModelCsv {
|
||||
"com.google.common.base;Joiner;false;withKeyValueSeparator;(String);;Argument[-1];ReturnValue;taint",
|
||||
"com.google.common.base;Joiner;false;withKeyValueSeparator;(char);;Argument[-1];ReturnValue;taint",
|
||||
// Note: The signatures of some of the appendTo methods involve collection flow
|
||||
"com.google.common.base;Joiner;false;appendTo;;;Argument;Argument[0];taint",
|
||||
"com.google.common.base;Joiner;false;appendTo;;;Argument[-1..3];Argument[0];taint",
|
||||
"com.google.common.base;Joiner;false;appendTo;;;Argument[0];ReturnValue;value",
|
||||
"com.google.common.base;Joiner;false;join;;;Argument;ReturnValue;taint",
|
||||
"com.google.common.base;Joiner;false;join;;;Argument[-1..2];ReturnValue;taint",
|
||||
"com.google.common.base;Joiner$MapJoiner;false;useForNull;(String);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Joiner$MapJoiner;false;useForNull;(String);;Argument[-1];ReturnValue;taint",
|
||||
"com.google.common.base;Joiner$MapJoiner;false;appendTo;;;Argument;Argument[0];taint",
|
||||
"com.google.common.base;Joiner$MapJoiner;false;appendTo;;;Argument[1];Argument[0];taint",
|
||||
"com.google.common.base;Joiner$MapJoiner;false;appendTo;;;Argument[0];ReturnValue;value",
|
||||
"com.google.common.base;Joiner$MapJoiner;false;join;;;Argument;ReturnValue;taint",
|
||||
"com.google.common.base;Joiner$MapJoiner;false;join;;;Argument[-1..0];ReturnValue;taint",
|
||||
"com.google.common.base;Splitter;false;split;(CharSequence);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Splitter;false;splitToList;(CharSequence);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.base;Splitter;false;splitToStream;(CharSequence);;Argument[0];ReturnValue;taint",
|
||||
|
||||
@@ -61,7 +61,7 @@ private class GuavaIoCsv extends SummaryModelCsv {
|
||||
"com.google.common.io;Files;false;simplifyPath;(String);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.io;MoreFiles;false;getFileExtension;(Path);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.io;MoreFiles;false;getNameWithoutExtension;(Path);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.io;LineReader;false;LineReader;(Readable);;Argument[0];ReturnValue;taint",
|
||||
"com.google.common.io;LineReader;false;LineReader;(Readable);;Argument[0];Argument[-1];taint",
|
||||
"com.google.common.io;LineReader;true;readLine;();;Argument[-1];ReturnValue;taint",
|
||||
"com.google.common.io;ByteArrayDataOutput;true;toByteArray;();;Argument[-1];ReturnValue;taint",
|
||||
"com.google.common.io;ByteArrayDataOutput;true;write;(byte[]);;Argument[0];Argument[-1];taint",
|
||||
|
||||
@@ -1,7 +1,20 @@
|
||||
/* Definitions related to external processes. */
|
||||
import semmle.code.java.Member
|
||||
import semmle.code.java.JDK
|
||||
import semmle.code.java.frameworks.apache.Exec
|
||||
|
||||
private module Instances {
|
||||
private import semmle.code.java.JDK
|
||||
private import semmle.code.java.frameworks.apache.Exec
|
||||
}
|
||||
|
||||
/**
|
||||
* A callable that executes a command.
|
||||
*/
|
||||
abstract class ExecCallable extends Callable {
|
||||
/**
|
||||
* Gets the index of an argument that will be part of the command that is executed.
|
||||
*/
|
||||
abstract int getAnExecutedArgument();
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression used as an argument to a call that executes an external command. For calls to
|
||||
@@ -10,21 +23,10 @@ import semmle.code.java.frameworks.apache.Exec
|
||||
*/
|
||||
class ArgumentToExec extends Expr {
|
||||
ArgumentToExec() {
|
||||
exists(MethodAccess execCall, Method method |
|
||||
execCall.getArgument(0) = this and
|
||||
method = execCall.getMethod() and
|
||||
(
|
||||
method instanceof MethodRuntimeExec or
|
||||
method instanceof MethodProcessBuilderCommand or
|
||||
method instanceof MethodCommandLineParse or
|
||||
method instanceof MethodCommandLineAddArguments
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(ConstructorCall expr, Constructor cons |
|
||||
expr.getConstructor() = cons and
|
||||
cons.getDeclaringType().hasQualifiedName("java.lang", "ProcessBuilder") and
|
||||
expr.getArgument(0) = this
|
||||
exists(Call execCall, ExecCallable execCallable, int i |
|
||||
execCall.getArgument(pragma[only_bind_into](i)) = this and
|
||||
execCallable = execCall.getCallee() and
|
||||
i = execCallable.getAnExecutedArgument()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -1,48 +1,81 @@
|
||||
edges
|
||||
| XsltInjection.java:30:44:30:66 | getInputStream(...) : InputStream | XsltInjection.java:31:5:31:59 | newTransformer(...) |
|
||||
| XsltInjection.java:35:66:35:88 | getInputStream(...) : InputStream | XsltInjection.java:36:5:36:74 | newTransformer(...) |
|
||||
| XsltInjection.java:40:45:40:70 | param : String | XsltInjection.java:43:5:43:59 | newTransformer(...) |
|
||||
| XsltInjection.java:47:54:47:76 | getInputStream(...) : InputStream | XsltInjection.java:48:5:48:74 | newTransformer(...) |
|
||||
| XsltInjection.java:52:82:52:104 | getInputStream(...) : InputStream | XsltInjection.java:53:5:53:59 | newTransformer(...) |
|
||||
| XsltInjection.java:30:27:30:67 | new StreamSource(...) : StreamSource | XsltInjection.java:31:5:31:59 | newTransformer(...) |
|
||||
| XsltInjection.java:30:44:30:66 | getInputStream(...) : InputStream | XsltInjection.java:30:27:30:67 | new StreamSource(...) : StreamSource |
|
||||
| XsltInjection.java:35:27:35:90 | new StreamSource(...) : StreamSource | XsltInjection.java:36:5:36:74 | newTransformer(...) |
|
||||
| XsltInjection.java:35:44:35:89 | new InputStreamReader(...) : InputStreamReader | XsltInjection.java:35:27:35:90 | new StreamSource(...) : StreamSource |
|
||||
| XsltInjection.java:35:66:35:88 | getInputStream(...) : InputStream | XsltInjection.java:35:44:35:89 | new InputStreamReader(...) : InputStreamReader |
|
||||
| XsltInjection.java:40:45:40:70 | param : String | XsltInjection.java:42:61:42:64 | xslt : String |
|
||||
| XsltInjection.java:42:27:42:66 | new StreamSource(...) : StreamSource | XsltInjection.java:43:5:43:59 | newTransformer(...) |
|
||||
| XsltInjection.java:42:44:42:65 | new StringReader(...) : StringReader | XsltInjection.java:42:27:42:66 | new StreamSource(...) : StreamSource |
|
||||
| XsltInjection.java:42:61:42:64 | xslt : String | XsltInjection.java:42:44:42:65 | new StringReader(...) : StringReader |
|
||||
| XsltInjection.java:47:24:47:78 | new SAXSource(...) : SAXSource | XsltInjection.java:48:5:48:74 | newTransformer(...) |
|
||||
| XsltInjection.java:47:38:47:77 | new InputSource(...) : InputSource | XsltInjection.java:47:24:47:78 | new SAXSource(...) : SAXSource |
|
||||
| XsltInjection.java:47:54:47:76 | getInputStream(...) : InputStream | XsltInjection.java:47:38:47:77 | new InputSource(...) : InputSource |
|
||||
| XsltInjection.java:52:24:52:107 | new SAXSource(...) : SAXSource | XsltInjection.java:53:5:53:59 | newTransformer(...) |
|
||||
| XsltInjection.java:52:44:52:106 | new InputSource(...) : InputSource | XsltInjection.java:52:24:52:107 | new SAXSource(...) : SAXSource |
|
||||
| XsltInjection.java:52:60:52:105 | new InputStreamReader(...) : InputStreamReader | XsltInjection.java:52:44:52:106 | new InputSource(...) : InputSource |
|
||||
| XsltInjection.java:52:82:52:104 | getInputStream(...) : InputStream | XsltInjection.java:52:60:52:105 | new InputStreamReader(...) : InputStreamReader |
|
||||
| XsltInjection.java:57:91:57:113 | getInputStream(...) : InputStream | XsltInjection.java:58:5:58:59 | newTransformer(...) |
|
||||
| XsltInjection.java:62:120:62:142 | getInputStream(...) : InputStream | XsltInjection.java:63:5:63:74 | newTransformer(...) |
|
||||
| XsltInjection.java:62:98:62:143 | new InputStreamReader(...) : InputStreamReader | XsltInjection.java:63:5:63:74 | newTransformer(...) |
|
||||
| XsltInjection.java:62:120:62:142 | getInputStream(...) : InputStream | XsltInjection.java:62:98:62:143 | new InputStreamReader(...) : InputStreamReader |
|
||||
| XsltInjection.java:67:102:67:124 | getInputStream(...) : InputStream | XsltInjection.java:68:5:68:59 | newTransformer(...) |
|
||||
| XsltInjection.java:72:44:72:66 | getInputStream(...) : InputStream | XsltInjection.java:76:5:76:34 | newTransformer(...) |
|
||||
| XsltInjection.java:80:44:80:66 | getInputStream(...) : InputStream | XsltInjection.java:83:5:83:34 | newTransformer(...) |
|
||||
| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:90:5:90:35 | load(...) |
|
||||
| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:91:5:91:37 | load30(...) |
|
||||
| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:92:5:92:37 | load30(...) |
|
||||
| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:93:5:93:37 | load30(...) |
|
||||
| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:94:5:94:37 | load30(...) |
|
||||
| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:95:5:95:37 | load30(...) |
|
||||
| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:96:5:96:37 | load30(...) |
|
||||
| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:97:5:97:37 | load30(...) |
|
||||
| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:98:5:98:37 | load30(...) |
|
||||
| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:99:5:99:37 | load30(...) |
|
||||
| XsltInjection.java:103:36:103:61 | param : String | XsltInjection.java:108:5:108:46 | load(...) |
|
||||
| XsltInjection.java:103:36:103:61 | param : String | XsltInjection.java:110:5:110:50 | load(...) |
|
||||
| XsltInjection.java:105:44:105:66 | getInputStream(...) : InputStream | XsltInjection.java:109:5:109:49 | load(...) |
|
||||
| XsltInjection.java:72:27:72:67 | new StreamSource(...) : StreamSource | XsltInjection.java:76:5:76:34 | newTransformer(...) |
|
||||
| XsltInjection.java:72:44:72:66 | getInputStream(...) : InputStream | XsltInjection.java:72:27:72:67 | new StreamSource(...) : StreamSource |
|
||||
| XsltInjection.java:80:27:80:67 | new StreamSource(...) : StreamSource | XsltInjection.java:83:5:83:34 | newTransformer(...) |
|
||||
| XsltInjection.java:80:44:80:66 | getInputStream(...) : InputStream | XsltInjection.java:80:27:80:67 | new StreamSource(...) : StreamSource |
|
||||
| XsltInjection.java:87:27:87:67 | new StreamSource(...) : StreamSource | XsltInjection.java:90:5:90:35 | load(...) |
|
||||
| XsltInjection.java:87:27:87:67 | new StreamSource(...) : StreamSource | XsltInjection.java:91:5:91:37 | load30(...) |
|
||||
| XsltInjection.java:87:27:87:67 | new StreamSource(...) : StreamSource | XsltInjection.java:92:5:92:37 | load30(...) |
|
||||
| XsltInjection.java:87:27:87:67 | new StreamSource(...) : StreamSource | XsltInjection.java:93:5:93:37 | load30(...) |
|
||||
| XsltInjection.java:87:27:87:67 | new StreamSource(...) : StreamSource | XsltInjection.java:94:5:94:37 | load30(...) |
|
||||
| XsltInjection.java:87:27:87:67 | new StreamSource(...) : StreamSource | XsltInjection.java:95:5:95:37 | load30(...) |
|
||||
| XsltInjection.java:87:27:87:67 | new StreamSource(...) : StreamSource | XsltInjection.java:96:5:96:37 | load30(...) |
|
||||
| XsltInjection.java:87:27:87:67 | new StreamSource(...) : StreamSource | XsltInjection.java:97:5:97:37 | load30(...) |
|
||||
| XsltInjection.java:87:27:87:67 | new StreamSource(...) : StreamSource | XsltInjection.java:98:5:98:37 | load30(...) |
|
||||
| XsltInjection.java:87:27:87:67 | new StreamSource(...) : StreamSource | XsltInjection.java:99:5:99:37 | load30(...) |
|
||||
| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | XsltInjection.java:87:27:87:67 | new StreamSource(...) : StreamSource |
|
||||
| XsltInjection.java:103:36:103:61 | param : String | XsltInjection.java:104:23:104:27 | param : String |
|
||||
| XsltInjection.java:104:15:104:28 | new URI(...) : URI | XsltInjection.java:108:5:108:46 | load(...) |
|
||||
| XsltInjection.java:104:15:104:28 | new URI(...) : URI | XsltInjection.java:110:5:110:50 | load(...) |
|
||||
| XsltInjection.java:104:23:104:27 | param : String | XsltInjection.java:104:15:104:28 | new URI(...) : URI |
|
||||
| XsltInjection.java:105:27:105:67 | new StreamSource(...) : StreamSource | XsltInjection.java:109:5:109:49 | load(...) |
|
||||
| XsltInjection.java:105:44:105:66 | getInputStream(...) : InputStream | XsltInjection.java:105:27:105:67 | new StreamSource(...) : StreamSource |
|
||||
nodes
|
||||
| XsltInjection.java:30:27:30:67 | new StreamSource(...) : StreamSource | semmle.label | new StreamSource(...) : StreamSource |
|
||||
| XsltInjection.java:30:44:30:66 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| XsltInjection.java:31:5:31:59 | newTransformer(...) | semmle.label | newTransformer(...) |
|
||||
| XsltInjection.java:35:27:35:90 | new StreamSource(...) : StreamSource | semmle.label | new StreamSource(...) : StreamSource |
|
||||
| XsltInjection.java:35:44:35:89 | new InputStreamReader(...) : InputStreamReader | semmle.label | new InputStreamReader(...) : InputStreamReader |
|
||||
| XsltInjection.java:35:66:35:88 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| XsltInjection.java:36:5:36:74 | newTransformer(...) | semmle.label | newTransformer(...) |
|
||||
| XsltInjection.java:40:45:40:70 | param : String | semmle.label | param : String |
|
||||
| XsltInjection.java:42:27:42:66 | new StreamSource(...) : StreamSource | semmle.label | new StreamSource(...) : StreamSource |
|
||||
| XsltInjection.java:42:44:42:65 | new StringReader(...) : StringReader | semmle.label | new StringReader(...) : StringReader |
|
||||
| XsltInjection.java:42:61:42:64 | xslt : String | semmle.label | xslt : String |
|
||||
| XsltInjection.java:43:5:43:59 | newTransformer(...) | semmle.label | newTransformer(...) |
|
||||
| XsltInjection.java:47:24:47:78 | new SAXSource(...) : SAXSource | semmle.label | new SAXSource(...) : SAXSource |
|
||||
| XsltInjection.java:47:38:47:77 | new InputSource(...) : InputSource | semmle.label | new InputSource(...) : InputSource |
|
||||
| XsltInjection.java:47:54:47:76 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| XsltInjection.java:48:5:48:74 | newTransformer(...) | semmle.label | newTransformer(...) |
|
||||
| XsltInjection.java:52:24:52:107 | new SAXSource(...) : SAXSource | semmle.label | new SAXSource(...) : SAXSource |
|
||||
| XsltInjection.java:52:44:52:106 | new InputSource(...) : InputSource | semmle.label | new InputSource(...) : InputSource |
|
||||
| XsltInjection.java:52:60:52:105 | new InputStreamReader(...) : InputStreamReader | semmle.label | new InputStreamReader(...) : InputStreamReader |
|
||||
| XsltInjection.java:52:82:52:104 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| XsltInjection.java:53:5:53:59 | newTransformer(...) | semmle.label | newTransformer(...) |
|
||||
| XsltInjection.java:57:91:57:113 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| XsltInjection.java:58:5:58:59 | newTransformer(...) | semmle.label | newTransformer(...) |
|
||||
| XsltInjection.java:62:98:62:143 | new InputStreamReader(...) : InputStreamReader | semmle.label | new InputStreamReader(...) : InputStreamReader |
|
||||
| XsltInjection.java:62:120:62:142 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| XsltInjection.java:63:5:63:74 | newTransformer(...) | semmle.label | newTransformer(...) |
|
||||
| XsltInjection.java:67:102:67:124 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| XsltInjection.java:68:5:68:59 | newTransformer(...) | semmle.label | newTransformer(...) |
|
||||
| XsltInjection.java:72:27:72:67 | new StreamSource(...) : StreamSource | semmle.label | new StreamSource(...) : StreamSource |
|
||||
| XsltInjection.java:72:44:72:66 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| XsltInjection.java:76:5:76:34 | newTransformer(...) | semmle.label | newTransformer(...) |
|
||||
| XsltInjection.java:80:27:80:67 | new StreamSource(...) : StreamSource | semmle.label | new StreamSource(...) : StreamSource |
|
||||
| XsltInjection.java:80:44:80:66 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| XsltInjection.java:83:5:83:34 | newTransformer(...) | semmle.label | newTransformer(...) |
|
||||
| XsltInjection.java:87:27:87:67 | new StreamSource(...) : StreamSource | semmle.label | new StreamSource(...) : StreamSource |
|
||||
| XsltInjection.java:87:44:87:66 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| XsltInjection.java:90:5:90:35 | load(...) | semmle.label | load(...) |
|
||||
| XsltInjection.java:91:5:91:37 | load30(...) | semmle.label | load30(...) |
|
||||
@@ -55,6 +88,9 @@ nodes
|
||||
| XsltInjection.java:98:5:98:37 | load30(...) | semmle.label | load30(...) |
|
||||
| XsltInjection.java:99:5:99:37 | load30(...) | semmle.label | load30(...) |
|
||||
| XsltInjection.java:103:36:103:61 | param : String | semmle.label | param : String |
|
||||
| XsltInjection.java:104:15:104:28 | new URI(...) : URI | semmle.label | new URI(...) : URI |
|
||||
| XsltInjection.java:104:23:104:27 | param : String | semmle.label | param : String |
|
||||
| XsltInjection.java:105:27:105:67 | new StreamSource(...) : StreamSource | semmle.label | new StreamSource(...) : StreamSource |
|
||||
| XsltInjection.java:105:44:105:66 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| XsltInjection.java:108:5:108:46 | load(...) | semmle.label | load(...) |
|
||||
| XsltInjection.java:109:5:109:49 | load(...) | semmle.label | load(...) |
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import groovy.lang.GroovyClassLoader;
|
||||
import groovy.lang.GroovyCodeSource;
|
||||
import groovy.lang.GroovyObject;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
public class GroovyClassLoaderTest extends HttpServlet {
|
||||
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
try {
|
||||
String script = request.getParameter("script");
|
||||
final GroovyClassLoader classLoader = new GroovyClassLoader();
|
||||
Class groovy = classLoader.parseClass(script);
|
||||
GroovyObject groovyObj = (GroovyObject) groovy.newInstance();
|
||||
|
||||
} catch (Exception e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
protected void doPost(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
try {
|
||||
String script = request.getParameter("script");
|
||||
final GroovyClassLoader classLoader = new GroovyClassLoader();
|
||||
GroovyCodeSource gcs = new GroovyCodeSource(script, "test", "Test");
|
||||
Class groovy = classLoader.parseClass(gcs);
|
||||
GroovyObject groovyObj = (GroovyObject) groovy.newInstance();
|
||||
} catch (Exception e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import groovy.util.Eval;
|
||||
|
||||
public class GroovyEvalTest extends HttpServlet {
|
||||
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
String script = request.getParameter("script");
|
||||
Eval.me(script);
|
||||
}
|
||||
|
||||
protected void doPost(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
String script = request.getParameter("script");
|
||||
Eval.me("test", "result", script);
|
||||
}
|
||||
|
||||
protected void doPut(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
String script = request.getParameter("script");
|
||||
Eval.x("result2", script);
|
||||
|
||||
}
|
||||
|
||||
protected void doDelete(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
String script = request.getParameter("script");
|
||||
Eval.xy("result3", "result4", script);
|
||||
}
|
||||
|
||||
protected void doPatch(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
String script = request.getParameter("script");
|
||||
Eval.xyz("result3", "result4", "aaa", script);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
edges
|
||||
| ../../../stubs/groovy-all-3.0.7/groovy/util/Eval.java:22:29:22:51 | expression : String | ../../../stubs/groovy-all-3.0.7/groovy/util/Eval.java:23:31:23:40 | expression |
|
||||
| ../../../stubs/groovy-all-3.0.7/groovy/util/Eval.java:30:44:30:66 | expression : String | ../../../stubs/groovy-all-3.0.7/groovy/util/Eval.java:31:27:31:36 | expression |
|
||||
| GroovyClassLoaderTest.java:16:29:16:58 | getParameter(...) : String | GroovyClassLoaderTest.java:18:51:18:56 | script |
|
||||
| GroovyClassLoaderTest.java:29:29:29:58 | getParameter(...) : String | GroovyClassLoaderTest.java:32:51:32:53 | gcs |
|
||||
| GroovyEvalTest.java:12:25:12:54 | getParameter(...) : String | GroovyEvalTest.java:13:17:13:22 | script |
|
||||
| GroovyEvalTest.java:12:25:12:54 | getParameter(...) : String | GroovyEvalTest.java:13:17:13:22 | script : String |
|
||||
| GroovyEvalTest.java:13:17:13:22 | script : String | ../../../stubs/groovy-all-3.0.7/groovy/util/Eval.java:22:29:22:51 | expression : String |
|
||||
| GroovyEvalTest.java:18:25:18:54 | getParameter(...) : String | GroovyEvalTest.java:19:35:19:40 | script |
|
||||
| GroovyEvalTest.java:24:25:24:54 | getParameter(...) : String | GroovyEvalTest.java:25:27:25:32 | script |
|
||||
| GroovyEvalTest.java:24:25:24:54 | getParameter(...) : String | GroovyEvalTest.java:25:27:25:32 | script : String |
|
||||
| GroovyEvalTest.java:25:27:25:32 | script : String | ../../../stubs/groovy-all-3.0.7/groovy/util/Eval.java:30:44:30:66 | expression : String |
|
||||
| GroovyEvalTest.java:31:25:31:54 | getParameter(...) : String | GroovyEvalTest.java:32:39:32:44 | script |
|
||||
| GroovyEvalTest.java:37:25:37:54 | getParameter(...) : String | GroovyEvalTest.java:38:47:38:52 | script |
|
||||
| GroovyShellTest.java:15:25:15:54 | getParameter(...) : String | GroovyShellTest.java:16:24:16:29 | script |
|
||||
| GroovyShellTest.java:22:25:22:54 | getParameter(...) : String | GroovyShellTest.java:23:24:23:29 | script |
|
||||
| GroovyShellTest.java:29:25:29:54 | getParameter(...) : String | GroovyShellTest.java:30:24:30:29 | script |
|
||||
| GroovyShellTest.java:36:25:36:54 | getParameter(...) : String | GroovyShellTest.java:37:19:37:24 | script |
|
||||
| GroovyShellTest.java:43:25:43:54 | getParameter(...) : String | GroovyShellTest.java:45:19:45:21 | gcs |
|
||||
| GroovyShellTest.java:51:25:51:54 | getParameter(...) : String | GroovyShellTest.java:53:24:53:26 | gcs |
|
||||
| GroovyShellTest.java:59:25:59:54 | getParameter(...) : String | GroovyShellTest.java:60:21:60:26 | script |
|
||||
nodes
|
||||
| ../../../stubs/groovy-all-3.0.7/groovy/util/Eval.java:22:29:22:51 | expression : String | semmle.label | expression : String |
|
||||
| ../../../stubs/groovy-all-3.0.7/groovy/util/Eval.java:23:31:23:40 | expression | semmle.label | expression |
|
||||
| ../../../stubs/groovy-all-3.0.7/groovy/util/Eval.java:30:44:30:66 | expression : String | semmle.label | expression : String |
|
||||
| ../../../stubs/groovy-all-3.0.7/groovy/util/Eval.java:31:27:31:36 | expression | semmle.label | expression |
|
||||
| GroovyClassLoaderTest.java:16:29:16:58 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| GroovyClassLoaderTest.java:18:51:18:56 | script | semmle.label | script |
|
||||
| GroovyClassLoaderTest.java:29:29:29:58 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| GroovyClassLoaderTest.java:32:51:32:53 | gcs | semmle.label | gcs |
|
||||
| GroovyEvalTest.java:12:25:12:54 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| GroovyEvalTest.java:13:17:13:22 | script | semmle.label | script |
|
||||
| GroovyEvalTest.java:13:17:13:22 | script : String | semmle.label | script : String |
|
||||
| GroovyEvalTest.java:18:25:18:54 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| GroovyEvalTest.java:19:35:19:40 | script | semmle.label | script |
|
||||
| GroovyEvalTest.java:24:25:24:54 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| GroovyEvalTest.java:25:27:25:32 | script | semmle.label | script |
|
||||
| GroovyEvalTest.java:25:27:25:32 | script : String | semmle.label | script : String |
|
||||
| GroovyEvalTest.java:31:25:31:54 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| GroovyEvalTest.java:32:39:32:44 | script | semmle.label | script |
|
||||
| GroovyEvalTest.java:37:25:37:54 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| GroovyEvalTest.java:38:47:38:52 | script | semmle.label | script |
|
||||
| GroovyShellTest.java:15:25:15:54 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| GroovyShellTest.java:16:24:16:29 | script | semmle.label | script |
|
||||
| GroovyShellTest.java:22:25:22:54 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| GroovyShellTest.java:23:24:23:29 | script | semmle.label | script |
|
||||
| GroovyShellTest.java:29:25:29:54 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| GroovyShellTest.java:30:24:30:29 | script | semmle.label | script |
|
||||
| GroovyShellTest.java:36:25:36:54 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| GroovyShellTest.java:37:19:37:24 | script | semmle.label | script |
|
||||
| GroovyShellTest.java:43:25:43:54 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| GroovyShellTest.java:45:19:45:21 | gcs | semmle.label | gcs |
|
||||
| GroovyShellTest.java:51:25:51:54 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| GroovyShellTest.java:53:24:53:26 | gcs | semmle.label | gcs |
|
||||
| GroovyShellTest.java:59:25:59:54 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| GroovyShellTest.java:60:21:60:26 | script | semmle.label | script |
|
||||
#select
|
||||
| ../../../stubs/groovy-all-3.0.7/groovy/util/Eval.java:23:31:23:40 | expression | GroovyEvalTest.java:12:25:12:54 | getParameter(...) : String | ../../../stubs/groovy-all-3.0.7/groovy/util/Eval.java:23:31:23:40 | expression | Groovy Injection from $@. | GroovyEvalTest.java:12:25:12:54 | getParameter(...) | this user input |
|
||||
| ../../../stubs/groovy-all-3.0.7/groovy/util/Eval.java:31:27:31:36 | expression | GroovyEvalTest.java:24:25:24:54 | getParameter(...) : String | ../../../stubs/groovy-all-3.0.7/groovy/util/Eval.java:31:27:31:36 | expression | Groovy Injection from $@. | GroovyEvalTest.java:24:25:24:54 | getParameter(...) | this user input |
|
||||
| GroovyClassLoaderTest.java:18:51:18:56 | script | GroovyClassLoaderTest.java:16:29:16:58 | getParameter(...) : String | GroovyClassLoaderTest.java:18:51:18:56 | script | Groovy Injection from $@. | GroovyClassLoaderTest.java:16:29:16:58 | getParameter(...) | this user input |
|
||||
| GroovyClassLoaderTest.java:32:51:32:53 | gcs | GroovyClassLoaderTest.java:29:29:29:58 | getParameter(...) : String | GroovyClassLoaderTest.java:32:51:32:53 | gcs | Groovy Injection from $@. | GroovyClassLoaderTest.java:29:29:29:58 | getParameter(...) | this user input |
|
||||
| GroovyEvalTest.java:13:17:13:22 | script | GroovyEvalTest.java:12:25:12:54 | getParameter(...) : String | GroovyEvalTest.java:13:17:13:22 | script | Groovy Injection from $@. | GroovyEvalTest.java:12:25:12:54 | getParameter(...) | this user input |
|
||||
| GroovyEvalTest.java:19:35:19:40 | script | GroovyEvalTest.java:18:25:18:54 | getParameter(...) : String | GroovyEvalTest.java:19:35:19:40 | script | Groovy Injection from $@. | GroovyEvalTest.java:18:25:18:54 | getParameter(...) | this user input |
|
||||
| GroovyEvalTest.java:25:27:25:32 | script | GroovyEvalTest.java:24:25:24:54 | getParameter(...) : String | GroovyEvalTest.java:25:27:25:32 | script | Groovy Injection from $@. | GroovyEvalTest.java:24:25:24:54 | getParameter(...) | this user input |
|
||||
| GroovyEvalTest.java:32:39:32:44 | script | GroovyEvalTest.java:31:25:31:54 | getParameter(...) : String | GroovyEvalTest.java:32:39:32:44 | script | Groovy Injection from $@. | GroovyEvalTest.java:31:25:31:54 | getParameter(...) | this user input |
|
||||
| GroovyEvalTest.java:38:47:38:52 | script | GroovyEvalTest.java:37:25:37:54 | getParameter(...) : String | GroovyEvalTest.java:38:47:38:52 | script | Groovy Injection from $@. | GroovyEvalTest.java:37:25:37:54 | getParameter(...) | this user input |
|
||||
| GroovyShellTest.java:16:24:16:29 | script | GroovyShellTest.java:15:25:15:54 | getParameter(...) : String | GroovyShellTest.java:16:24:16:29 | script | Groovy Injection from $@. | GroovyShellTest.java:15:25:15:54 | getParameter(...) | this user input |
|
||||
| GroovyShellTest.java:23:24:23:29 | script | GroovyShellTest.java:22:25:22:54 | getParameter(...) : String | GroovyShellTest.java:23:24:23:29 | script | Groovy Injection from $@. | GroovyShellTest.java:22:25:22:54 | getParameter(...) | this user input |
|
||||
| GroovyShellTest.java:30:24:30:29 | script | GroovyShellTest.java:29:25:29:54 | getParameter(...) : String | GroovyShellTest.java:30:24:30:29 | script | Groovy Injection from $@. | GroovyShellTest.java:29:25:29:54 | getParameter(...) | this user input |
|
||||
| GroovyShellTest.java:37:19:37:24 | script | GroovyShellTest.java:36:25:36:54 | getParameter(...) : String | GroovyShellTest.java:37:19:37:24 | script | Groovy Injection from $@. | GroovyShellTest.java:36:25:36:54 | getParameter(...) | this user input |
|
||||
| GroovyShellTest.java:45:19:45:21 | gcs | GroovyShellTest.java:43:25:43:54 | getParameter(...) : String | GroovyShellTest.java:45:19:45:21 | gcs | Groovy Injection from $@. | GroovyShellTest.java:43:25:43:54 | getParameter(...) | this user input |
|
||||
| GroovyShellTest.java:53:24:53:26 | gcs | GroovyShellTest.java:51:25:51:54 | getParameter(...) : String | GroovyShellTest.java:53:24:53:26 | gcs | Groovy Injection from $@. | GroovyShellTest.java:51:25:51:54 | getParameter(...) | this user input |
|
||||
| GroovyShellTest.java:60:21:60:26 | script | GroovyShellTest.java:59:25:59:54 | getParameter(...) : String | GroovyShellTest.java:60:21:60:26 | script | Groovy Injection from $@. | GroovyShellTest.java:59:25:59:54 | getParameter(...) | this user input |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-094/GroovyInjection.ql
|
||||
@@ -0,0 +1,63 @@
|
||||
import groovy.lang.GroovyCodeSource;
|
||||
import groovy.lang.GroovyShell;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
public class GroovyShellTest extends HttpServlet {
|
||||
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
GroovyShell shell = new GroovyShell();
|
||||
String script = request.getParameter("script");
|
||||
shell.evaluate(script);
|
||||
}
|
||||
|
||||
protected void doPost(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
GroovyShell shell = new GroovyShell();
|
||||
String script = request.getParameter("script");
|
||||
shell.evaluate(script, "test");
|
||||
}
|
||||
|
||||
protected void doPut(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
GroovyShell shell = new GroovyShell();
|
||||
String script = request.getParameter("script");
|
||||
shell.evaluate(script, "test", "test2");
|
||||
}
|
||||
|
||||
protected void doOptions(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
GroovyShell shell = new GroovyShell();
|
||||
String script = request.getParameter("script");
|
||||
shell.run(script, "_", new String[]{});
|
||||
}
|
||||
|
||||
protected void doHead(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
GroovyShell shell = new GroovyShell();
|
||||
String script = request.getParameter("script");
|
||||
GroovyCodeSource gcs = new GroovyCodeSource(script, "test", "Test");
|
||||
shell.run(gcs, new String[]{});
|
||||
}
|
||||
|
||||
protected void doDelete(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
GroovyShell shell = new GroovyShell();
|
||||
String script = request.getParameter("script");
|
||||
GroovyCodeSource gcs = new GroovyCodeSource(script, "test", "Test");
|
||||
shell.evaluate(gcs);
|
||||
}
|
||||
|
||||
protected void doPatch(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
GroovyShell shell = new GroovyShell();
|
||||
String script = request.getParameter("script");
|
||||
shell.parse(script);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
edges
|
||||
| JakartaExpressionInjection.java:23:25:23:47 | getInputStream(...) : InputStream | JakartaExpressionInjection.java:23:54:23:58 | bytes [post update] : byte[] |
|
||||
| JakartaExpressionInjection.java:23:54:23:58 | bytes [post update] : byte[] | JakartaExpressionInjection.java:25:31:25:40 | expression : String |
|
||||
| JakartaExpressionInjection.java:25:31:25:40 | expression : String | JakartaExpressionInjection.java:32:24:32:33 | expression : String |
|
||||
| JakartaExpressionInjection.java:25:31:25:40 | expression : String | JakartaExpressionInjection.java:40:24:40:33 | expression : String |
|
||||
| JakartaExpressionInjection.java:25:31:25:40 | expression : String | JakartaExpressionInjection.java:48:24:48:33 | expression : String |
|
||||
| JakartaExpressionInjection.java:25:31:25:40 | expression : String | JakartaExpressionInjection.java:59:24:59:33 | expression : String |
|
||||
| JakartaExpressionInjection.java:25:31:25:40 | expression : String | JakartaExpressionInjection.java:67:24:67:33 | expression : String |
|
||||
| JakartaExpressionInjection.java:25:31:25:40 | expression : String | JakartaExpressionInjection.java:75:24:75:33 | expression : String |
|
||||
| JakartaExpressionInjection.java:25:31:25:40 | expression : String | JakartaExpressionInjection.java:85:24:85:33 | expression : String |
|
||||
| JakartaExpressionInjection.java:25:31:25:40 | expression : String | JakartaExpressionInjection.java:95:24:95:33 | expression : String |
|
||||
| JakartaExpressionInjection.java:32:24:32:33 | expression : String | JakartaExpressionInjection.java:34:28:34:37 | expression |
|
||||
| JakartaExpressionInjection.java:40:24:40:33 | expression : String | JakartaExpressionInjection.java:42:32:42:41 | expression |
|
||||
| JakartaExpressionInjection.java:48:24:48:33 | expression : String | JakartaExpressionInjection.java:53:13:53:28 | lambdaExpression |
|
||||
| JakartaExpressionInjection.java:59:24:59:33 | expression : String | JakartaExpressionInjection.java:61:32:61:41 | expression |
|
||||
| JakartaExpressionInjection.java:67:24:67:33 | expression : String | JakartaExpressionInjection.java:69:43:69:52 | expression |
|
||||
| JakartaExpressionInjection.java:75:24:75:33 | expression : String | JakartaExpressionInjection.java:79:13:79:13 | e |
|
||||
| JakartaExpressionInjection.java:85:24:85:33 | expression : String | JakartaExpressionInjection.java:89:13:89:13 | e |
|
||||
| JakartaExpressionInjection.java:95:24:95:33 | expression : String | JakartaExpressionInjection.java:99:13:99:13 | e |
|
||||
nodes
|
||||
| JakartaExpressionInjection.java:23:25:23:47 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| JakartaExpressionInjection.java:23:54:23:58 | bytes [post update] : byte[] | semmle.label | bytes [post update] : byte[] |
|
||||
| JakartaExpressionInjection.java:25:31:25:40 | expression : String | semmle.label | expression : String |
|
||||
| JakartaExpressionInjection.java:32:24:32:33 | expression : String | semmle.label | expression : String |
|
||||
| JakartaExpressionInjection.java:34:28:34:37 | expression | semmle.label | expression |
|
||||
| JakartaExpressionInjection.java:40:24:40:33 | expression : String | semmle.label | expression : String |
|
||||
| JakartaExpressionInjection.java:42:32:42:41 | expression | semmle.label | expression |
|
||||
| JakartaExpressionInjection.java:48:24:48:33 | expression : String | semmle.label | expression : String |
|
||||
| JakartaExpressionInjection.java:53:13:53:28 | lambdaExpression | semmle.label | lambdaExpression |
|
||||
| JakartaExpressionInjection.java:59:24:59:33 | expression : String | semmle.label | expression : String |
|
||||
| JakartaExpressionInjection.java:61:32:61:41 | expression | semmle.label | expression |
|
||||
| JakartaExpressionInjection.java:67:24:67:33 | expression : String | semmle.label | expression : String |
|
||||
| JakartaExpressionInjection.java:69:43:69:52 | expression | semmle.label | expression |
|
||||
| JakartaExpressionInjection.java:75:24:75:33 | expression : String | semmle.label | expression : String |
|
||||
| JakartaExpressionInjection.java:79:13:79:13 | e | semmle.label | e |
|
||||
| JakartaExpressionInjection.java:85:24:85:33 | expression : String | semmle.label | expression : String |
|
||||
| JakartaExpressionInjection.java:89:13:89:13 | e | semmle.label | e |
|
||||
| JakartaExpressionInjection.java:95:24:95:33 | expression : String | semmle.label | expression : String |
|
||||
| JakartaExpressionInjection.java:99:13:99:13 | e | semmle.label | e |
|
||||
#select
|
||||
| JakartaExpressionInjection.java:34:28:34:37 | expression | JakartaExpressionInjection.java:23:25:23:47 | getInputStream(...) : InputStream | JakartaExpressionInjection.java:34:28:34:37 | expression | Jakarta Expression Language injection from $@. | JakartaExpressionInjection.java:23:25:23:47 | getInputStream(...) | this user input |
|
||||
| JakartaExpressionInjection.java:42:32:42:41 | expression | JakartaExpressionInjection.java:23:25:23:47 | getInputStream(...) : InputStream | JakartaExpressionInjection.java:42:32:42:41 | expression | Jakarta Expression Language injection from $@. | JakartaExpressionInjection.java:23:25:23:47 | getInputStream(...) | this user input |
|
||||
| JakartaExpressionInjection.java:53:13:53:28 | lambdaExpression | JakartaExpressionInjection.java:23:25:23:47 | getInputStream(...) : InputStream | JakartaExpressionInjection.java:53:13:53:28 | lambdaExpression | Jakarta Expression Language injection from $@. | JakartaExpressionInjection.java:23:25:23:47 | getInputStream(...) | this user input |
|
||||
| JakartaExpressionInjection.java:61:32:61:41 | expression | JakartaExpressionInjection.java:23:25:23:47 | getInputStream(...) : InputStream | JakartaExpressionInjection.java:61:32:61:41 | expression | Jakarta Expression Language injection from $@. | JakartaExpressionInjection.java:23:25:23:47 | getInputStream(...) | this user input |
|
||||
| JakartaExpressionInjection.java:69:43:69:52 | expression | JakartaExpressionInjection.java:23:25:23:47 | getInputStream(...) : InputStream | JakartaExpressionInjection.java:69:43:69:52 | expression | Jakarta Expression Language injection from $@. | JakartaExpressionInjection.java:23:25:23:47 | getInputStream(...) | this user input |
|
||||
| JakartaExpressionInjection.java:79:13:79:13 | e | JakartaExpressionInjection.java:23:25:23:47 | getInputStream(...) : InputStream | JakartaExpressionInjection.java:79:13:79:13 | e | Jakarta Expression Language injection from $@. | JakartaExpressionInjection.java:23:25:23:47 | getInputStream(...) | this user input |
|
||||
| JakartaExpressionInjection.java:89:13:89:13 | e | JakartaExpressionInjection.java:23:25:23:47 | getInputStream(...) : InputStream | JakartaExpressionInjection.java:89:13:89:13 | e | Jakarta Expression Language injection from $@. | JakartaExpressionInjection.java:23:25:23:47 | getInputStream(...) | this user input |
|
||||
| JakartaExpressionInjection.java:99:13:99:13 | e | JakartaExpressionInjection.java:23:25:23:47 | getInputStream(...) : InputStream | JakartaExpressionInjection.java:99:13:99:13 | e | Jakarta Expression Language injection from $@. | JakartaExpressionInjection.java:23:25:23:47 | getInputStream(...) | this user input |
|
||||
@@ -0,0 +1,103 @@
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.el.ELContext;
|
||||
import javax.el.ELManager;
|
||||
import javax.el.ELProcessor;
|
||||
import javax.el.ExpressionFactory;
|
||||
import javax.el.LambdaExpression;
|
||||
import javax.el.MethodExpression;
|
||||
import javax.el.StandardELContext;
|
||||
import javax.el.ValueExpression;
|
||||
|
||||
public class JakartaExpressionInjection {
|
||||
|
||||
// calls a consumer with a string received from a socket
|
||||
private static void testWithSocket(Consumer<String> action) throws IOException {
|
||||
try (ServerSocket serverSocket = new ServerSocket(0)) {
|
||||
try (Socket socket = serverSocket.accept()) {
|
||||
byte[] bytes = new byte[1024];
|
||||
int n = socket.getInputStream().read(bytes);
|
||||
String expression = new String(bytes, 0, n);
|
||||
action.accept(expression);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BAD (untrusted input to ELProcessor.eval)
|
||||
private static void testWithELProcessorEval() throws IOException {
|
||||
testWithSocket(expression -> {
|
||||
ELProcessor processor = new ELProcessor();
|
||||
processor.eval(expression);
|
||||
});
|
||||
}
|
||||
|
||||
// BAD (untrusted input to ELProcessor.getValue)
|
||||
private static void testWithELProcessorGetValue() throws IOException {
|
||||
testWithSocket(expression -> {
|
||||
ELProcessor processor = new ELProcessor();
|
||||
processor.getValue(expression, Object.class);
|
||||
});
|
||||
}
|
||||
|
||||
// BAD (untrusted input to LambdaExpression.invoke)
|
||||
private static void testWithLambdaExpressionInvoke() throws IOException {
|
||||
testWithSocket(expression -> {
|
||||
ExpressionFactory factory = ELManager.getExpressionFactory();
|
||||
StandardELContext context = new StandardELContext(factory);
|
||||
ValueExpression valueExpression = factory.createValueExpression(context, expression, Object.class);
|
||||
LambdaExpression lambdaExpression = new LambdaExpression(new ArrayList<>(), valueExpression);
|
||||
lambdaExpression.invoke(context, new Object[0]);
|
||||
});
|
||||
}
|
||||
|
||||
// BAD (untrusted input to ELProcessor.setValue)
|
||||
private static void testWithELProcessorSetValue() throws IOException {
|
||||
testWithSocket(expression -> {
|
||||
ELProcessor processor = new ELProcessor();
|
||||
processor.setValue(expression, new Object());
|
||||
});
|
||||
}
|
||||
|
||||
// BAD (untrusted input to ELProcessor.setVariable)
|
||||
private static void testWithELProcessorSetVariable() throws IOException {
|
||||
testWithSocket(expression -> {
|
||||
ELProcessor processor = new ELProcessor();
|
||||
processor.setVariable("test", expression);
|
||||
});
|
||||
}
|
||||
|
||||
// BAD (untrusted input to ValueExpression.getValue when it was created by JUEL)
|
||||
private static void testWithJuelValueExpressionGetValue() throws IOException {
|
||||
testWithSocket(expression -> {
|
||||
ExpressionFactory factory = new de.odysseus.el.ExpressionFactoryImpl();
|
||||
ELContext context = new de.odysseus.el.util.SimpleContext();
|
||||
ValueExpression e = factory.createValueExpression(context, expression, Object.class);
|
||||
e.getValue(context);
|
||||
});
|
||||
}
|
||||
|
||||
// BAD (untrusted input to ValueExpression.setValue when it was created by JUEL)
|
||||
private static void testWithJuelValueExpressionSetValue() throws IOException {
|
||||
testWithSocket(expression -> {
|
||||
ExpressionFactory factory = new de.odysseus.el.ExpressionFactoryImpl();
|
||||
ELContext context = new de.odysseus.el.util.SimpleContext();
|
||||
ValueExpression e = factory.createValueExpression(context, expression, Object.class);
|
||||
e.setValue(context, new Object());
|
||||
});
|
||||
}
|
||||
|
||||
// BAD (untrusted input to MethodExpression.invoke when it was created by JUEL)
|
||||
private static void testWithJuelMethodExpressionInvoke() throws IOException {
|
||||
testWithSocket(expression -> {
|
||||
ExpressionFactory factory = new de.odysseus.el.ExpressionFactoryImpl();
|
||||
ELContext context = new de.odysseus.el.util.SimpleContext();
|
||||
MethodExpression e = factory.createMethodExpression(context, expression, Object.class, new Class[0]);
|
||||
e.invoke(context, new Object[0]);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-094/JakartaExpressionInjection.ql
|
||||
@@ -8,7 +8,8 @@ edges
|
||||
| Jexl2Injection.java:54:73:54:87 | jexlExpr : String | Jexl2Injection.java:57:9:57:35 | parse(...) |
|
||||
| Jexl2Injection.java:60:72:60:86 | jexlExpr : String | Jexl2Injection.java:63:9:63:35 | parse(...) |
|
||||
| Jexl2Injection.java:66:73:66:87 | jexlExpr : String | Jexl2Injection.java:69:9:69:44 | createTemplate(...) |
|
||||
| Jexl2Injection.java:76:25:76:47 | getInputStream(...) : InputStream | Jexl2Injection.java:78:31:78:38 | jexlExpr : String |
|
||||
| Jexl2Injection.java:76:25:76:47 | getInputStream(...) : InputStream | Jexl2Injection.java:76:54:76:58 | bytes [post update] : byte[] |
|
||||
| Jexl2Injection.java:76:54:76:58 | bytes [post update] : byte[] | Jexl2Injection.java:78:31:78:38 | jexlExpr : String |
|
||||
| Jexl2Injection.java:78:31:78:38 | jexlExpr : String | Jexl2Injection.java:86:24:86:56 | jexlExpr : String |
|
||||
| Jexl2Injection.java:78:31:78:38 | jexlExpr : String | Jexl2Injection.java:90:24:90:68 | jexlExpr : String |
|
||||
| Jexl2Injection.java:78:31:78:38 | jexlExpr : String | Jexl2Injection.java:94:24:94:52 | jexlExpr : String |
|
||||
@@ -46,7 +47,8 @@ edges
|
||||
| Jexl3Injection.java:66:73:66:87 | jexlExpr : String | Jexl3Injection.java:69:9:69:39 | createExpression(...) |
|
||||
| Jexl3Injection.java:72:72:72:86 | jexlExpr : String | Jexl3Injection.java:75:9:75:37 | createTemplate(...) |
|
||||
| Jexl3Injection.java:78:54:78:68 | jexlExpr : String | Jexl3Injection.java:84:13:84:13 | e |
|
||||
| Jexl3Injection.java:94:25:94:47 | getInputStream(...) : InputStream | Jexl3Injection.java:96:31:96:38 | jexlExpr : String |
|
||||
| Jexl3Injection.java:94:25:94:47 | getInputStream(...) : InputStream | Jexl3Injection.java:94:54:94:58 | bytes [post update] : byte[] |
|
||||
| Jexl3Injection.java:94:54:94:58 | bytes [post update] : byte[] | Jexl3Injection.java:96:31:96:38 | jexlExpr : String |
|
||||
| Jexl3Injection.java:96:31:96:38 | jexlExpr : String | Jexl3Injection.java:104:24:104:56 | jexlExpr : String |
|
||||
| Jexl3Injection.java:96:31:96:38 | jexlExpr : String | Jexl3Injection.java:108:24:108:68 | jexlExpr : String |
|
||||
| Jexl3Injection.java:96:31:96:38 | jexlExpr : String | Jexl3Injection.java:112:24:112:52 | jexlExpr : String |
|
||||
@@ -103,6 +105,7 @@ nodes
|
||||
| Jexl2Injection.java:66:73:66:87 | jexlExpr : String | semmle.label | jexlExpr : String |
|
||||
| Jexl2Injection.java:69:9:69:44 | createTemplate(...) | semmle.label | createTemplate(...) |
|
||||
| Jexl2Injection.java:76:25:76:47 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| Jexl2Injection.java:76:54:76:58 | bytes [post update] : byte[] | semmle.label | bytes [post update] : byte[] |
|
||||
| Jexl2Injection.java:78:31:78:38 | jexlExpr : String | semmle.label | jexlExpr : String |
|
||||
| Jexl2Injection.java:86:24:86:56 | jexlExpr : String | semmle.label | jexlExpr : String |
|
||||
| Jexl2Injection.java:86:24:86:56 | jexlExpr : String | semmle.label | jexlExpr : String |
|
||||
@@ -143,6 +146,7 @@ nodes
|
||||
| Jexl3Injection.java:78:54:78:68 | jexlExpr : String | semmle.label | jexlExpr : String |
|
||||
| Jexl3Injection.java:84:13:84:13 | e | semmle.label | e |
|
||||
| Jexl3Injection.java:94:25:94:47 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| Jexl3Injection.java:94:54:94:58 | bytes [post update] : byte[] | semmle.label | bytes [post update] : byte[] |
|
||||
| Jexl3Injection.java:96:31:96:38 | jexlExpr : String | semmle.label | jexlExpr : String |
|
||||
| Jexl3Injection.java:104:24:104:56 | jexlExpr : String | semmle.label | jexlExpr : String |
|
||||
| Jexl3Injection.java:104:24:104:56 | jexlExpr : String | semmle.label | jexlExpr : String |
|
||||
|
||||
@@ -10,7 +10,9 @@ edges
|
||||
| MvelInjection.java:77:40:77:51 | read(...) : String | MvelInjection.java:77:7:77:52 | compileTemplate(...) |
|
||||
| MvelInjection.java:81:54:81:65 | read(...) : String | MvelInjection.java:82:29:82:46 | compile(...) |
|
||||
| MvelInjection.java:86:58:86:69 | read(...) : String | MvelInjection.java:88:32:88:41 | expression |
|
||||
| MvelInjection.java:92:27:92:49 | getInputStream(...) : InputStream | MvelInjection.java:95:14:95:36 | new String(...) : String |
|
||||
| MvelInjection.java:92:27:92:49 | getInputStream(...) : InputStream | MvelInjection.java:94:15:94:16 | is : InputStream |
|
||||
| MvelInjection.java:94:15:94:16 | is : InputStream | MvelInjection.java:94:23:94:27 | bytes [post update] : byte[] |
|
||||
| MvelInjection.java:94:23:94:27 | bytes [post update] : byte[] | MvelInjection.java:95:14:95:36 | new String(...) : String |
|
||||
| MvelInjection.java:95:14:95:36 | new String(...) : String | MvelInjection.java:25:15:25:26 | read(...) |
|
||||
| MvelInjection.java:95:14:95:36 | new String(...) : String | MvelInjection.java:29:54:29:65 | read(...) : String |
|
||||
| MvelInjection.java:95:14:95:36 | new String(...) : String | MvelInjection.java:34:58:34:69 | read(...) : String |
|
||||
@@ -46,6 +48,8 @@ nodes
|
||||
| MvelInjection.java:86:58:86:69 | read(...) : String | semmle.label | read(...) : String |
|
||||
| MvelInjection.java:88:32:88:41 | expression | semmle.label | expression |
|
||||
| MvelInjection.java:92:27:92:49 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| MvelInjection.java:94:15:94:16 | is : InputStream | semmle.label | is : InputStream |
|
||||
| MvelInjection.java:94:23:94:27 | bytes [post update] : byte[] | semmle.label | bytes [post update] : byte[] |
|
||||
| MvelInjection.java:95:14:95:36 | new String(...) : String | semmle.label | new String(...) : String |
|
||||
#select
|
||||
| MvelInjection.java:25:15:25:26 | read(...) | MvelInjection.java:92:27:92:49 | getInputStream(...) : InputStream | MvelInjection.java:25:15:25:26 | read(...) | MVEL injection from $@. | MvelInjection.java:92:27:92:49 | getInputStream(...) | this user input |
|
||||
|
||||
@@ -1,22 +1,46 @@
|
||||
edges
|
||||
| SpelInjection.java:15:22:15:44 | getInputStream(...) : InputStream | SpelInjection.java:23:5:23:14 | expression |
|
||||
| SpelInjection.java:27:22:27:44 | getInputStream(...) : InputStream | SpelInjection.java:34:5:34:14 | expression |
|
||||
| SpelInjection.java:38:22:38:44 | getInputStream(...) : InputStream | SpelInjection.java:48:5:48:14 | expression |
|
||||
| SpelInjection.java:52:22:52:44 | getInputStream(...) : InputStream | SpelInjection.java:59:5:59:14 | expression |
|
||||
| SpelInjection.java:63:22:63:44 | getInputStream(...) : InputStream | SpelInjection.java:70:5:70:14 | expression |
|
||||
| SpelInjection.java:74:22:74:44 | getInputStream(...) : InputStream | SpelInjection.java:83:5:83:14 | expression |
|
||||
| SpelInjection.java:15:22:15:44 | getInputStream(...) : InputStream | SpelInjection.java:18:13:18:14 | in : InputStream |
|
||||
| SpelInjection.java:18:13:18:14 | in : InputStream | SpelInjection.java:18:21:18:25 | bytes [post update] : byte[] |
|
||||
| SpelInjection.java:18:21:18:25 | bytes [post update] : byte[] | SpelInjection.java:23:5:23:14 | expression |
|
||||
| SpelInjection.java:27:22:27:44 | getInputStream(...) : InputStream | SpelInjection.java:30:13:30:14 | in : InputStream |
|
||||
| SpelInjection.java:30:13:30:14 | in : InputStream | SpelInjection.java:30:21:30:25 | bytes [post update] : byte[] |
|
||||
| SpelInjection.java:30:21:30:25 | bytes [post update] : byte[] | SpelInjection.java:34:5:34:14 | expression |
|
||||
| SpelInjection.java:38:22:38:44 | getInputStream(...) : InputStream | SpelInjection.java:41:13:41:14 | in : InputStream |
|
||||
| SpelInjection.java:41:13:41:14 | in : InputStream | SpelInjection.java:41:21:41:25 | bytes [post update] : byte[] |
|
||||
| SpelInjection.java:41:21:41:25 | bytes [post update] : byte[] | SpelInjection.java:48:5:48:14 | expression |
|
||||
| SpelInjection.java:52:22:52:44 | getInputStream(...) : InputStream | SpelInjection.java:55:13:55:14 | in : InputStream |
|
||||
| SpelInjection.java:55:13:55:14 | in : InputStream | SpelInjection.java:55:21:55:25 | bytes [post update] : byte[] |
|
||||
| SpelInjection.java:55:21:55:25 | bytes [post update] : byte[] | SpelInjection.java:59:5:59:14 | expression |
|
||||
| SpelInjection.java:63:22:63:44 | getInputStream(...) : InputStream | SpelInjection.java:66:13:66:14 | in : InputStream |
|
||||
| SpelInjection.java:66:13:66:14 | in : InputStream | SpelInjection.java:66:21:66:25 | bytes [post update] : byte[] |
|
||||
| SpelInjection.java:66:21:66:25 | bytes [post update] : byte[] | SpelInjection.java:70:5:70:14 | expression |
|
||||
| SpelInjection.java:74:22:74:44 | getInputStream(...) : InputStream | SpelInjection.java:77:13:77:14 | in : InputStream |
|
||||
| SpelInjection.java:77:13:77:14 | in : InputStream | SpelInjection.java:77:21:77:25 | bytes [post update] : byte[] |
|
||||
| SpelInjection.java:77:21:77:25 | bytes [post update] : byte[] | SpelInjection.java:83:5:83:14 | expression |
|
||||
nodes
|
||||
| SpelInjection.java:15:22:15:44 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| SpelInjection.java:18:13:18:14 | in : InputStream | semmle.label | in : InputStream |
|
||||
| SpelInjection.java:18:21:18:25 | bytes [post update] : byte[] | semmle.label | bytes [post update] : byte[] |
|
||||
| SpelInjection.java:23:5:23:14 | expression | semmle.label | expression |
|
||||
| SpelInjection.java:27:22:27:44 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| SpelInjection.java:30:13:30:14 | in : InputStream | semmle.label | in : InputStream |
|
||||
| SpelInjection.java:30:21:30:25 | bytes [post update] : byte[] | semmle.label | bytes [post update] : byte[] |
|
||||
| SpelInjection.java:34:5:34:14 | expression | semmle.label | expression |
|
||||
| SpelInjection.java:38:22:38:44 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| SpelInjection.java:41:13:41:14 | in : InputStream | semmle.label | in : InputStream |
|
||||
| SpelInjection.java:41:21:41:25 | bytes [post update] : byte[] | semmle.label | bytes [post update] : byte[] |
|
||||
| SpelInjection.java:48:5:48:14 | expression | semmle.label | expression |
|
||||
| SpelInjection.java:52:22:52:44 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| SpelInjection.java:55:13:55:14 | in : InputStream | semmle.label | in : InputStream |
|
||||
| SpelInjection.java:55:21:55:25 | bytes [post update] : byte[] | semmle.label | bytes [post update] : byte[] |
|
||||
| SpelInjection.java:59:5:59:14 | expression | semmle.label | expression |
|
||||
| SpelInjection.java:63:22:63:44 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| SpelInjection.java:66:13:66:14 | in : InputStream | semmle.label | in : InputStream |
|
||||
| SpelInjection.java:66:21:66:25 | bytes [post update] : byte[] | semmle.label | bytes [post update] : byte[] |
|
||||
| SpelInjection.java:70:5:70:14 | expression | semmle.label | expression |
|
||||
| SpelInjection.java:74:22:74:44 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
|
||||
| SpelInjection.java:77:13:77:14 | in : InputStream | semmle.label | in : InputStream |
|
||||
| SpelInjection.java:77:21:77:25 | bytes [post update] : byte[] | semmle.label | bytes [post update] : byte[] |
|
||||
| SpelInjection.java:83:5:83:14 | expression | semmle.label | expression |
|
||||
#select
|
||||
| SpelInjection.java:23:5:23:14 | expression | SpelInjection.java:15:22:15:44 | getInputStream(...) : InputStream | SpelInjection.java:23:5:23:14 | expression | SpEL injection from $@. | SpelInjection.java:15:22:15:44 | getInputStream(...) | this user input |
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../../stubs/mvel2-2.4.7:${testdir}/../../../../stubs/jsr223-api:${testdir}/../../../../stubs/apache-commons-jexl-2.1.1:${testdir}/../../../../stubs/apache-commons-jexl-3.1:${testdir}/../../../../stubs/scriptengine
|
||||
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3:${testdir}/../../../../stubs/mvel2-2.4.7:${testdir}/../../../../stubs/jsr223-api:${testdir}/../../../../stubs/apache-commons-jexl-2.1.1:${testdir}/../../../../stubs/apache-commons-jexl-3.1:${testdir}/../../../../stubs/scriptengine:${testdir}/../../../../stubs/java-ee-el:${testdir}/../../../../stubs/juel-2.2:${testdir}/../../../stubs/groovy-all-3.0.7:${testdir}/../../../../stubs/servlet-api-2.4
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
edges
|
||||
| SensitiveCookieNotHttpOnly.java:24:33:24:43 | "jwt_token" : String | SensitiveCookieNotHttpOnly.java:25:39:25:52 | tokenCookieStr : String |
|
||||
| SensitiveCookieNotHttpOnly.java:24:33:24:43 | "jwt_token" : String | SensitiveCookieNotHttpOnly.java:31:28:31:36 | jwtCookie |
|
||||
| SensitiveCookieNotHttpOnly.java:25:28:25:64 | new Cookie(...) : Cookie | SensitiveCookieNotHttpOnly.java:31:28:31:36 | jwtCookie |
|
||||
| SensitiveCookieNotHttpOnly.java:25:39:25:52 | tokenCookieStr : String | SensitiveCookieNotHttpOnly.java:25:28:25:64 | new Cookie(...) : Cookie |
|
||||
| SensitiveCookieNotHttpOnly.java:42:42:42:49 | "token=" : String | SensitiveCookieNotHttpOnly.java:42:42:42:69 | ... + ... |
|
||||
| SensitiveCookieNotHttpOnly.java:42:42:42:57 | ... + ... : String | SensitiveCookieNotHttpOnly.java:42:42:42:69 | ... + ... |
|
||||
| SensitiveCookieNotHttpOnly.java:52:56:52:75 | "session-access-key" : String | SensitiveCookieNotHttpOnly.java:52:42:52:124 | toString(...) |
|
||||
@@ -7,11 +10,16 @@ edges
|
||||
| SensitiveCookieNotHttpOnly.java:70:28:70:35 | "token=" : String | SensitiveCookieNotHttpOnly.java:71:42:71:50 | secString |
|
||||
| SensitiveCookieNotHttpOnly.java:70:28:70:43 | ... + ... : String | SensitiveCookieNotHttpOnly.java:71:42:71:50 | secString |
|
||||
| SensitiveCookieNotHttpOnly.java:70:28:70:55 | ... + ... : String | SensitiveCookieNotHttpOnly.java:71:42:71:50 | secString |
|
||||
| SensitiveCookieNotHttpOnly.java:88:35:88:51 | "Presto-UI-Token" : String | SensitiveCookieNotHttpOnly.java:89:36:89:51 | PRESTO_UI_COOKIE : String |
|
||||
| SensitiveCookieNotHttpOnly.java:88:35:88:51 | "Presto-UI-Token" : String | SensitiveCookieNotHttpOnly.java:91:16:91:21 | cookie : Cookie |
|
||||
| SensitiveCookieNotHttpOnly.java:89:25:89:57 | new Cookie(...) : Cookie | SensitiveCookieNotHttpOnly.java:91:16:91:21 | cookie : Cookie |
|
||||
| SensitiveCookieNotHttpOnly.java:89:36:89:51 | PRESTO_UI_COOKIE : String | SensitiveCookieNotHttpOnly.java:89:25:89:57 | new Cookie(...) : Cookie |
|
||||
| SensitiveCookieNotHttpOnly.java:91:16:91:21 | cookie : Cookie | SensitiveCookieNotHttpOnly.java:110:25:110:64 | createAuthenticationCookie(...) : Cookie |
|
||||
| SensitiveCookieNotHttpOnly.java:110:25:110:64 | createAuthenticationCookie(...) : Cookie | SensitiveCookieNotHttpOnly.java:111:28:111:33 | cookie |
|
||||
nodes
|
||||
| SensitiveCookieNotHttpOnly.java:24:33:24:43 | "jwt_token" : String | semmle.label | "jwt_token" : String |
|
||||
| SensitiveCookieNotHttpOnly.java:25:28:25:64 | new Cookie(...) : Cookie | semmle.label | new Cookie(...) : Cookie |
|
||||
| SensitiveCookieNotHttpOnly.java:25:39:25:52 | tokenCookieStr : String | semmle.label | tokenCookieStr : String |
|
||||
| SensitiveCookieNotHttpOnly.java:31:28:31:36 | jwtCookie | semmle.label | jwtCookie |
|
||||
| SensitiveCookieNotHttpOnly.java:42:42:42:49 | "token=" : String | semmle.label | "token=" : String |
|
||||
| SensitiveCookieNotHttpOnly.java:42:42:42:57 | ... + ... : String | semmle.label | ... + ... : String |
|
||||
@@ -25,6 +33,8 @@ nodes
|
||||
| SensitiveCookieNotHttpOnly.java:70:28:70:55 | ... + ... : String | semmle.label | ... + ... : String |
|
||||
| SensitiveCookieNotHttpOnly.java:71:42:71:50 | secString | semmle.label | secString |
|
||||
| SensitiveCookieNotHttpOnly.java:88:35:88:51 | "Presto-UI-Token" : String | semmle.label | "Presto-UI-Token" : String |
|
||||
| SensitiveCookieNotHttpOnly.java:89:25:89:57 | new Cookie(...) : Cookie | semmle.label | new Cookie(...) : Cookie |
|
||||
| SensitiveCookieNotHttpOnly.java:89:36:89:51 | PRESTO_UI_COOKIE : String | semmle.label | PRESTO_UI_COOKIE : String |
|
||||
| SensitiveCookieNotHttpOnly.java:91:16:91:21 | cookie : Cookie | semmle.label | cookie : Cookie |
|
||||
| SensitiveCookieNotHttpOnly.java:110:25:110:64 | createAuthenticationCookie(...) : Cookie | semmle.label | createAuthenticationCookie(...) : Cookie |
|
||||
| SensitiveCookieNotHttpOnly.java:111:28:111:33 | cookie | semmle.label | cookie |
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.gson.Gson;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.HashMap;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@Controller
|
||||
public class JsonpController {
|
||||
|
||||
private static HashMap hashMap = new HashMap();
|
||||
|
||||
static {
|
||||
hashMap.put("username","admin");
|
||||
hashMap.put("password","123456");
|
||||
}
|
||||
|
||||
@GetMapping(value = "jsonp1")
|
||||
@ResponseBody
|
||||
public String bad1(HttpServletRequest request) {
|
||||
String resultStr = null;
|
||||
String jsonpCallback = request.getParameter("jsonpCallback");
|
||||
Gson gson = new Gson();
|
||||
String result = gson.toJson(hashMap);
|
||||
resultStr = jsonpCallback + "(" + result + ")";
|
||||
return resultStr;
|
||||
}
|
||||
|
||||
@GetMapping(value = "jsonp2")
|
||||
@ResponseBody
|
||||
public String bad2(HttpServletRequest request) {
|
||||
String resultStr = null;
|
||||
String jsonpCallback = request.getParameter("jsonpCallback");
|
||||
resultStr = jsonpCallback + "(" + JSONObject.toJSONString(hashMap) + ")";
|
||||
return resultStr;
|
||||
}
|
||||
|
||||
@GetMapping(value = "jsonp3")
|
||||
@ResponseBody
|
||||
public String bad3(HttpServletRequest request) {
|
||||
String resultStr = null;
|
||||
String jsonpCallback = request.getParameter("jsonpCallback");
|
||||
String jsonStr = getJsonStr(hashMap);
|
||||
resultStr = jsonpCallback + "(" + jsonStr + ")";
|
||||
return resultStr;
|
||||
}
|
||||
|
||||
@GetMapping(value = "jsonp4")
|
||||
@ResponseBody
|
||||
public String bad4(HttpServletRequest request) {
|
||||
String resultStr = null;
|
||||
String jsonpCallback = request.getParameter("jsonpCallback");
|
||||
String restr = JSONObject.toJSONString(hashMap);
|
||||
resultStr = jsonpCallback + "(" + restr + ");";
|
||||
return resultStr;
|
||||
}
|
||||
|
||||
@GetMapping(value = "jsonp5")
|
||||
@ResponseBody
|
||||
public void bad5(HttpServletRequest request,
|
||||
HttpServletResponse response) throws Exception {
|
||||
String jsonpCallback = request.getParameter("jsonpCallback");
|
||||
PrintWriter pw = null;
|
||||
Gson gson = new Gson();
|
||||
String result = gson.toJson(hashMap);
|
||||
String resultStr = null;
|
||||
pw = response.getWriter();
|
||||
resultStr = jsonpCallback + "(" + result + ")";
|
||||
pw.println(resultStr);
|
||||
}
|
||||
|
||||
@GetMapping(value = "jsonp6")
|
||||
@ResponseBody
|
||||
public void bad6(HttpServletRequest request,
|
||||
HttpServletResponse response) throws Exception {
|
||||
String jsonpCallback = request.getParameter("jsonpCallback");
|
||||
PrintWriter pw = null;
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
String result = mapper.writeValueAsString(hashMap);
|
||||
String resultStr = null;
|
||||
pw = response.getWriter();
|
||||
resultStr = jsonpCallback + "(" + result + ")";
|
||||
pw.println(resultStr);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "jsonp7", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public String bad7(HttpServletRequest request) {
|
||||
String resultStr = null;
|
||||
String jsonpCallback = request.getParameter("jsonpCallback");
|
||||
Gson gson = new Gson();
|
||||
String result = gson.toJson(hashMap);
|
||||
resultStr = jsonpCallback + "(" + result + ")";
|
||||
return resultStr;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "jsonp11")
|
||||
@ResponseBody
|
||||
public String good1(HttpServletRequest request) {
|
||||
JSONObject parameterObj = readToJSONObect(request);
|
||||
String resultStr = null;
|
||||
String jsonpCallback = request.getParameter("jsonpCallback");
|
||||
String restr = JSONObject.toJSONString(hashMap);
|
||||
resultStr = jsonpCallback + "(" + restr + ");";
|
||||
return resultStr;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "jsonp12")
|
||||
@ResponseBody
|
||||
public String good2(@RequestParam("file") MultipartFile file,HttpServletRequest request) {
|
||||
if(null == file){
|
||||
return "upload file error";
|
||||
}
|
||||
String fileName = file.getOriginalFilename();
|
||||
System.out.println("file operations");
|
||||
String resultStr = null;
|
||||
String jsonpCallback = request.getParameter("jsonpCallback");
|
||||
String restr = JSONObject.toJSONString(hashMap);
|
||||
resultStr = jsonpCallback + "(" + restr + ");";
|
||||
return resultStr;
|
||||
}
|
||||
|
||||
public static JSONObject readToJSONObect(HttpServletRequest request){
|
||||
String jsonText = readPostContent(request);
|
||||
JSONObject jsonObj = JSONObject.parseObject(jsonText, JSONObject.class);
|
||||
return jsonObj;
|
||||
}
|
||||
|
||||
public static String readPostContent(HttpServletRequest request){
|
||||
BufferedReader in= null;
|
||||
String content = null;
|
||||
String line = null;
|
||||
try {
|
||||
in = new BufferedReader(new InputStreamReader(request.getInputStream(),"UTF-8"));
|
||||
StringBuilder buf = new StringBuilder();
|
||||
while ((line = in.readLine()) != null) {
|
||||
buf.append(line);
|
||||
}
|
||||
content = buf.toString();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
String uri = request.getRequestURI();
|
||||
return content;
|
||||
}
|
||||
|
||||
public static String getJsonStr(Object result) {
|
||||
return JSONObject.toJSONString(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
edges
|
||||
| JsonpController.java:33:32:33:68 | getParameter(...) : String | JsonpController.java:37:16:37:24 | resultStr |
|
||||
| JsonpController.java:36:21:36:54 | ... + ... : String | JsonpController.java:37:16:37:24 | resultStr |
|
||||
| JsonpController.java:44:32:44:68 | getParameter(...) : String | JsonpController.java:46:16:46:24 | resultStr |
|
||||
| JsonpController.java:45:21:45:80 | ... + ... : String | JsonpController.java:46:16:46:24 | resultStr |
|
||||
| JsonpController.java:53:32:53:68 | getParameter(...) : String | JsonpController.java:56:16:56:24 | resultStr |
|
||||
| JsonpController.java:55:21:55:55 | ... + ... : String | JsonpController.java:56:16:56:24 | resultStr |
|
||||
| JsonpController.java:63:32:63:68 | getParameter(...) : String | JsonpController.java:66:16:66:24 | resultStr |
|
||||
| JsonpController.java:65:21:65:54 | ... + ... : String | JsonpController.java:66:16:66:24 | resultStr |
|
||||
| JsonpController.java:73:32:73:68 | getParameter(...) : String | JsonpController.java:80:20:80:28 | resultStr |
|
||||
| JsonpController.java:79:21:79:54 | ... + ... : String | JsonpController.java:80:20:80:28 | resultStr |
|
||||
| JsonpController.java:87:32:87:68 | getParameter(...) : String | JsonpController.java:94:20:94:28 | resultStr |
|
||||
| JsonpController.java:93:21:93:54 | ... + ... : String | JsonpController.java:94:20:94:28 | resultStr |
|
||||
| JsonpController.java:101:32:101:68 | getParameter(...) : String | JsonpController.java:105:16:105:24 | resultStr |
|
||||
| JsonpController.java:104:21:104:54 | ... + ... : String | JsonpController.java:105:16:105:24 | resultStr |
|
||||
| JsonpController.java:115:21:115:54 | ... + ... : String | JsonpController.java:116:16:116:24 | resultStr |
|
||||
| JsonpController.java:130:21:130:54 | ... + ... : String | JsonpController.java:131:16:131:24 | resultStr |
|
||||
nodes
|
||||
| JsonpController.java:33:32:33:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| JsonpController.java:36:21:36:54 | ... + ... : String | semmle.label | ... + ... : String |
|
||||
| JsonpController.java:37:16:37:24 | resultStr | semmle.label | resultStr |
|
||||
| JsonpController.java:37:16:37:24 | resultStr | semmle.label | resultStr |
|
||||
| JsonpController.java:44:32:44:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| JsonpController.java:45:21:45:80 | ... + ... : String | semmle.label | ... + ... : String |
|
||||
| JsonpController.java:46:16:46:24 | resultStr | semmle.label | resultStr |
|
||||
| JsonpController.java:46:16:46:24 | resultStr | semmle.label | resultStr |
|
||||
| JsonpController.java:53:32:53:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| JsonpController.java:55:21:55:55 | ... + ... : String | semmle.label | ... + ... : String |
|
||||
| JsonpController.java:56:16:56:24 | resultStr | semmle.label | resultStr |
|
||||
| JsonpController.java:56:16:56:24 | resultStr | semmle.label | resultStr |
|
||||
| JsonpController.java:63:32:63:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| JsonpController.java:65:21:65:54 | ... + ... : String | semmle.label | ... + ... : String |
|
||||
| JsonpController.java:66:16:66:24 | resultStr | semmle.label | resultStr |
|
||||
| JsonpController.java:66:16:66:24 | resultStr | semmle.label | resultStr |
|
||||
| JsonpController.java:73:32:73:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| JsonpController.java:79:21:79:54 | ... + ... : String | semmle.label | ... + ... : String |
|
||||
| JsonpController.java:80:20:80:28 | resultStr | semmle.label | resultStr |
|
||||
| JsonpController.java:80:20:80:28 | resultStr | semmle.label | resultStr |
|
||||
| JsonpController.java:87:32:87:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| JsonpController.java:93:21:93:54 | ... + ... : String | semmle.label | ... + ... : String |
|
||||
| JsonpController.java:94:20:94:28 | resultStr | semmle.label | resultStr |
|
||||
| JsonpController.java:94:20:94:28 | resultStr | semmle.label | resultStr |
|
||||
| JsonpController.java:101:32:101:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| JsonpController.java:104:21:104:54 | ... + ... : String | semmle.label | ... + ... : String |
|
||||
| JsonpController.java:105:16:105:24 | resultStr | semmle.label | resultStr |
|
||||
| JsonpController.java:105:16:105:24 | resultStr | semmle.label | resultStr |
|
||||
| JsonpController.java:115:21:115:54 | ... + ... : String | semmle.label | ... + ... : String |
|
||||
| JsonpController.java:116:16:116:24 | resultStr | semmle.label | resultStr |
|
||||
| JsonpController.java:130:21:130:54 | ... + ... : String | semmle.label | ... + ... : String |
|
||||
| JsonpController.java:131:16:131:24 | resultStr | semmle.label | resultStr |
|
||||
#select
|
||||
| JsonpController.java:37:16:37:24 | resultStr | JsonpController.java:33:32:33:68 | getParameter(...) : String | JsonpController.java:37:16:37:24 | resultStr | Jsonp response might include code from $@. | JsonpController.java:33:32:33:68 | getParameter(...) | this user input |
|
||||
| JsonpController.java:46:16:46:24 | resultStr | JsonpController.java:44:32:44:68 | getParameter(...) : String | JsonpController.java:46:16:46:24 | resultStr | Jsonp response might include code from $@. | JsonpController.java:44:32:44:68 | getParameter(...) | this user input |
|
||||
| JsonpController.java:56:16:56:24 | resultStr | JsonpController.java:53:32:53:68 | getParameter(...) : String | JsonpController.java:56:16:56:24 | resultStr | Jsonp response might include code from $@. | JsonpController.java:53:32:53:68 | getParameter(...) | this user input |
|
||||
| JsonpController.java:66:16:66:24 | resultStr | JsonpController.java:63:32:63:68 | getParameter(...) : String | JsonpController.java:66:16:66:24 | resultStr | Jsonp response might include code from $@. | JsonpController.java:63:32:63:68 | getParameter(...) | this user input |
|
||||
| JsonpController.java:80:20:80:28 | resultStr | JsonpController.java:73:32:73:68 | getParameter(...) : String | JsonpController.java:80:20:80:28 | resultStr | Jsonp response might include code from $@. | JsonpController.java:73:32:73:68 | getParameter(...) | this user input |
|
||||
| JsonpController.java:94:20:94:28 | resultStr | JsonpController.java:87:32:87:68 | getParameter(...) : String | JsonpController.java:94:20:94:28 | resultStr | Jsonp response might include code from $@. | JsonpController.java:87:32:87:68 | getParameter(...) | this user input |
|
||||
| JsonpController.java:105:16:105:24 | resultStr | JsonpController.java:101:32:101:68 | getParameter(...) : String | JsonpController.java:105:16:105:24 | resultStr | Jsonp response might include code from $@. | JsonpController.java:101:32:101:68 | getParameter(...) | this user input |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-352/JsonpInjection.ql
|
||||
@@ -0,0 +1 @@
|
||||
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/apache-http-4.4.13/:${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/fastjson-1.2.74/:${testdir}/../../../../stubs/gson-2.8.6/:${testdir}/../../../../stubs/jackson-databind-2.10/:${testdir}/../../../../stubs/springframework-5.2.3/
|
||||
@@ -1,8 +1,14 @@
|
||||
edges
|
||||
| InsecureBasicAuth.java:20:39:20:52 | ... + ... : String | InsecureBasicAuth.java:28:3:28:6 | post |
|
||||
| InsecureBasicAuth.java:35:19:35:64 | "http://www.example.com:8000/payment/retrieve" : String | InsecureBasicAuth.java:38:3:38:5 | get |
|
||||
| InsecureBasicAuth.java:45:19:45:68 | "http://www.example.com/rest/getuser.do?uid=abcdx" : String | InsecureBasicAuth.java:46:50:46:55 | uriStr : String |
|
||||
| InsecureBasicAuth.java:45:19:45:68 | "http://www.example.com/rest/getuser.do?uid=abcdx" : String | InsecureBasicAuth.java:54:3:54:6 | post |
|
||||
| InsecureBasicAuth.java:46:39:46:56 | create(...) : URI | InsecureBasicAuth.java:54:3:54:6 | post |
|
||||
| InsecureBasicAuth.java:46:50:46:55 | uriStr : String | InsecureBasicAuth.java:46:39:46:56 | create(...) : URI |
|
||||
| InsecureBasicAuth.java:61:19:61:68 | "http://www.example.com/rest/getuser.do?uid=abcdx" : String | InsecureBasicAuth.java:62:21:62:26 | uriStr : String |
|
||||
| InsecureBasicAuth.java:61:19:61:68 | "http://www.example.com/rest/getuser.do?uid=abcdx" : String | InsecureBasicAuth.java:71:3:71:6 | post |
|
||||
| InsecureBasicAuth.java:62:13:62:27 | new URI(...) : URI | InsecureBasicAuth.java:71:3:71:6 | post |
|
||||
| InsecureBasicAuth.java:62:21:62:26 | uriStr : String | InsecureBasicAuth.java:62:13:62:27 | new URI(...) : URI |
|
||||
| InsecureBasicAuth.java:78:47:78:52 | "http" : String | InsecureBasicAuth.java:86:3:86:6 | post |
|
||||
| InsecureBasicAuth.java:93:19:93:68 | "http://www.example.com/rest/getuser.do?uid=abcdx" : String | InsecureBasicAuth.java:102:3:102:6 | post |
|
||||
| InsecureBasicAuth.java:109:19:109:68 | "http://www.example.com/rest/getuser.do?uid=abcdx" : String | InsecureBasicAuth.java:119:3:119:6 | post |
|
||||
@@ -16,8 +22,12 @@ nodes
|
||||
| InsecureBasicAuth.java:35:19:35:64 | "http://www.example.com:8000/payment/retrieve" : String | semmle.label | "http://www.example.com:8000/payment/retrieve" : String |
|
||||
| InsecureBasicAuth.java:38:3:38:5 | get | semmle.label | get |
|
||||
| InsecureBasicAuth.java:45:19:45:68 | "http://www.example.com/rest/getuser.do?uid=abcdx" : String | semmle.label | "http://www.example.com/rest/getuser.do?uid=abcdx" : String |
|
||||
| InsecureBasicAuth.java:46:39:46:56 | create(...) : URI | semmle.label | create(...) : URI |
|
||||
| InsecureBasicAuth.java:46:50:46:55 | uriStr : String | semmle.label | uriStr : String |
|
||||
| InsecureBasicAuth.java:54:3:54:6 | post | semmle.label | post |
|
||||
| InsecureBasicAuth.java:61:19:61:68 | "http://www.example.com/rest/getuser.do?uid=abcdx" : String | semmle.label | "http://www.example.com/rest/getuser.do?uid=abcdx" : String |
|
||||
| InsecureBasicAuth.java:62:13:62:27 | new URI(...) : URI | semmle.label | new URI(...) : URI |
|
||||
| InsecureBasicAuth.java:62:21:62:26 | uriStr : String | semmle.label | uriStr : String |
|
||||
| InsecureBasicAuth.java:71:3:71:6 | post | semmle.label | post |
|
||||
| InsecureBasicAuth.java:78:47:78:52 | "http" : String | semmle.label | "http" : String |
|
||||
| InsecureBasicAuth.java:86:3:86:6 | post | semmle.label | post |
|
||||
|
||||
@@ -5,10 +5,19 @@ edges
|
||||
| XQueryInjection.java:86:33:86:60 | nameStr : String | XQueryInjection.java:92:53:92:57 | query |
|
||||
| XQueryInjection.java:100:28:100:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:104:35:104:38 | xqpe |
|
||||
| XQueryInjection.java:112:28:112:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:116:53:116:56 | name |
|
||||
| XQueryInjection.java:124:28:124:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:129:35:129:38 | xqpe |
|
||||
| XQueryInjection.java:137:28:137:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:142:53:142:54 | br |
|
||||
| XQueryInjection.java:124:28:124:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:125:70:125:73 | name : ServletInputStream |
|
||||
| XQueryInjection.java:125:29:125:75 | new BufferedReader(...) : BufferedReader | XQueryInjection.java:129:35:129:38 | xqpe |
|
||||
| XQueryInjection.java:125:48:125:74 | new InputStreamReader(...) : InputStreamReader | XQueryInjection.java:125:29:125:75 | new BufferedReader(...) : BufferedReader |
|
||||
| XQueryInjection.java:125:70:125:73 | name : ServletInputStream | XQueryInjection.java:125:48:125:74 | new InputStreamReader(...) : InputStreamReader |
|
||||
| XQueryInjection.java:137:28:137:51 | getInputStream(...) : ServletInputStream | XQueryInjection.java:138:70:138:73 | name : ServletInputStream |
|
||||
| XQueryInjection.java:138:29:138:75 | new BufferedReader(...) : BufferedReader | XQueryInjection.java:142:53:142:54 | br |
|
||||
| XQueryInjection.java:138:48:138:74 | new InputStreamReader(...) : InputStreamReader | XQueryInjection.java:138:29:138:75 | new BufferedReader(...) : BufferedReader |
|
||||
| XQueryInjection.java:138:70:138:73 | name : ServletInputStream | XQueryInjection.java:138:48:138:74 | new InputStreamReader(...) : InputStreamReader |
|
||||
| XQueryInjection.java:150:23:150:50 | getParameter(...) : String | XQueryInjection.java:155:29:155:32 | name |
|
||||
| XQueryInjection.java:157:26:157:49 | getInputStream(...) : ServletInputStream | XQueryInjection.java:159:29:159:30 | br |
|
||||
| XQueryInjection.java:157:26:157:49 | getInputStream(...) : ServletInputStream | XQueryInjection.java:158:70:158:71 | is : ServletInputStream |
|
||||
| XQueryInjection.java:158:29:158:73 | new BufferedReader(...) : BufferedReader | XQueryInjection.java:159:29:159:30 | br |
|
||||
| XQueryInjection.java:158:48:158:72 | new InputStreamReader(...) : InputStreamReader | XQueryInjection.java:158:29:158:73 | new BufferedReader(...) : BufferedReader |
|
||||
| XQueryInjection.java:158:70:158:71 | is : ServletInputStream | XQueryInjection.java:158:48:158:72 | new InputStreamReader(...) : InputStreamReader |
|
||||
nodes
|
||||
| XQueryInjection.java:45:23:45:50 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| XQueryInjection.java:51:35:51:38 | xqpe | semmle.label | xqpe |
|
||||
@@ -23,12 +32,21 @@ nodes
|
||||
| XQueryInjection.java:112:28:112:51 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
|
||||
| XQueryInjection.java:116:53:116:56 | name | semmle.label | name |
|
||||
| XQueryInjection.java:124:28:124:51 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
|
||||
| XQueryInjection.java:125:29:125:75 | new BufferedReader(...) : BufferedReader | semmle.label | new BufferedReader(...) : BufferedReader |
|
||||
| XQueryInjection.java:125:48:125:74 | new InputStreamReader(...) : InputStreamReader | semmle.label | new InputStreamReader(...) : InputStreamReader |
|
||||
| XQueryInjection.java:125:70:125:73 | name : ServletInputStream | semmle.label | name : ServletInputStream |
|
||||
| XQueryInjection.java:129:35:129:38 | xqpe | semmle.label | xqpe |
|
||||
| XQueryInjection.java:137:28:137:51 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
|
||||
| XQueryInjection.java:138:29:138:75 | new BufferedReader(...) : BufferedReader | semmle.label | new BufferedReader(...) : BufferedReader |
|
||||
| XQueryInjection.java:138:48:138:74 | new InputStreamReader(...) : InputStreamReader | semmle.label | new InputStreamReader(...) : InputStreamReader |
|
||||
| XQueryInjection.java:138:70:138:73 | name : ServletInputStream | semmle.label | name : ServletInputStream |
|
||||
| XQueryInjection.java:142:53:142:54 | br | semmle.label | br |
|
||||
| XQueryInjection.java:150:23:150:50 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| XQueryInjection.java:155:29:155:32 | name | semmle.label | name |
|
||||
| XQueryInjection.java:157:26:157:49 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
|
||||
| XQueryInjection.java:158:29:158:73 | new BufferedReader(...) : BufferedReader | semmle.label | new BufferedReader(...) : BufferedReader |
|
||||
| XQueryInjection.java:158:48:158:72 | new InputStreamReader(...) : InputStreamReader | semmle.label | new InputStreamReader(...) : InputStreamReader |
|
||||
| XQueryInjection.java:158:70:158:71 | is : ServletInputStream | semmle.label | is : ServletInputStream |
|
||||
| XQueryInjection.java:159:29:159:30 | br | semmle.label | br |
|
||||
#select
|
||||
| XQueryInjection.java:51:35:51:38 | xqpe | XQueryInjection.java:45:23:45:50 | getParameter(...) : String | XQueryInjection.java:51:35:51:38 | xqpe | XQuery query might include code from $@. | XQueryInjection.java:45:23:45:50 | getParameter(...) | this user input |
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
edges
|
||||
| JaxWsSSRF.java:21:22:21:48 | getParameter(...) : String | JaxWsSSRF.java:22:23:22:25 | url |
|
||||
| RequestForgery2.java:23:27:23:53 | getParameter(...) : String | RequestForgery2.java:25:31:25:34 | sink : String |
|
||||
| RequestForgery2.java:23:27:23:53 | getParameter(...) : String | RequestForgery2.java:55:32:55:35 | url1 |
|
||||
| RequestForgery2.java:23:27:23:53 | getParameter(...) : String | RequestForgery2.java:58:32:58:35 | url1 |
|
||||
| RequestForgery2.java:23:27:23:53 | getParameter(...) : String | RequestForgery2.java:59:30:59:33 | url1 |
|
||||
@@ -7,6 +8,12 @@ edges
|
||||
| RequestForgery2.java:23:27:23:53 | getParameter(...) : String | RequestForgery2.java:64:59:64:61 | uri |
|
||||
| RequestForgery2.java:23:27:23:53 | getParameter(...) : String | RequestForgery2.java:67:43:67:45 | uri |
|
||||
| RequestForgery2.java:23:27:23:53 | getParameter(...) : String | RequestForgery2.java:69:29:69:32 | uri2 |
|
||||
| RequestForgery2.java:25:23:25:35 | new URI(...) : URI | RequestForgery2.java:64:59:64:61 | uri |
|
||||
| RequestForgery2.java:25:23:25:35 | new URI(...) : URI | RequestForgery2.java:67:43:67:45 | uri |
|
||||
| RequestForgery2.java:25:31:25:34 | sink : String | RequestForgery2.java:25:23:25:35 | new URI(...) : URI |
|
||||
| RequestForgery.java:19:23:19:58 | new URI(...) : URI | RequestForgery.java:22:52:22:54 | uri |
|
||||
| RequestForgery.java:19:23:19:58 | new URI(...) : URI | RequestForgery.java:27:57:27:59 | uri |
|
||||
| RequestForgery.java:19:31:19:57 | getParameter(...) : String | RequestForgery.java:19:23:19:58 | new URI(...) : URI |
|
||||
| RequestForgery.java:19:31:19:57 | getParameter(...) : String | RequestForgery.java:22:52:22:54 | uri |
|
||||
| RequestForgery.java:19:31:19:57 | getParameter(...) : String | RequestForgery.java:27:57:27:59 | uri |
|
||||
| SpringSSRF.java:26:33:26:60 | getParameter(...) : String | SpringSSRF.java:32:47:32:67 | ... + ... |
|
||||
@@ -15,13 +22,17 @@ edges
|
||||
| SpringSSRF.java:26:33:26:60 | getParameter(...) : String | SpringSSRF.java:45:47:45:60 | fooResourceUrl |
|
||||
| SpringSSRF.java:26:33:26:60 | getParameter(...) : String | SpringSSRF.java:54:59:54:72 | fooResourceUrl |
|
||||
| SpringSSRF.java:26:33:26:60 | getParameter(...) : String | SpringSSRF.java:58:74:58:96 | new URI(...) |
|
||||
| SpringSSRF.java:26:33:26:60 | getParameter(...) : String | SpringSSRF.java:58:82:58:95 | fooResourceUrl : String |
|
||||
| SpringSSRF.java:26:33:26:60 | getParameter(...) : String | SpringSSRF.java:62:57:62:70 | fooResourceUrl |
|
||||
| SpringSSRF.java:26:33:26:60 | getParameter(...) : String | SpringSSRF.java:66:48:66:61 | fooResourceUrl |
|
||||
| SpringSSRF.java:26:33:26:60 | getParameter(...) : String | SpringSSRF.java:69:30:69:43 | fooResourceUrl |
|
||||
| SpringSSRF.java:58:82:58:95 | fooResourceUrl : String | SpringSSRF.java:58:74:58:96 | new URI(...) |
|
||||
nodes
|
||||
| JaxWsSSRF.java:21:22:21:48 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| JaxWsSSRF.java:22:23:22:25 | url | semmle.label | url |
|
||||
| RequestForgery2.java:23:27:23:53 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| RequestForgery2.java:25:23:25:35 | new URI(...) : URI | semmle.label | new URI(...) : URI |
|
||||
| RequestForgery2.java:25:31:25:34 | sink : String | semmle.label | sink : String |
|
||||
| RequestForgery2.java:55:32:55:35 | url1 | semmle.label | url1 |
|
||||
| RequestForgery2.java:58:32:58:35 | url1 | semmle.label | url1 |
|
||||
| RequestForgery2.java:59:30:59:33 | url1 | semmle.label | url1 |
|
||||
@@ -29,6 +40,7 @@ nodes
|
||||
| RequestForgery2.java:64:59:64:61 | uri | semmle.label | uri |
|
||||
| RequestForgery2.java:67:43:67:45 | uri | semmle.label | uri |
|
||||
| RequestForgery2.java:69:29:69:32 | uri2 | semmle.label | uri2 |
|
||||
| RequestForgery.java:19:23:19:58 | new URI(...) : URI | semmle.label | new URI(...) : URI |
|
||||
| RequestForgery.java:19:31:19:57 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| RequestForgery.java:22:52:22:54 | uri | semmle.label | uri |
|
||||
| RequestForgery.java:27:57:27:59 | uri | semmle.label | uri |
|
||||
@@ -39,6 +51,7 @@ nodes
|
||||
| SpringSSRF.java:45:47:45:60 | fooResourceUrl | semmle.label | fooResourceUrl |
|
||||
| SpringSSRF.java:54:59:54:72 | fooResourceUrl | semmle.label | fooResourceUrl |
|
||||
| SpringSSRF.java:58:74:58:96 | new URI(...) | semmle.label | new URI(...) |
|
||||
| SpringSSRF.java:58:82:58:95 | fooResourceUrl : String | semmle.label | fooResourceUrl : String |
|
||||
| SpringSSRF.java:62:57:62:70 | fooResourceUrl | semmle.label | fooResourceUrl |
|
||||
| SpringSSRF.java:66:48:66:61 | fooResourceUrl | semmle.label | fooResourceUrl |
|
||||
| SpringSSRF.java:69:30:69:43 | fooResourceUrl | semmle.label | fooResourceUrl |
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package groovy.lang;
|
||||
|
||||
public class GroovyClassLoader {
|
||||
public GroovyClassLoader() {
|
||||
}
|
||||
|
||||
public Class parseClass(String text) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Class parseClass(GroovyCodeSource gcs) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user