mirror of
https://github.com/github/codeql.git
synced 2026-07-19 18:28:13 +02:00
Merge branch 'main' into weak_crypto
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
lgtm
|
||||
* The "Tainted allocation size" query (cpp/uncontrolled-allocation-size) has been improved to produce fewer false positives.
|
||||
2
cpp/change-notes/2021-05-18-static-buffer-overflow.md
Normal file
2
cpp/change-notes/2021-05-18-static-buffer-overflow.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm
|
||||
* The "Static buffer overflow" query (cpp/static-buffer-overflow) has been improved to produce fewer false positives.
|
||||
2
cpp/change-notes/2021-12-05-uncontrolled-arithmetic.md
Normal file
2
cpp/change-notes/2021-12-05-uncontrolled-arithmetic.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* The query "Uncontrolled arithmetic" (`cpp/uncontrolled-arithmetic`) has been improved to produce fewer false positives.
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.commons.Buffer
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
import LoopBounds
|
||||
|
||||
private predicate staticBufferBase(VariableAccess access, Variable v) {
|
||||
@@ -51,6 +52,8 @@ predicate overflowOffsetInLoop(BufferAccess bufaccess, string msg) {
|
||||
loop.getStmt().getAChild*() = bufaccess.getEnclosingStmt() and
|
||||
loop.limit() >= bufaccess.bufferSize() and
|
||||
loop.counter().getAnAccess() = bufaccess.getArrayOffset() and
|
||||
// Ensure that we don't have an upper bound on the array index that's less than the buffer size.
|
||||
not upperBound(bufaccess.getArrayOffset().getFullyConverted()) < bufaccess.bufferSize() and
|
||||
msg =
|
||||
"Potential buffer-overflow: counter '" + loop.counter().toString() + "' <= " +
|
||||
loop.limit().toString() + " but '" + bufaccess.buffer().getName() + "' has " +
|
||||
@@ -94,17 +97,22 @@ class CallWithBufferSize extends FunctionCall {
|
||||
}
|
||||
|
||||
int statedSizeValue() {
|
||||
exists(Expr statedSizeSrc |
|
||||
DataFlow::localExprFlow(statedSizeSrc, statedSizeExpr()) and
|
||||
result = statedSizeSrc.getValue().toInt()
|
||||
)
|
||||
// `upperBound(e)` defaults to `exprMaxVal(e)` when `e` isn't analyzable. So to get a meaningful
|
||||
// result in this case we pick the minimum value obtainable from dataflow and range analysis.
|
||||
result =
|
||||
upperBound(statedSizeExpr())
|
||||
.minimum(min(Expr statedSizeSrc |
|
||||
DataFlow::localExprFlow(statedSizeSrc, statedSizeExpr())
|
||||
|
|
||||
statedSizeSrc.getValue().toInt()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
predicate wrongBufferSize(Expr error, string msg) {
|
||||
exists(CallWithBufferSize call, int bufsize, Variable buf, int statedSize |
|
||||
staticBuffer(call.buffer(), buf, bufsize) and
|
||||
statedSize = min(call.statedSizeValue()) and
|
||||
statedSize = call.statedSizeValue() and
|
||||
statedSize > bufsize and
|
||||
error = call.statedSizeExpr() and
|
||||
msg =
|
||||
|
||||
@@ -15,34 +15,99 @@ import cpp
|
||||
import semmle.code.cpp.security.Overflow
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
import TaintedWithPath
|
||||
|
||||
predicate isRandCall(FunctionCall fc) { fc.getTarget().getName() = "rand" }
|
||||
|
||||
predicate isRandCallOrParent(Expr e) {
|
||||
isRandCall(e) or
|
||||
isRandCallOrParent(e.getAChild())
|
||||
predicate isUnboundedRandCall(FunctionCall fc) {
|
||||
fc.getTarget().getName() = "rand" and not bounded(fc)
|
||||
}
|
||||
|
||||
predicate isRandValue(Expr e) {
|
||||
isRandCall(e)
|
||||
/**
|
||||
* An operand `e` of a division expression (i.e., `e` is an operand of either a `DivExpr` or
|
||||
* a `AssignDivExpr`) is bounded when `e` is the left-hand side of the division.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate boundedDiv(Expr e, Expr left) { e = left }
|
||||
|
||||
/**
|
||||
* An operand `e` of a remainder expression `rem` (i.e., `rem` is either a `RemExpr` or
|
||||
* an `AssignRemExpr`) with left-hand side `left` and right-ahnd side `right` is bounded
|
||||
* when `e` is `left` and `right` is upper bounded by some number that is less than the maximum integer
|
||||
* allowed by the result type of `rem`.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate boundedRem(Expr e, Expr rem, Expr left, Expr right) {
|
||||
e = left and
|
||||
upperBound(right.getFullyConverted()) < exprMaxVal(rem.getFullyConverted())
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand `e` of a bitwise and expression `andExpr` (i.e., `andExpr` is either an `BitwiseAndExpr`
|
||||
* or an `AssignAndExpr`) with operands `operand1` and `operand2` is the operand that is not `e` is upper
|
||||
* bounded by some number that is less than the maximum integer allowed by the result type of `andExpr`.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate boundedBitwiseAnd(Expr e, Expr andExpr, Expr operand1, Expr operand2) {
|
||||
operand1 != operand2 and
|
||||
e = operand1 and
|
||||
upperBound(operand2.getFullyConverted()) < exprMaxVal(andExpr.getFullyConverted())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `fc` is a part of the left operand of a binary operation that greatly reduces the range
|
||||
* of possible values.
|
||||
*/
|
||||
predicate bounded(Expr e) {
|
||||
// For `%` and `&` we require that `e` is bounded by a value that is strictly smaller than the
|
||||
// maximum possible value of the result type of the operation.
|
||||
// For example, the function call `rand()` is considered bounded in the following program:
|
||||
// ```
|
||||
// int i = rand() % (UINT8_MAX + 1);
|
||||
// ```
|
||||
// but not in:
|
||||
// ```
|
||||
// unsigned char uc = rand() % (UINT8_MAX + 1);
|
||||
// ```
|
||||
exists(RemExpr rem | boundedRem(e, rem, rem.getLeftOperand(), rem.getRightOperand()))
|
||||
or
|
||||
exists(AssignRemExpr rem | boundedRem(e, rem, rem.getLValue(), rem.getRValue()))
|
||||
or
|
||||
exists(BitwiseAndExpr andExpr |
|
||||
boundedBitwiseAnd(e, andExpr, andExpr.getAnOperand(), andExpr.getAnOperand())
|
||||
)
|
||||
or
|
||||
exists(AssignAndExpr andExpr |
|
||||
boundedBitwiseAnd(e, andExpr, andExpr.getAnOperand(), andExpr.getAnOperand())
|
||||
)
|
||||
or
|
||||
// Optimitically assume that a division always yields a much smaller value.
|
||||
boundedDiv(e, any(DivExpr div).getLeftOperand())
|
||||
or
|
||||
boundedDiv(e, any(AssignDivExpr div).getLValue())
|
||||
}
|
||||
|
||||
predicate isUnboundedRandCallOrParent(Expr e) {
|
||||
isUnboundedRandCall(e)
|
||||
or
|
||||
isUnboundedRandCallOrParent(e.getAChild())
|
||||
}
|
||||
|
||||
predicate isUnboundedRandValue(Expr e) {
|
||||
isUnboundedRandCall(e)
|
||||
or
|
||||
exists(MacroInvocation mi |
|
||||
e = mi.getExpr() and
|
||||
isRandCallOrParent(e)
|
||||
isUnboundedRandCallOrParent(e)
|
||||
)
|
||||
}
|
||||
|
||||
class SecurityOptionsArith extends SecurityOptions {
|
||||
override predicate isUserInput(Expr expr, string cause) {
|
||||
isRandValue(expr) and
|
||||
cause = "rand" and
|
||||
not expr.getParent*() instanceof DivExpr
|
||||
isUnboundedRandValue(expr) and
|
||||
cause = "rand"
|
||||
}
|
||||
}
|
||||
|
||||
predicate isDiv(VariableAccess va) { exists(AssignDivExpr div | div.getLValue() = va) }
|
||||
|
||||
predicate missingGuard(VariableAccess va, string effect) {
|
||||
exists(Operation op | op.getAnOperand() = va |
|
||||
missingGuardAgainstUnderflow(op, va) and effect = "underflow"
|
||||
@@ -52,29 +117,15 @@ predicate missingGuard(VariableAccess va, string effect) {
|
||||
}
|
||||
|
||||
class Configuration extends TaintTrackingConfiguration {
|
||||
override predicate isSink(Element e) {
|
||||
isDiv(e)
|
||||
or
|
||||
missingGuard(e, _)
|
||||
}
|
||||
}
|
||||
override predicate isSink(Element e) { missingGuard(e, _) }
|
||||
|
||||
/**
|
||||
* A value that undergoes division is likely to be bounded within a safe
|
||||
* range.
|
||||
*/
|
||||
predicate guardedByAssignDiv(Expr origin) {
|
||||
exists(VariableAccess va |
|
||||
taintedWithPath(origin, va, _, _) and
|
||||
isDiv(va)
|
||||
)
|
||||
override predicate isBarrier(Expr e) { super.isBarrier(e) or bounded(e) }
|
||||
}
|
||||
|
||||
from Expr origin, VariableAccess va, string effect, PathNode sourceNode, PathNode sinkNode
|
||||
where
|
||||
taintedWithPath(origin, va, sourceNode, sinkNode) and
|
||||
missingGuard(va, effect) and
|
||||
not guardedByAssignDiv(origin)
|
||||
missingGuard(va, effect)
|
||||
select va, sourceNode, sinkNode,
|
||||
"$@ flows to here and is used in arithmetic, potentially causing an " + effect + ".", origin,
|
||||
"Uncontrolled value"
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import TaintedWithPath
|
||||
|
||||
@@ -27,6 +28,27 @@ predicate allocSink(Expr alloc, Expr tainted) {
|
||||
|
||||
class TaintedAllocationSizeConfiguration extends TaintTrackingConfiguration {
|
||||
override predicate isSink(Element tainted) { allocSink(_, tainted) }
|
||||
|
||||
override predicate isBarrier(Expr e) {
|
||||
super.isBarrier(e)
|
||||
or
|
||||
// There can be two separate reasons for `convertedExprMightOverflow` not holding:
|
||||
// 1. `e` really cannot overflow.
|
||||
// 2. `e` isn't analyzable.
|
||||
// If we didn't rule out case 2 we would place barriers on anything that isn't analyzable.
|
||||
(
|
||||
e instanceof UnaryArithmeticOperation or
|
||||
e instanceof BinaryArithmeticOperation or
|
||||
e instanceof AssignArithmeticOperation
|
||||
) and
|
||||
not convertedExprMightOverflow(e)
|
||||
or
|
||||
// Subtracting two pointers is either well-defined (and the result will likely be small), or
|
||||
// terribly undefined and dangerous. Here, we assume that the programmer has ensured that the
|
||||
// result is well-defined (i.e., the two pointers point to the same object), and thus the result
|
||||
// will likely be small.
|
||||
e = any(PointerDiffExpr diff).getAnOperand()
|
||||
}
|
||||
}
|
||||
|
||||
predicate taintedAllocSize(
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* @description The total number of lines of C/C++ code across all files, including system headers, libraries, and auto-generated 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
|
||||
* lines-of-code
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -748,16 +748,10 @@ private predicate modelFlow(Operand opFrom, Instruction iTo) {
|
||||
)
|
||||
or
|
||||
exists(int index, ReadSideEffectInstruction read |
|
||||
modelIn.isParameterDeref(index) and
|
||||
modelIn.isParameterDerefOrQualifierObject(index) and
|
||||
read = getSideEffectFor(call, index) and
|
||||
opFrom = read.getSideEffectOperand()
|
||||
)
|
||||
or
|
||||
exists(ReadSideEffectInstruction read |
|
||||
modelIn.isQualifierObject() and
|
||||
read = getSideEffectFor(call, -1) and
|
||||
opFrom = read.getSideEffectOperand()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -586,6 +586,23 @@ void test21(bool cond)
|
||||
if (ptr[-1] == 0) { return; } // GOOD: accesses buffer[1]
|
||||
}
|
||||
|
||||
void test22(bool b, const char* source) {
|
||||
char buffer[16];
|
||||
int k;
|
||||
for (k = 0; k <= 100; k++) {
|
||||
if(k < 16) {
|
||||
buffer[k] = 'x'; // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
char dest[128];
|
||||
int n = b ? 1024 : 132;
|
||||
if (n >= 128) {
|
||||
return;
|
||||
}
|
||||
memcpy(dest, source, n); // GOOD
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
long long arr17[19];
|
||||
@@ -609,6 +626,7 @@ int main(int argc, char *argv[])
|
||||
test19(argc == 0);
|
||||
test20();
|
||||
test21(argc == 0);
|
||||
test22(argc == 0, argv[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,228 +1,174 @@
|
||||
edges
|
||||
| test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | (size_t)... |
|
||||
| test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | (size_t)... |
|
||||
| test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted |
|
||||
| test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted |
|
||||
| test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted |
|
||||
| test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted |
|
||||
| test.cpp:39:21:39:24 | argv | test.cpp:43:38:43:63 | ... * ... |
|
||||
| test.cpp:39:21:39:24 | argv | test.cpp:43:38:43:63 | ... * ... |
|
||||
| test.cpp:39:21:39:24 | argv | test.cpp:43:38:43:63 | ... * ... |
|
||||
| test.cpp:39:21:39:24 | argv | test.cpp:43:38:43:63 | ... * ... |
|
||||
| test.cpp:39:21:39:24 | argv | test.cpp:45:38:45:63 | ... + ... |
|
||||
| test.cpp:39:21:39:24 | argv | test.cpp:45:38:45:63 | ... + ... |
|
||||
| test.cpp:39:21:39:24 | argv | test.cpp:45:38:45:63 | ... + ... |
|
||||
| test.cpp:39:21:39:24 | argv | test.cpp:45:38:45:63 | ... + ... |
|
||||
| test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | (size_t)... |
|
||||
| test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | (size_t)... |
|
||||
| test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | size |
|
||||
| test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | size |
|
||||
| test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | size |
|
||||
| test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | size |
|
||||
| test.cpp:39:21:39:24 | argv | test.cpp:49:26:49:29 | size |
|
||||
| test.cpp:39:21:39:24 | argv | test.cpp:49:26:49:29 | size |
|
||||
| test.cpp:39:21:39:24 | argv | test.cpp:49:26:49:29 | size |
|
||||
| test.cpp:39:21:39:24 | argv | test.cpp:49:26:49:29 | size |
|
||||
| test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... |
|
||||
| test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... |
|
||||
| test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... |
|
||||
| test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... |
|
||||
| test.cpp:75:25:75:29 | start | test.cpp:79:18:79:28 | ... - ... |
|
||||
| test.cpp:75:25:75:29 | start | test.cpp:79:18:79:28 | ... - ... |
|
||||
| test.cpp:75:38:75:40 | end | test.cpp:79:18:79:28 | ... - ... |
|
||||
| test.cpp:75:38:75:40 | end | test.cpp:79:18:79:28 | ... - ... |
|
||||
| test.cpp:97:18:97:23 | buffer | test.cpp:100:4:100:15 | buffer |
|
||||
| test.cpp:97:18:97:23 | buffer | test.cpp:100:17:100:22 | buffer indirection |
|
||||
| test.cpp:97:18:97:23 | buffer | test.cpp:101:4:101:15 | ... + ... |
|
||||
| test.cpp:97:18:97:23 | buffer | test.cpp:101:4:101:15 | buffer |
|
||||
| test.cpp:97:18:97:23 | fread output argument | test.cpp:100:4:100:15 | buffer |
|
||||
| test.cpp:97:18:97:23 | fread output argument | test.cpp:100:17:100:22 | buffer indirection |
|
||||
| test.cpp:97:18:97:23 | fread output argument | test.cpp:101:4:101:15 | ... + ... |
|
||||
| test.cpp:97:18:97:23 | fread output argument | test.cpp:101:4:101:15 | buffer |
|
||||
| test.cpp:100:4:100:15 | buffer | test.cpp:100:17:100:22 | processData1 output argument |
|
||||
| test.cpp:100:17:100:22 | buffer indirection | test.cpp:100:17:100:22 | processData1 output argument |
|
||||
| test.cpp:100:17:100:22 | processData1 output argument | test.cpp:101:4:101:15 | ... + ... |
|
||||
| test.cpp:100:17:100:22 | processData1 output argument | test.cpp:101:4:101:15 | buffer |
|
||||
| test.cpp:101:4:101:15 | ... + ... | test.cpp:75:38:75:40 | end |
|
||||
| test.cpp:101:4:101:15 | buffer | test.cpp:75:25:75:29 | start |
|
||||
| test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:41 | ... * ... |
|
||||
| test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:41 | ... * ... |
|
||||
| test.cpp:123:18:123:31 | (const char *)... | test.cpp:127:24:127:41 | ... * ... |
|
||||
| test.cpp:123:18:123:31 | (const char *)... | test.cpp:127:24:127:41 | ... * ... |
|
||||
| test.cpp:132:19:132:24 | call to getenv | test.cpp:134:10:134:27 | ... * ... |
|
||||
| test.cpp:132:19:132:24 | call to getenv | test.cpp:134:10:134:27 | ... * ... |
|
||||
| test.cpp:132:19:132:32 | (const char *)... | test.cpp:134:10:134:27 | ... * ... |
|
||||
| test.cpp:132:19:132:32 | (const char *)... | test.cpp:134:10:134:27 | ... * ... |
|
||||
| test.cpp:138:19:138:24 | call to getenv | test.cpp:142:11:142:28 | ... * ... |
|
||||
| test.cpp:138:19:138:24 | call to getenv | test.cpp:142:11:142:28 | ... * ... |
|
||||
| test.cpp:138:19:138:32 | (const char *)... | test.cpp:142:11:142:28 | ... * ... |
|
||||
| test.cpp:138:19:138:32 | (const char *)... | test.cpp:142:11:142:28 | ... * ... |
|
||||
| test.cpp:201:9:201:42 | Store | test.cpp:231:9:231:24 | call to get_tainted_size |
|
||||
| test.cpp:201:9:201:42 | Store | test.cpp:231:9:231:24 | call to get_tainted_size |
|
||||
| test.cpp:201:14:201:19 | call to getenv | test.cpp:201:9:201:42 | Store |
|
||||
| test.cpp:201:14:201:27 | (const char *)... | test.cpp:201:9:201:42 | Store |
|
||||
| test.cpp:214:23:214:23 | s | test.cpp:215:21:215:21 | s |
|
||||
| test.cpp:214:23:214:23 | s | test.cpp:215:21:215:21 | s |
|
||||
| test.cpp:220:21:220:21 | s | test.cpp:221:21:221:21 | s |
|
||||
| test.cpp:220:21:220:21 | s | test.cpp:221:21:221:21 | s |
|
||||
| test.cpp:227:24:227:29 | call to getenv | test.cpp:229:9:229:18 | (size_t)... |
|
||||
| test.cpp:227:24:227:29 | call to getenv | test.cpp:229:9:229:18 | local_size |
|
||||
| test.cpp:227:24:227:29 | call to getenv | test.cpp:229:9:229:18 | local_size |
|
||||
| test.cpp:227:24:227:29 | call to getenv | test.cpp:235:2:235:9 | local_size |
|
||||
| test.cpp:227:24:227:29 | call to getenv | test.cpp:237:2:237:8 | local_size |
|
||||
| test.cpp:227:24:227:37 | (const char *)... | test.cpp:229:9:229:18 | (size_t)... |
|
||||
| test.cpp:227:24:227:37 | (const char *)... | test.cpp:229:9:229:18 | local_size |
|
||||
| test.cpp:227:24:227:37 | (const char *)... | test.cpp:229:9:229:18 | local_size |
|
||||
| test.cpp:227:24:227:37 | (const char *)... | test.cpp:235:2:235:9 | local_size |
|
||||
| test.cpp:227:24:227:37 | (const char *)... | test.cpp:237:2:237:8 | local_size |
|
||||
| test.cpp:235:2:235:9 | local_size | test.cpp:214:23:214:23 | s |
|
||||
| test.cpp:237:2:237:8 | local_size | test.cpp:220:21:220:21 | s |
|
||||
| test.cpp:241:2:241:32 | Chi [array content] | test.cpp:279:17:279:20 | get_size output argument [array content] |
|
||||
| test.cpp:241:2:241:32 | Chi [array content] | test.cpp:295:18:295:21 | get_size output argument [array content] |
|
||||
| test.cpp:241:18:241:23 | call to getenv | test.cpp:241:2:241:32 | Chi [array content] |
|
||||
| test.cpp:241:18:241:31 | (const char *)... | test.cpp:241:2:241:32 | Chi [array content] |
|
||||
| test.cpp:249:20:249:25 | call to getenv | test.cpp:253:11:253:29 | ... * ... |
|
||||
| test.cpp:249:20:249:25 | call to getenv | test.cpp:253:11:253:29 | ... * ... |
|
||||
| test.cpp:249:20:249:33 | (const char *)... | test.cpp:253:11:253:29 | ... * ... |
|
||||
| test.cpp:249:20:249:33 | (const char *)... | test.cpp:253:11:253:29 | ... * ... |
|
||||
| test.cpp:279:17:279:20 | Chi | test.cpp:281:11:281:28 | ... * ... |
|
||||
| test.cpp:279:17:279:20 | Chi | test.cpp:281:11:281:28 | ... * ... |
|
||||
| test.cpp:279:17:279:20 | get_size output argument [array content] | test.cpp:279:17:279:20 | Chi |
|
||||
| test.cpp:295:18:295:21 | Chi | test.cpp:298:10:298:27 | ... * ... |
|
||||
| test.cpp:295:18:295:21 | Chi | test.cpp:298:10:298:27 | ... * ... |
|
||||
| test.cpp:295:18:295:21 | get_size output argument [array content] | test.cpp:295:18:295:21 | Chi |
|
||||
| test.cpp:301:19:301:24 | call to getenv | test.cpp:305:11:305:28 | ... * ... |
|
||||
| test.cpp:301:19:301:24 | call to getenv | test.cpp:305:11:305:28 | ... * ... |
|
||||
| test.cpp:301:19:301:32 | (const char *)... | test.cpp:305:11:305:28 | ... * ... |
|
||||
| test.cpp:301:19:301:32 | (const char *)... | test.cpp:305:11:305:28 | ... * ... |
|
||||
| test.cpp:309:19:309:24 | call to getenv | test.cpp:314:10:314:27 | ... * ... |
|
||||
| test.cpp:309:19:309:24 | call to getenv | test.cpp:314:10:314:27 | ... * ... |
|
||||
| test.cpp:309:19:309:32 | (const char *)... | test.cpp:314:10:314:27 | ... * ... |
|
||||
| test.cpp:309:19:309:32 | (const char *)... | test.cpp:314:10:314:27 | ... * ... |
|
||||
| test.cpp:40:21:40:24 | argv | test.cpp:43:38:43:44 | (size_t)... |
|
||||
| test.cpp:40:21:40:24 | argv | test.cpp:43:38:43:44 | (size_t)... |
|
||||
| test.cpp:40:21:40:24 | argv | test.cpp:43:38:43:44 | tainted |
|
||||
| test.cpp:40:21:40:24 | argv | test.cpp:43:38:43:44 | tainted |
|
||||
| test.cpp:40:21:40:24 | argv | test.cpp:43:38:43:44 | tainted |
|
||||
| test.cpp:40:21:40:24 | argv | test.cpp:43:38:43:44 | tainted |
|
||||
| test.cpp:40:21:40:24 | argv | test.cpp:44:38:44:63 | ... * ... |
|
||||
| test.cpp:40:21:40:24 | argv | test.cpp:44:38:44:63 | ... * ... |
|
||||
| test.cpp:40:21:40:24 | argv | test.cpp:44:38:44:63 | ... * ... |
|
||||
| test.cpp:40:21:40:24 | argv | test.cpp:44:38:44:63 | ... * ... |
|
||||
| test.cpp:40:21:40:24 | argv | test.cpp:46:38:46:63 | ... + ... |
|
||||
| test.cpp:40:21:40:24 | argv | test.cpp:46:38:46:63 | ... + ... |
|
||||
| test.cpp:40:21:40:24 | argv | test.cpp:46:38:46:63 | ... + ... |
|
||||
| test.cpp:40:21:40:24 | argv | test.cpp:46:38:46:63 | ... + ... |
|
||||
| test.cpp:40:21:40:24 | argv | test.cpp:49:32:49:35 | (size_t)... |
|
||||
| test.cpp:40:21:40:24 | argv | test.cpp:49:32:49:35 | (size_t)... |
|
||||
| test.cpp:40:21:40:24 | argv | test.cpp:49:32:49:35 | size |
|
||||
| test.cpp:40:21:40:24 | argv | test.cpp:49:32:49:35 | size |
|
||||
| test.cpp:40:21:40:24 | argv | test.cpp:49:32:49:35 | size |
|
||||
| test.cpp:40:21:40:24 | argv | test.cpp:49:32:49:35 | size |
|
||||
| test.cpp:40:21:40:24 | argv | test.cpp:50:26:50:29 | size |
|
||||
| test.cpp:40:21:40:24 | argv | test.cpp:50:26:50:29 | size |
|
||||
| test.cpp:40:21:40:24 | argv | test.cpp:50:26:50:29 | size |
|
||||
| test.cpp:40:21:40:24 | argv | test.cpp:50:26:50:29 | size |
|
||||
| test.cpp:40:21:40:24 | argv | test.cpp:53:35:53:60 | ... * ... |
|
||||
| test.cpp:40:21:40:24 | argv | test.cpp:53:35:53:60 | ... * ... |
|
||||
| test.cpp:40:21:40:24 | argv | test.cpp:53:35:53:60 | ... * ... |
|
||||
| test.cpp:40:21:40:24 | argv | test.cpp:53:35:53:60 | ... * ... |
|
||||
| test.cpp:124:18:124:23 | call to getenv | test.cpp:128:24:128:41 | ... * ... |
|
||||
| test.cpp:124:18:124:23 | call to getenv | test.cpp:128:24:128:41 | ... * ... |
|
||||
| test.cpp:124:18:124:31 | (const char *)... | test.cpp:128:24:128:41 | ... * ... |
|
||||
| test.cpp:124:18:124:31 | (const char *)... | test.cpp:128:24:128:41 | ... * ... |
|
||||
| test.cpp:133:19:133:24 | call to getenv | test.cpp:135:10:135:27 | ... * ... |
|
||||
| test.cpp:133:19:133:24 | call to getenv | test.cpp:135:10:135:27 | ... * ... |
|
||||
| test.cpp:133:19:133:32 | (const char *)... | test.cpp:135:10:135:27 | ... * ... |
|
||||
| test.cpp:133:19:133:32 | (const char *)... | test.cpp:135:10:135:27 | ... * ... |
|
||||
| test.cpp:148:20:148:25 | call to getenv | test.cpp:152:11:152:28 | ... * ... |
|
||||
| test.cpp:148:20:148:25 | call to getenv | test.cpp:152:11:152:28 | ... * ... |
|
||||
| test.cpp:148:20:148:33 | (const char *)... | test.cpp:152:11:152:28 | ... * ... |
|
||||
| test.cpp:148:20:148:33 | (const char *)... | test.cpp:152:11:152:28 | ... * ... |
|
||||
| test.cpp:211:9:211:42 | Store | test.cpp:241:9:241:24 | call to get_tainted_size |
|
||||
| test.cpp:211:9:211:42 | Store | test.cpp:241:9:241:24 | call to get_tainted_size |
|
||||
| test.cpp:211:14:211:19 | call to getenv | test.cpp:211:9:211:42 | Store |
|
||||
| test.cpp:211:14:211:27 | (const char *)... | test.cpp:211:9:211:42 | Store |
|
||||
| test.cpp:224:23:224:23 | s | test.cpp:225:21:225:21 | s |
|
||||
| test.cpp:224:23:224:23 | s | test.cpp:225:21:225:21 | s |
|
||||
| test.cpp:230:21:230:21 | s | test.cpp:231:21:231:21 | s |
|
||||
| test.cpp:230:21:230:21 | s | test.cpp:231:21:231:21 | s |
|
||||
| test.cpp:237:24:237:29 | call to getenv | test.cpp:239:9:239:18 | (size_t)... |
|
||||
| test.cpp:237:24:237:29 | call to getenv | test.cpp:239:9:239:18 | local_size |
|
||||
| test.cpp:237:24:237:29 | call to getenv | test.cpp:239:9:239:18 | local_size |
|
||||
| test.cpp:237:24:237:29 | call to getenv | test.cpp:245:2:245:9 | local_size |
|
||||
| test.cpp:237:24:237:29 | call to getenv | test.cpp:247:2:247:8 | local_size |
|
||||
| test.cpp:237:24:237:37 | (const char *)... | test.cpp:239:9:239:18 | (size_t)... |
|
||||
| test.cpp:237:24:237:37 | (const char *)... | test.cpp:239:9:239:18 | local_size |
|
||||
| test.cpp:237:24:237:37 | (const char *)... | test.cpp:239:9:239:18 | local_size |
|
||||
| test.cpp:237:24:237:37 | (const char *)... | test.cpp:245:2:245:9 | local_size |
|
||||
| test.cpp:237:24:237:37 | (const char *)... | test.cpp:247:2:247:8 | local_size |
|
||||
| test.cpp:245:2:245:9 | local_size | test.cpp:224:23:224:23 | s |
|
||||
| test.cpp:247:2:247:8 | local_size | test.cpp:230:21:230:21 | s |
|
||||
| test.cpp:251:2:251:32 | Chi [array content] | test.cpp:289:17:289:20 | get_size output argument [array content] |
|
||||
| test.cpp:251:2:251:32 | Chi [array content] | test.cpp:305:18:305:21 | get_size output argument [array content] |
|
||||
| test.cpp:251:18:251:23 | call to getenv | test.cpp:251:2:251:32 | Chi [array content] |
|
||||
| test.cpp:251:18:251:31 | (const char *)... | test.cpp:251:2:251:32 | Chi [array content] |
|
||||
| test.cpp:259:20:259:25 | call to getenv | test.cpp:263:11:263:29 | ... * ... |
|
||||
| test.cpp:259:20:259:25 | call to getenv | test.cpp:263:11:263:29 | ... * ... |
|
||||
| test.cpp:259:20:259:33 | (const char *)... | test.cpp:263:11:263:29 | ... * ... |
|
||||
| test.cpp:259:20:259:33 | (const char *)... | test.cpp:263:11:263:29 | ... * ... |
|
||||
| test.cpp:289:17:289:20 | Chi | test.cpp:291:11:291:28 | ... * ... |
|
||||
| test.cpp:289:17:289:20 | Chi | test.cpp:291:11:291:28 | ... * ... |
|
||||
| test.cpp:289:17:289:20 | get_size output argument [array content] | test.cpp:289:17:289:20 | Chi |
|
||||
| test.cpp:305:18:305:21 | Chi | test.cpp:308:10:308:27 | ... * ... |
|
||||
| test.cpp:305:18:305:21 | Chi | test.cpp:308:10:308:27 | ... * ... |
|
||||
| test.cpp:305:18:305:21 | get_size output argument [array content] | test.cpp:305:18:305:21 | Chi |
|
||||
nodes
|
||||
| test.cpp:39:21:39:24 | argv | semmle.label | argv |
|
||||
| test.cpp:39:21:39:24 | argv | semmle.label | argv |
|
||||
| test.cpp:42:38:42:44 | (size_t)... | semmle.label | (size_t)... |
|
||||
| test.cpp:42:38:42:44 | (size_t)... | semmle.label | (size_t)... |
|
||||
| test.cpp:42:38:42:44 | tainted | semmle.label | tainted |
|
||||
| test.cpp:42:38:42:44 | tainted | semmle.label | tainted |
|
||||
| test.cpp:42:38:42:44 | tainted | semmle.label | tainted |
|
||||
| test.cpp:43:38:43:63 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:43:38:43:63 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:43:38:43:63 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:45:38:45:63 | ... + ... | semmle.label | ... + ... |
|
||||
| test.cpp:45:38:45:63 | ... + ... | semmle.label | ... + ... |
|
||||
| test.cpp:45:38:45:63 | ... + ... | semmle.label | ... + ... |
|
||||
| test.cpp:48:32:48:35 | (size_t)... | semmle.label | (size_t)... |
|
||||
| test.cpp:48:32:48:35 | (size_t)... | semmle.label | (size_t)... |
|
||||
| test.cpp:48:32:48:35 | size | semmle.label | size |
|
||||
| test.cpp:48:32:48:35 | size | semmle.label | size |
|
||||
| test.cpp:48:32:48:35 | size | semmle.label | size |
|
||||
| test.cpp:49:26:49:29 | size | semmle.label | size |
|
||||
| test.cpp:49:26:49:29 | size | semmle.label | size |
|
||||
| test.cpp:49:26:49:29 | size | semmle.label | size |
|
||||
| test.cpp:52:35:52:60 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:52:35:52:60 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:52:35:52:60 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:64:25:64:30 | *buffer | semmle.label | *buffer |
|
||||
| test.cpp:64:25:64:30 | *buffer | semmle.label | *buffer |
|
||||
| test.cpp:64:25:64:30 | buffer | semmle.label | buffer |
|
||||
| test.cpp:75:25:75:29 | start | semmle.label | start |
|
||||
| test.cpp:75:38:75:40 | end | semmle.label | end |
|
||||
| test.cpp:79:18:79:28 | ... - ... | semmle.label | ... - ... |
|
||||
| test.cpp:79:18:79:28 | ... - ... | semmle.label | ... - ... |
|
||||
| test.cpp:79:18:79:28 | ... - ... | semmle.label | ... - ... |
|
||||
| test.cpp:97:18:97:23 | buffer | semmle.label | buffer |
|
||||
| test.cpp:97:18:97:23 | fread output argument | semmle.label | fread output argument |
|
||||
| test.cpp:100:4:100:15 | buffer | semmle.label | buffer |
|
||||
| test.cpp:100:17:100:22 | buffer indirection | semmle.label | buffer indirection |
|
||||
| test.cpp:100:17:100:22 | processData1 output argument | semmle.label | processData1 output argument |
|
||||
| test.cpp:101:4:101:15 | ... + ... | semmle.label | ... + ... |
|
||||
| test.cpp:101:4:101:15 | buffer | semmle.label | buffer |
|
||||
| test.cpp:123:18:123:23 | call to getenv | semmle.label | call to getenv |
|
||||
| test.cpp:123:18:123:31 | (const char *)... | semmle.label | (const char *)... |
|
||||
| test.cpp:127:24:127:41 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:127:24:127:41 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:127:24:127:41 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:132:19:132:24 | call to getenv | semmle.label | call to getenv |
|
||||
| test.cpp:132:19:132:32 | (const char *)... | semmle.label | (const char *)... |
|
||||
| test.cpp:134:10:134:27 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:134:10:134:27 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:134:10:134:27 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:138:19:138:24 | call to getenv | semmle.label | call to getenv |
|
||||
| test.cpp:138:19:138:32 | (const char *)... | semmle.label | (const char *)... |
|
||||
| test.cpp:142:11:142:28 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:142:11:142:28 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:142:11:142:28 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:201:9:201:42 | Store | semmle.label | Store |
|
||||
| test.cpp:201:14:201:19 | call to getenv | semmle.label | call to getenv |
|
||||
| test.cpp:201:14:201:27 | (const char *)... | semmle.label | (const char *)... |
|
||||
| test.cpp:214:23:214:23 | s | semmle.label | s |
|
||||
| test.cpp:215:21:215:21 | s | semmle.label | s |
|
||||
| test.cpp:215:21:215:21 | s | semmle.label | s |
|
||||
| test.cpp:215:21:215:21 | s | semmle.label | s |
|
||||
| test.cpp:220:21:220:21 | s | semmle.label | s |
|
||||
| test.cpp:221:21:221:21 | s | semmle.label | s |
|
||||
| test.cpp:221:21:221:21 | s | semmle.label | s |
|
||||
| test.cpp:221:21:221:21 | s | semmle.label | s |
|
||||
| test.cpp:227:24:227:29 | call to getenv | semmle.label | call to getenv |
|
||||
| test.cpp:227:24:227:37 | (const char *)... | semmle.label | (const char *)... |
|
||||
| test.cpp:229:9:229:18 | (size_t)... | semmle.label | (size_t)... |
|
||||
| test.cpp:229:9:229:18 | (size_t)... | semmle.label | (size_t)... |
|
||||
| test.cpp:229:9:229:18 | local_size | semmle.label | local_size |
|
||||
| test.cpp:229:9:229:18 | local_size | semmle.label | local_size |
|
||||
| test.cpp:229:9:229:18 | local_size | semmle.label | local_size |
|
||||
| test.cpp:231:9:231:24 | call to get_tainted_size | semmle.label | call to get_tainted_size |
|
||||
| test.cpp:231:9:231:24 | call to get_tainted_size | semmle.label | call to get_tainted_size |
|
||||
| test.cpp:231:9:231:24 | call to get_tainted_size | semmle.label | call to get_tainted_size |
|
||||
| test.cpp:235:2:235:9 | local_size | semmle.label | local_size |
|
||||
| test.cpp:237:2:237:8 | local_size | semmle.label | local_size |
|
||||
| test.cpp:241:2:241:32 | Chi [array content] | semmle.label | Chi [array content] |
|
||||
| test.cpp:241:2:241:32 | ChiPartial | semmle.label | ChiPartial |
|
||||
| test.cpp:241:18:241:23 | call to getenv | semmle.label | call to getenv |
|
||||
| test.cpp:241:18:241:31 | (const char *)... | semmle.label | (const char *)... |
|
||||
| test.cpp:249:20:249:25 | call to getenv | semmle.label | call to getenv |
|
||||
| test.cpp:249:20:249:33 | (const char *)... | semmle.label | (const char *)... |
|
||||
| test.cpp:253:11:253:29 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:253:11:253:29 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:253:11:253:29 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:279:17:279:20 | Chi | semmle.label | Chi |
|
||||
| test.cpp:279:17:279:20 | get_size output argument [array content] | semmle.label | get_size output argument [array content] |
|
||||
| test.cpp:281:11:281:28 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:281:11:281:28 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:281:11:281:28 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:295:18:295:21 | Chi | semmle.label | Chi |
|
||||
| test.cpp:295:18:295:21 | get_size output argument [array content] | semmle.label | get_size output argument [array content] |
|
||||
| test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:301:19:301:24 | call to getenv | semmle.label | call to getenv |
|
||||
| test.cpp:301:19:301:32 | (const char *)... | semmle.label | (const char *)... |
|
||||
| test.cpp:305:11:305:28 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:305:11:305:28 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:305:11:305:28 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:309:19:309:24 | call to getenv | semmle.label | call to getenv |
|
||||
| test.cpp:309:19:309:32 | (const char *)... | semmle.label | (const char *)... |
|
||||
| test.cpp:314:10:314:27 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:314:10:314:27 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:314:10:314:27 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:40:21:40:24 | argv | semmle.label | argv |
|
||||
| test.cpp:40:21:40:24 | argv | semmle.label | argv |
|
||||
| test.cpp:43:38:43:44 | (size_t)... | semmle.label | (size_t)... |
|
||||
| test.cpp:43:38:43:44 | (size_t)... | semmle.label | (size_t)... |
|
||||
| test.cpp:43:38:43:44 | tainted | semmle.label | tainted |
|
||||
| test.cpp:43:38:43:44 | tainted | semmle.label | tainted |
|
||||
| test.cpp:43:38:43:44 | tainted | semmle.label | tainted |
|
||||
| test.cpp:44:38:44:63 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:44:38:44:63 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:44:38:44:63 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:46:38:46:63 | ... + ... | semmle.label | ... + ... |
|
||||
| test.cpp:46:38:46:63 | ... + ... | semmle.label | ... + ... |
|
||||
| test.cpp:46:38:46:63 | ... + ... | semmle.label | ... + ... |
|
||||
| test.cpp:49:32:49:35 | (size_t)... | semmle.label | (size_t)... |
|
||||
| test.cpp:49:32:49:35 | (size_t)... | semmle.label | (size_t)... |
|
||||
| test.cpp:49:32:49:35 | size | semmle.label | size |
|
||||
| test.cpp:49:32:49:35 | size | semmle.label | size |
|
||||
| test.cpp:49:32:49:35 | size | semmle.label | size |
|
||||
| test.cpp:50:26:50:29 | size | semmle.label | size |
|
||||
| test.cpp:50:26:50:29 | size | semmle.label | size |
|
||||
| test.cpp:50:26:50:29 | size | semmle.label | size |
|
||||
| test.cpp:53:35:53:60 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:53:35:53:60 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:53:35:53:60 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:124:18:124:23 | call to getenv | semmle.label | call to getenv |
|
||||
| test.cpp:124:18:124:31 | (const char *)... | semmle.label | (const char *)... |
|
||||
| test.cpp:128:24:128:41 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:128:24:128:41 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:128:24:128:41 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:133:19:133:24 | call to getenv | semmle.label | call to getenv |
|
||||
| test.cpp:133:19:133:32 | (const char *)... | semmle.label | (const char *)... |
|
||||
| test.cpp:135:10:135:27 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:135:10:135:27 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:135:10:135:27 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:148:20:148:25 | call to getenv | semmle.label | call to getenv |
|
||||
| test.cpp:148:20:148:33 | (const char *)... | semmle.label | (const char *)... |
|
||||
| test.cpp:152:11:152:28 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:152:11:152:28 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:152:11:152:28 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:211:9:211:42 | Store | semmle.label | Store |
|
||||
| test.cpp:211:14:211:19 | call to getenv | semmle.label | call to getenv |
|
||||
| test.cpp:211:14:211:27 | (const char *)... | semmle.label | (const char *)... |
|
||||
| test.cpp:224:23:224:23 | s | semmle.label | s |
|
||||
| test.cpp:225:21:225:21 | s | semmle.label | s |
|
||||
| test.cpp:225:21:225:21 | s | semmle.label | s |
|
||||
| test.cpp:225:21:225:21 | s | semmle.label | s |
|
||||
| test.cpp:230:21:230:21 | s | semmle.label | s |
|
||||
| test.cpp:231:21:231:21 | s | semmle.label | s |
|
||||
| test.cpp:231:21:231:21 | s | semmle.label | s |
|
||||
| test.cpp:231:21:231:21 | s | semmle.label | s |
|
||||
| test.cpp:237:24:237:29 | call to getenv | semmle.label | call to getenv |
|
||||
| test.cpp:237:24:237:37 | (const char *)... | semmle.label | (const char *)... |
|
||||
| test.cpp:239:9:239:18 | (size_t)... | semmle.label | (size_t)... |
|
||||
| test.cpp:239:9:239:18 | (size_t)... | semmle.label | (size_t)... |
|
||||
| test.cpp:239:9:239:18 | local_size | semmle.label | local_size |
|
||||
| test.cpp:239:9:239:18 | local_size | semmle.label | local_size |
|
||||
| test.cpp:239:9:239:18 | local_size | semmle.label | local_size |
|
||||
| test.cpp:241:9:241:24 | call to get_tainted_size | semmle.label | call to get_tainted_size |
|
||||
| test.cpp:241:9:241:24 | call to get_tainted_size | semmle.label | call to get_tainted_size |
|
||||
| test.cpp:241:9:241:24 | call to get_tainted_size | semmle.label | call to get_tainted_size |
|
||||
| test.cpp:245:2:245:9 | local_size | semmle.label | local_size |
|
||||
| test.cpp:247:2:247:8 | local_size | semmle.label | local_size |
|
||||
| test.cpp:251:2:251:32 | Chi [array content] | semmle.label | Chi [array content] |
|
||||
| test.cpp:251:2:251:32 | ChiPartial | semmle.label | ChiPartial |
|
||||
| test.cpp:251:18:251:23 | call to getenv | semmle.label | call to getenv |
|
||||
| test.cpp:251:18:251:31 | (const char *)... | semmle.label | (const char *)... |
|
||||
| test.cpp:259:20:259:25 | call to getenv | semmle.label | call to getenv |
|
||||
| test.cpp:259:20:259:33 | (const char *)... | semmle.label | (const char *)... |
|
||||
| test.cpp:263:11:263:29 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:263:11:263:29 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:263:11:263:29 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:289:17:289:20 | Chi | semmle.label | Chi |
|
||||
| test.cpp:289:17:289:20 | get_size output argument [array content] | semmle.label | get_size output argument [array content] |
|
||||
| test.cpp:291:11:291:28 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:291:11:291:28 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:291:11:291:28 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:305:18:305:21 | Chi | semmle.label | Chi |
|
||||
| test.cpp:305:18:305:21 | get_size output argument [array content] | semmle.label | get_size output argument [array content] |
|
||||
| test.cpp:308:10:308:27 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:308:10:308:27 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:308:10:308:27 | ... * ... | semmle.label | ... * ... |
|
||||
#select
|
||||
| test.cpp:42:31:42:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
|
||||
| test.cpp:43:31:43:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:43:38:43:63 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
|
||||
| test.cpp:45:31:45:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:45:38:45:63 | ... + ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
|
||||
| test.cpp:48:25:48:30 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:48:32:48:35 | size | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
|
||||
| test.cpp:49:17:49:30 | new[] | test.cpp:39:21:39:24 | argv | test.cpp:49:26:49:29 | size | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
|
||||
| test.cpp:52:21:52:27 | call to realloc | test.cpp:39:21:39:24 | argv | test.cpp:52:35:52:60 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
|
||||
| test.cpp:79:9:79:29 | new[] | test.cpp:97:18:97:23 | buffer | test.cpp:79:18:79:28 | ... - ... | This allocation size is derived from $@ and might overflow | test.cpp:97:18:97:23 | buffer | user input (fread) |
|
||||
| test.cpp:127:17:127:22 | call to malloc | test.cpp:123:18:123:23 | call to getenv | test.cpp:127:24:127:41 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:123:18:123:23 | call to getenv | user input (getenv) |
|
||||
| test.cpp:134:3:134:8 | call to malloc | test.cpp:132:19:132:24 | call to getenv | test.cpp:134:10:134:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:132:19:132:24 | call to getenv | user input (getenv) |
|
||||
| test.cpp:142:4:142:9 | call to malloc | test.cpp:138:19:138:24 | call to getenv | test.cpp:142:11:142:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:138:19:138:24 | call to getenv | user input (getenv) |
|
||||
| test.cpp:215:14:215:19 | call to malloc | test.cpp:227:24:227:29 | call to getenv | test.cpp:215:21:215:21 | s | This allocation size is derived from $@ and might overflow | test.cpp:227:24:227:29 | call to getenv | user input (getenv) |
|
||||
| test.cpp:221:14:221:19 | call to malloc | test.cpp:227:24:227:29 | call to getenv | test.cpp:221:21:221:21 | s | This allocation size is derived from $@ and might overflow | test.cpp:227:24:227:29 | call to getenv | user input (getenv) |
|
||||
| test.cpp:229:2:229:7 | call to malloc | test.cpp:227:24:227:29 | call to getenv | test.cpp:229:9:229:18 | local_size | This allocation size is derived from $@ and might overflow | test.cpp:227:24:227:29 | call to getenv | user input (getenv) |
|
||||
| test.cpp:231:2:231:7 | call to malloc | test.cpp:201:14:201:19 | call to getenv | test.cpp:231:9:231:24 | call to get_tainted_size | This allocation size is derived from $@ and might overflow | test.cpp:201:14:201:19 | call to getenv | user input (getenv) |
|
||||
| test.cpp:253:4:253:9 | call to malloc | test.cpp:249:20:249:25 | call to getenv | test.cpp:253:11:253:29 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:249:20:249:25 | call to getenv | user input (getenv) |
|
||||
| test.cpp:281:4:281:9 | call to malloc | test.cpp:241:18:241:23 | call to getenv | test.cpp:281:11:281:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:241:18:241:23 | call to getenv | user input (getenv) |
|
||||
| test.cpp:298:3:298:8 | call to malloc | test.cpp:241:18:241:23 | call to getenv | test.cpp:298:10:298:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:241:18:241:23 | call to getenv | user input (getenv) |
|
||||
| test.cpp:305:4:305:9 | call to malloc | test.cpp:301:19:301:24 | call to getenv | test.cpp:305:11:305:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:301:19:301:24 | call to getenv | user input (getenv) |
|
||||
| test.cpp:314:3:314:8 | call to malloc | test.cpp:309:19:309:24 | call to getenv | test.cpp:314:10:314:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:309:19:309:24 | call to getenv | user input (getenv) |
|
||||
| test.cpp:43:31:43:36 | call to malloc | test.cpp:40:21:40:24 | argv | test.cpp:43:38:43:44 | tainted | This allocation size is derived from $@ and might overflow | test.cpp:40:21:40:24 | argv | user input (argv) |
|
||||
| test.cpp:44:31:44:36 | call to malloc | test.cpp:40:21:40:24 | argv | test.cpp:44:38:44:63 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:40:21:40:24 | argv | user input (argv) |
|
||||
| test.cpp:46:31:46:36 | call to malloc | test.cpp:40:21:40:24 | argv | test.cpp:46:38:46:63 | ... + ... | This allocation size is derived from $@ and might overflow | test.cpp:40:21:40:24 | argv | user input (argv) |
|
||||
| test.cpp:49:25:49:30 | call to malloc | test.cpp:40:21:40:24 | argv | test.cpp:49:32:49:35 | size | This allocation size is derived from $@ and might overflow | test.cpp:40:21:40:24 | argv | user input (argv) |
|
||||
| test.cpp:50:17:50:30 | new[] | test.cpp:40:21:40:24 | argv | test.cpp:50:26:50:29 | size | This allocation size is derived from $@ and might overflow | test.cpp:40:21:40:24 | argv | user input (argv) |
|
||||
| test.cpp:53:21:53:27 | call to realloc | test.cpp:40:21:40:24 | argv | test.cpp:53:35:53:60 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:40:21:40:24 | argv | user input (argv) |
|
||||
| test.cpp:128:17:128:22 | call to malloc | test.cpp:124:18:124:23 | call to getenv | test.cpp:128:24:128:41 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:124:18:124:23 | call to getenv | user input (getenv) |
|
||||
| test.cpp:135:3:135:8 | call to malloc | test.cpp:133:19:133:24 | call to getenv | test.cpp:135:10:135:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:133:19:133:24 | call to getenv | user input (getenv) |
|
||||
| test.cpp:152:4:152:9 | call to malloc | test.cpp:148:20:148:25 | call to getenv | test.cpp:152:11:152:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:148:20:148:25 | call to getenv | user input (getenv) |
|
||||
| test.cpp:225:14:225:19 | call to malloc | test.cpp:237:24:237:29 | call to getenv | test.cpp:225:21:225:21 | s | This allocation size is derived from $@ and might overflow | test.cpp:237:24:237:29 | call to getenv | user input (getenv) |
|
||||
| test.cpp:231:14:231:19 | call to malloc | test.cpp:237:24:237:29 | call to getenv | test.cpp:231:21:231:21 | s | This allocation size is derived from $@ and might overflow | test.cpp:237:24:237:29 | call to getenv | user input (getenv) |
|
||||
| test.cpp:239:2:239:7 | call to malloc | test.cpp:237:24:237:29 | call to getenv | test.cpp:239:9:239:18 | local_size | This allocation size is derived from $@ and might overflow | test.cpp:237:24:237:29 | call to getenv | user input (getenv) |
|
||||
| test.cpp:241:2:241:7 | call to malloc | test.cpp:211:14:211:19 | call to getenv | test.cpp:241:9:241:24 | call to get_tainted_size | This allocation size is derived from $@ and might overflow | test.cpp:211:14:211:19 | call to getenv | user input (getenv) |
|
||||
| test.cpp:263:4:263:9 | call to malloc | test.cpp:259:20:259:25 | call to getenv | test.cpp:263:11:263:29 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:259:20:259:25 | call to getenv | user input (getenv) |
|
||||
| test.cpp:291:4:291:9 | call to malloc | test.cpp:251:18:251:23 | call to getenv | test.cpp:291:11:291:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:251:18:251:23 | call to getenv | user input (getenv) |
|
||||
| test.cpp:308:3:308:8 | call to malloc | test.cpp:251:18:251:23 | call to getenv | test.cpp:308:10:308:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:251:18:251:23 | call to getenv | user input (getenv) |
|
||||
|
||||
@@ -7,6 +7,7 @@ void *malloc(size_t size);
|
||||
void *realloc(void *ptr, size_t size);
|
||||
void free(void *ptr);
|
||||
int atoi(const char *nptr);
|
||||
long atol(const char *nptr);
|
||||
struct MyStruct
|
||||
{
|
||||
char data[256];
|
||||
@@ -76,7 +77,7 @@ void processData2(char *start, char *end)
|
||||
{
|
||||
char *copy;
|
||||
|
||||
copy = new char[end - start]; // GOOD [FALSE POSITIVE]
|
||||
copy = new char[end - start]; // GOOD
|
||||
|
||||
// ...
|
||||
|
||||
@@ -137,6 +138,15 @@ void more_bounded_tests() {
|
||||
{
|
||||
int size = atoi(getenv("USER"));
|
||||
|
||||
if (size > 0)
|
||||
{
|
||||
malloc(size * sizeof(int)); // GOOD (overflow not possible)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
long size = atol(getenv("USER"));
|
||||
|
||||
if (size > 0)
|
||||
{
|
||||
malloc(size * sizeof(int)); // BAD
|
||||
@@ -302,7 +312,7 @@ void equality_cases() {
|
||||
|
||||
if ((size == 50) || (size == 100))
|
||||
{
|
||||
malloc(size * sizeof(int)); // GOOD [FALSE POSITIVE]
|
||||
malloc(size * sizeof(int)); // GOOD
|
||||
}
|
||||
}
|
||||
{
|
||||
@@ -311,6 +321,15 @@ void equality_cases() {
|
||||
if (size != 50 && size != 100)
|
||||
return;
|
||||
|
||||
malloc(size * sizeof(int)); // GOOD [FALSE POSITIVE]
|
||||
malloc(size * sizeof(int)); // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
char * strstr(char *, const char *);
|
||||
|
||||
void ptr_diff_case() {
|
||||
char* user = getenv("USER");
|
||||
char* admin_begin_pos = strstr(user, "ADMIN");
|
||||
int offset = admin_begin_pos ? user - admin_begin_pos : 0;
|
||||
malloc(offset); // GOOD
|
||||
}
|
||||
@@ -7,30 +7,10 @@ edges
|
||||
| test.c:34:13:34:18 | call to rand | test.c:35:5:35:5 | r |
|
||||
| test.c:34:13:34:18 | call to rand | test.c:35:5:35:5 | r |
|
||||
| test.c:34:13:34:18 | call to rand | test.c:35:5:35:5 | r |
|
||||
| test.c:39:13:39:21 | ... % ... | test.c:40:5:40:5 | r |
|
||||
| test.c:39:13:39:21 | ... % ... | test.c:40:5:40:5 | r |
|
||||
| test.c:39:13:39:21 | ... % ... | test.c:40:5:40:5 | r |
|
||||
| test.c:39:13:39:21 | ... % ... | test.c:40:5:40:5 | r |
|
||||
| test.c:44:13:44:16 | call to rand | test.c:45:5:45:5 | r |
|
||||
| test.c:44:13:44:16 | call to rand | test.c:45:5:45:5 | r |
|
||||
| test.c:44:13:44:16 | call to rand | test.c:45:5:45:5 | r |
|
||||
| test.c:44:13:44:16 | call to rand | test.c:45:5:45:5 | r |
|
||||
| test.c:54:13:54:16 | call to rand | test.c:56:5:56:5 | r |
|
||||
| test.c:54:13:54:16 | call to rand | test.c:56:5:56:5 | r |
|
||||
| test.c:54:13:54:16 | call to rand | test.c:56:5:56:5 | r |
|
||||
| test.c:54:13:54:16 | call to rand | test.c:56:5:56:5 | r |
|
||||
| test.c:60:13:60:16 | call to rand | test.c:61:5:61:5 | r |
|
||||
| test.c:60:13:60:16 | call to rand | test.c:61:5:61:5 | r |
|
||||
| test.c:60:13:60:16 | call to rand | test.c:61:5:61:5 | r |
|
||||
| test.c:60:13:60:16 | call to rand | test.c:61:5:61:5 | r |
|
||||
| test.c:60:13:60:16 | call to rand | test.c:62:5:62:5 | r |
|
||||
| test.c:60:13:60:16 | call to rand | test.c:62:5:62:5 | r |
|
||||
| test.c:60:13:60:16 | call to rand | test.c:62:5:62:5 | r |
|
||||
| test.c:60:13:60:16 | call to rand | test.c:62:5:62:5 | r |
|
||||
| test.c:66:13:66:16 | call to rand | test.c:67:5:67:5 | r |
|
||||
| test.c:66:13:66:16 | call to rand | test.c:67:5:67:5 | r |
|
||||
| test.c:66:13:66:16 | call to rand | test.c:67:5:67:5 | r |
|
||||
| test.c:66:13:66:16 | call to rand | test.c:67:5:67:5 | r |
|
||||
| test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r |
|
||||
| test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r |
|
||||
| test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r |
|
||||
@@ -67,34 +47,11 @@ nodes
|
||||
| test.c:35:5:35:5 | r | semmle.label | r |
|
||||
| test.c:35:5:35:5 | r | semmle.label | r |
|
||||
| test.c:35:5:35:5 | r | semmle.label | r |
|
||||
| test.c:39:13:39:21 | ... % ... | semmle.label | ... % ... |
|
||||
| test.c:39:13:39:21 | ... % ... | semmle.label | ... % ... |
|
||||
| test.c:40:5:40:5 | r | semmle.label | r |
|
||||
| test.c:40:5:40:5 | r | semmle.label | r |
|
||||
| test.c:40:5:40:5 | r | semmle.label | r |
|
||||
| test.c:44:13:44:16 | call to rand | semmle.label | call to rand |
|
||||
| test.c:44:13:44:16 | call to rand | semmle.label | call to rand |
|
||||
| test.c:45:5:45:5 | r | semmle.label | r |
|
||||
| test.c:45:5:45:5 | r | semmle.label | r |
|
||||
| test.c:45:5:45:5 | r | semmle.label | r |
|
||||
| test.c:54:13:54:16 | call to rand | semmle.label | call to rand |
|
||||
| test.c:54:13:54:16 | call to rand | semmle.label | call to rand |
|
||||
| test.c:56:5:56:5 | r | semmle.label | r |
|
||||
| test.c:56:5:56:5 | r | semmle.label | r |
|
||||
| test.c:56:5:56:5 | r | semmle.label | r |
|
||||
| test.c:60:13:60:16 | call to rand | semmle.label | call to rand |
|
||||
| test.c:60:13:60:16 | call to rand | semmle.label | call to rand |
|
||||
| test.c:61:5:61:5 | r | semmle.label | r |
|
||||
| test.c:61:5:61:5 | r | semmle.label | r |
|
||||
| test.c:61:5:61:5 | r | semmle.label | r |
|
||||
| test.c:62:5:62:5 | r | semmle.label | r |
|
||||
| test.c:62:5:62:5 | r | semmle.label | r |
|
||||
| test.c:62:5:62:5 | r | semmle.label | r |
|
||||
| test.c:66:13:66:16 | call to rand | semmle.label | call to rand |
|
||||
| test.c:66:13:66:16 | call to rand | semmle.label | call to rand |
|
||||
| test.c:67:5:67:5 | r | semmle.label | r |
|
||||
| test.c:67:5:67:5 | r | semmle.label | r |
|
||||
| test.c:67:5:67:5 | r | semmle.label | r |
|
||||
| test.c:75:13:75:19 | ... ^ ... | semmle.label | ... ^ ... |
|
||||
| test.c:75:13:75:19 | ... ^ ... | semmle.label | ... ^ ... |
|
||||
| test.c:77:9:77:9 | r | semmle.label | r |
|
||||
@@ -133,10 +90,7 @@ nodes
|
||||
#select
|
||||
| test.c:21:17:21:17 | r | test.c:18:13:18:16 | call to rand | test.c:21:17:21:17 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:18:13:18:16 | call to rand | Uncontrolled value |
|
||||
| test.c:35:5:35:5 | r | test.c:34:13:34:18 | call to rand | test.c:35:5:35:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:34:13:34:18 | call to rand | Uncontrolled value |
|
||||
| test.c:40:5:40:5 | r | test.c:39:13:39:21 | ... % ... | test.c:40:5:40:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:39:13:39:21 | ... % ... | Uncontrolled value |
|
||||
| test.c:45:5:45:5 | r | test.c:44:13:44:16 | call to rand | test.c:45:5:45:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:44:13:44:16 | call to rand | Uncontrolled value |
|
||||
| test.c:56:5:56:5 | r | test.c:54:13:54:16 | call to rand | test.c:56:5:56:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:54:13:54:16 | call to rand | Uncontrolled value |
|
||||
| test.c:67:5:67:5 | r | test.c:66:13:66:16 | call to rand | test.c:67:5:67:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:66:13:66:16 | call to rand | Uncontrolled value |
|
||||
| test.c:77:9:77:9 | r | test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:75:13:75:19 | ... ^ ... | Uncontrolled value |
|
||||
| test.c:100:5:100:5 | r | test.c:99:14:99:19 | call to rand | test.c:100:5:100:5 | r | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:99:14:99:19 | call to rand | Uncontrolled value |
|
||||
| test.cpp:25:7:25:7 | r | test.cpp:8:9:8:12 | call to rand | test.cpp:25:7:25:7 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:8:9:8:12 | call to rand | Uncontrolled value |
|
||||
|
||||
@@ -37,7 +37,7 @@ void randomTester() {
|
||||
|
||||
{
|
||||
int r = RANDN(100);
|
||||
r += 100; // GOOD: The return from RANDN is bounded [FALSE POSITIVE]
|
||||
r += 100; // GOOD: The return from RANDN is bounded
|
||||
}
|
||||
|
||||
{
|
||||
@@ -53,7 +53,7 @@ void randomTester() {
|
||||
{
|
||||
int r = rand();
|
||||
r = r / 10;
|
||||
r += 100; // GOOD [FALSE POSITIVE]
|
||||
r += 100; // GOOD
|
||||
}
|
||||
|
||||
{
|
||||
@@ -64,7 +64,7 @@ void randomTester() {
|
||||
|
||||
{
|
||||
int r = rand() & 0xFF;
|
||||
r += 100; // GOOD [FALSE POSITIVE]
|
||||
r += 100; // GOOD
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -415,7 +415,7 @@ namespace Semmle.Autobuild.CSharp.Tests
|
||||
actions.RunProcess["cmd.exe /C dotnet --info"] = 0;
|
||||
actions.RunProcess[@"cmd.exe /C dotnet clean C:\Project\test.csproj"] = 0;
|
||||
actions.RunProcess[@"cmd.exe /C dotnet restore C:\Project\test.csproj"] = 0;
|
||||
actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto dotnet build --no-incremental C:\Project\test.csproj"] = 0;
|
||||
actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto dotnet build --no-incremental /p:UseSharedCompilation=false C:\Project\test.csproj"] = 0;
|
||||
actions.FileExists["csharp.log"] = true;
|
||||
actions.FileExists[@"C:\Project\test.csproj"] = true;
|
||||
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
|
||||
@@ -439,9 +439,6 @@ namespace Semmle.Autobuild.CSharp.Tests
|
||||
[Fact]
|
||||
public void TestLinuxCSharpAutoBuilder()
|
||||
{
|
||||
actions.RunProcess["dotnet --list-runtimes"] = 0;
|
||||
actions.RunProcessOut["dotnet --list-runtimes"] = @"Microsoft.AspNetCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
|
||||
Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]";
|
||||
actions.RunProcess["dotnet --info"] = 0;
|
||||
actions.RunProcess[@"dotnet clean C:\Project/test.csproj"] = 0;
|
||||
actions.RunProcess[@"dotnet restore C:\Project/test.csproj"] = 0;
|
||||
@@ -463,7 +460,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
|
||||
actions.LoadXml[@"C:\Project/test.csproj"] = xml;
|
||||
|
||||
var autobuilder = CreateAutoBuilder(false);
|
||||
TestAutobuilderScript(autobuilder, 0, 5);
|
||||
TestAutobuilderScript(autobuilder, 0, 4);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -603,8 +600,6 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
|
||||
[Fact]
|
||||
public void TestLinuxBuildCommand()
|
||||
{
|
||||
actions.RunProcess["dotnet --list-runtimes"] = 1;
|
||||
actions.RunProcessOut["dotnet --list-runtimes"] = "";
|
||||
actions.RunProcess[@"C:\odasa/tools/odasa index --auto ""./build.sh --skip-tests"""] = 0;
|
||||
actions.FileExists["csharp.log"] = true;
|
||||
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
|
||||
@@ -615,7 +610,7 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
|
||||
SkipVsWhere();
|
||||
|
||||
var autobuilder = CreateAutoBuilder(false, buildCommand: "./build.sh --skip-tests");
|
||||
TestAutobuilderScript(autobuilder, 0, 2);
|
||||
TestAutobuilderScript(autobuilder, 0, 1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -626,14 +621,12 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
|
||||
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
|
||||
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = "";
|
||||
actions.RunProcess[@"/bin/chmod u+x C:\Project/build/build.sh"] = 0;
|
||||
actions.RunProcess["dotnet --list-runtimes"] = 1;
|
||||
actions.RunProcessOut["dotnet --list-runtimes"] = "";
|
||||
actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/build/build.sh"] = 0;
|
||||
actions.RunProcessWorkingDirectory[@"C:\odasa/tools/odasa index --auto C:\Project/build/build.sh"] = @"C:\Project/build";
|
||||
actions.FileExists["csharp.log"] = true;
|
||||
|
||||
var autobuilder = CreateAutoBuilder(false);
|
||||
TestAutobuilderScript(autobuilder, 0, 3);
|
||||
TestAutobuilderScript(autobuilder, 0, 2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -645,14 +638,12 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
|
||||
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = "";
|
||||
|
||||
actions.RunProcess[@"/bin/chmod u+x C:\Project/build.sh"] = 0;
|
||||
actions.RunProcess["dotnet --list-runtimes"] = 1;
|
||||
actions.RunProcessOut["dotnet --list-runtimes"] = "";
|
||||
actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/build.sh"] = 0;
|
||||
actions.RunProcessWorkingDirectory[@"C:\odasa/tools/odasa index --auto C:\Project/build.sh"] = @"C:\Project";
|
||||
actions.FileExists["csharp.log"] = false;
|
||||
|
||||
var autobuilder = CreateAutoBuilder(false);
|
||||
TestAutobuilderScript(autobuilder, 1, 3);
|
||||
TestAutobuilderScript(autobuilder, 1, 2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -664,14 +655,12 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
|
||||
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = "";
|
||||
|
||||
actions.RunProcess[@"/bin/chmod u+x C:\Project/build.sh"] = 0;
|
||||
actions.RunProcess["dotnet --list-runtimes"] = 1;
|
||||
actions.RunProcessOut["dotnet --list-runtimes"] = "";
|
||||
actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/build.sh"] = 5;
|
||||
actions.RunProcessWorkingDirectory[@"C:\odasa/tools/odasa index --auto C:\Project/build.sh"] = @"C:\Project";
|
||||
actions.FileExists["csharp.log"] = true;
|
||||
|
||||
var autobuilder = CreateAutoBuilder(false);
|
||||
TestAutobuilderScript(autobuilder, 1, 3);
|
||||
TestAutobuilderScript(autobuilder, 1, 2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -872,9 +861,6 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
|
||||
[Fact]
|
||||
public void TestSkipNugetDotnet()
|
||||
{
|
||||
actions.RunProcess["dotnet --list-runtimes"] = 0;
|
||||
actions.RunProcessOut["dotnet --list-runtimes"] = @"Microsoft.AspNetCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
|
||||
Microsoft.NETCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]";
|
||||
actions.RunProcess["dotnet --info"] = 0;
|
||||
actions.RunProcess[@"dotnet clean C:\Project/test.csproj"] = 0;
|
||||
actions.RunProcess[@"dotnet restore C:\Project/test.csproj"] = 0;
|
||||
@@ -896,7 +882,7 @@ Microsoft.NETCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
|
||||
actions.LoadXml[@"C:\Project/test.csproj"] = xml;
|
||||
|
||||
var autobuilder = CreateAutoBuilder(false, dotnetArguments: "--no-restore"); // nugetRestore=false does not work for now.
|
||||
TestAutobuilderScript(autobuilder, 0, 5);
|
||||
TestAutobuilderScript(autobuilder, 0, 4);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -907,13 +893,10 @@ Microsoft.NETCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
|
||||
actions.RunProcess[@"chmod u+x dotnet-install.sh"] = 0;
|
||||
actions.RunProcess[@"./dotnet-install.sh --channel release --version 2.1.3 --install-dir C:\Project/.dotnet"] = 0;
|
||||
actions.RunProcess[@"rm dotnet-install.sh"] = 0;
|
||||
actions.RunProcess[@"C:\Project/.dotnet/dotnet --list-runtimes"] = 0;
|
||||
actions.RunProcessOut[@"C:\Project/.dotnet/dotnet --list-runtimes"] = @"Microsoft.AspNetCore.App 3.0.0 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
|
||||
Microsoft.NETCore.App 3.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]";
|
||||
actions.RunProcess[@"C:\Project/.dotnet/dotnet --info"] = 0;
|
||||
actions.RunProcess[@"C:\Project/.dotnet/dotnet clean C:\Project/test.csproj"] = 0;
|
||||
actions.RunProcess[@"C:\Project/.dotnet/dotnet restore C:\Project/test.csproj"] = 0;
|
||||
actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/.dotnet/dotnet build --no-incremental C:\Project/test.csproj"] = 0;
|
||||
actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/.dotnet/dotnet build --no-incremental /p:UseSharedCompilation=false C:\Project/test.csproj"] = 0;
|
||||
actions.FileExists["csharp.log"] = true;
|
||||
actions.FileExists["test.csproj"] = true;
|
||||
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
|
||||
@@ -933,7 +916,7 @@ Microsoft.NETCore.App 3.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
|
||||
actions.DownloadFiles.Add(("https://dot.net/v1/dotnet-install.sh", "dotnet-install.sh"));
|
||||
|
||||
var autobuilder = CreateAutoBuilder(false, dotnetVersion: "2.1.3");
|
||||
TestAutobuilderScript(autobuilder, 0, 9);
|
||||
TestAutobuilderScript(autobuilder, 0, 8);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -945,11 +928,6 @@ Microsoft.NETCore.App 3.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
|
||||
actions.RunProcess[@"chmod u+x dotnet-install.sh"] = 0;
|
||||
actions.RunProcess[@"./dotnet-install.sh --channel release --version 2.1.3 --install-dir C:\Project/.dotnet"] = 0;
|
||||
actions.RunProcess[@"rm dotnet-install.sh"] = 0;
|
||||
actions.RunProcess[@"C:\Project/.dotnet/dotnet --list-runtimes"] = 0;
|
||||
actions.RunProcessOut[@"C:\Project/.dotnet/dotnet --list-runtimes"] = @"Microsoft.AspNetCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
|
||||
Microsoft.AspNetCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
|
||||
Microsoft.NETCore.App 2.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
|
||||
Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]";
|
||||
actions.RunProcess[@"C:\Project/.dotnet/dotnet --info"] = 0;
|
||||
actions.RunProcess[@"C:\Project/.dotnet/dotnet clean C:\Project/test.csproj"] = 0;
|
||||
actions.RunProcess[@"C:\Project/.dotnet/dotnet restore C:\Project/test.csproj"] = 0;
|
||||
@@ -973,7 +951,7 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
|
||||
actions.DownloadFiles.Add(("https://dot.net/v1/dotnet-install.sh", "dotnet-install.sh"));
|
||||
|
||||
var autobuilder = CreateAutoBuilder(false, dotnetVersion: "2.1.3");
|
||||
TestAutobuilderScript(autobuilder, 0, 9);
|
||||
TestAutobuilderScript(autobuilder, 0, 8);
|
||||
}
|
||||
|
||||
private void TestDotnetVersionWindows(Action action, int commandsRun)
|
||||
@@ -984,7 +962,7 @@ Microsoft.NETCore.App 2.1.4 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
|
||||
actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet --info"] = 0;
|
||||
actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet clean C:\Project\test.csproj"] = 0;
|
||||
actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet restore C:\Project\test.csproj"] = 0;
|
||||
actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto C:\Project\.dotnet\dotnet build --no-incremental C:\Project\test.csproj"] = 0;
|
||||
actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto C:\Project\.dotnet\dotnet build --no-incremental /p:UseSharedCompilation=false C:\Project\test.csproj"] = 0;
|
||||
actions.FileExists["csharp.log"] = true;
|
||||
actions.FileExists[@"C:\Project\test.csproj"] = true;
|
||||
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace Semmle.Autobuild.CSharp
|
||||
builder.Log(Severity.Info, "Attempting to build using .NET Core");
|
||||
}
|
||||
|
||||
return WithDotNet(builder, (dotNetPath, environment, compatibleClr) =>
|
||||
return WithDotNet(builder, (dotNetPath, environment) =>
|
||||
{
|
||||
var ret = GetInfoCommand(builder.Actions, dotNetPath, environment);
|
||||
foreach (var projectOrSolution in builder.ProjectsOrSolutionsToBuild)
|
||||
@@ -49,7 +49,7 @@ namespace Semmle.Autobuild.CSharp
|
||||
restoreCommand.QuoteArgument(projectOrSolution.FullPath);
|
||||
var restore = restoreCommand.Script;
|
||||
|
||||
var build = GetBuildScript(builder, dotNetPath, environment, compatibleClr, projectOrSolution.FullPath);
|
||||
var build = GetBuildScript(builder, dotNetPath, environment, projectOrSolution.FullPath);
|
||||
|
||||
ret &= BuildScript.Try(clean) & BuildScript.Try(restore) & build;
|
||||
}
|
||||
@@ -57,7 +57,7 @@ namespace Semmle.Autobuild.CSharp
|
||||
});
|
||||
}
|
||||
|
||||
private static BuildScript WithDotNet(Autobuilder builder, Func<string?, IDictionary<string, string>?, bool, BuildScript> f)
|
||||
private static BuildScript WithDotNet(Autobuilder builder, Func<string?, IDictionary<string, string>?, BuildScript> f)
|
||||
{
|
||||
var installDir = builder.Actions.PathCombine(builder.Options.RootDirectory, ".dotnet");
|
||||
var installScript = DownloadDotNet(builder, installDir);
|
||||
@@ -81,35 +81,10 @@ namespace Semmle.Autobuild.CSharp
|
||||
env = null;
|
||||
}
|
||||
|
||||
// The CLR tracer is always compatible on Windows
|
||||
if (builder.Actions.IsWindows())
|
||||
return f(installDir, env, true);
|
||||
|
||||
// The CLR tracer is only compatible on .NET Core >= 3 on Linux and macOS (see
|
||||
// https://github.com/dotnet/coreclr/issues/19622)
|
||||
return BuildScript.Bind(GetInstalledRuntimesScript(builder.Actions, installDir, env), (runtimes, runtimesRet) =>
|
||||
{
|
||||
var compatibleClr = false;
|
||||
if (runtimesRet == 0)
|
||||
{
|
||||
var minimumVersion = new Version(3, 0);
|
||||
var regex = new Regex(@"Microsoft\.NETCore\.App (\d\.\d\.\d)");
|
||||
compatibleClr = runtimes
|
||||
.Select(runtime => regex.Match(runtime))
|
||||
.Where(m => m.Success)
|
||||
.Select(m => m.Groups[1].Value)
|
||||
.Any(m => Version.TryParse(m, out var v) && v >= minimumVersion);
|
||||
}
|
||||
|
||||
if (!compatibleClr)
|
||||
{
|
||||
if (env is null)
|
||||
env = new Dictionary<string, string>();
|
||||
env.Add("UseSharedCompilation", "false");
|
||||
}
|
||||
|
||||
return f(installDir, env, compatibleClr);
|
||||
});
|
||||
if (env is null)
|
||||
env = new Dictionary<string, string>();
|
||||
env.Add("UseSharedCompilation", "false");
|
||||
return f(installDir, env);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -122,7 +97,7 @@ namespace Semmle.Autobuild.CSharp
|
||||
/// are needed).
|
||||
/// </summary>
|
||||
public static BuildScript WithDotNet(Autobuilder builder, Func<IDictionary<string, string>?, BuildScript> f)
|
||||
=> WithDotNet(builder, (_1, env, _2) => f(env));
|
||||
=> WithDotNet(builder, (_1, env) => f(env));
|
||||
|
||||
/// <summary>
|
||||
/// Returns a script for downloading relevant versions of the
|
||||
@@ -259,34 +234,17 @@ namespace Semmle.Autobuild.CSharp
|
||||
return restore;
|
||||
}
|
||||
|
||||
private static BuildScript GetInstalledRuntimesScript(IBuildActions actions, string? dotNetPath, IDictionary<string, string>? environment)
|
||||
{
|
||||
var listSdks = new CommandBuilder(actions, environment: environment, silent: true).
|
||||
RunCommand(DotNetCommand(actions, dotNetPath)).
|
||||
Argument("--list-runtimes");
|
||||
return listSdks.Script;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the `dotnet build` script.
|
||||
///
|
||||
/// The CLR tracer only works on .NET Core >= 3 on Linux and macOS (see
|
||||
/// https://github.com/dotnet/coreclr/issues/19622), so in case we are
|
||||
/// running on an older .NET Core, we disable shared compilation (and
|
||||
/// hence the need for CLR tracing), by adding a
|
||||
/// `/p:UseSharedCompilation=false` argument.
|
||||
/// </summary>
|
||||
private static BuildScript GetBuildScript(Autobuilder builder, string? dotNetPath, IDictionary<string, string>? environment, bool compatibleClr, string projOrSln)
|
||||
private static BuildScript GetBuildScript(Autobuilder builder, string? dotNetPath, IDictionary<string, string>? environment, string projOrSln)
|
||||
{
|
||||
var build = new CommandBuilder(builder.Actions, null, environment);
|
||||
var script = builder.MaybeIndex(build, DotNetCommand(builder.Actions, dotNetPath)).
|
||||
Argument("build").
|
||||
Argument("--no-incremental");
|
||||
|
||||
return compatibleClr ?
|
||||
script.Argument(builder.Options.DotNetArguments).
|
||||
QuoteArgument(projOrSln).
|
||||
Script :
|
||||
return
|
||||
script.Argument("/p:UseSharedCompilation=false").
|
||||
Argument(builder.Options.DotNetArguments).
|
||||
QuoteArgument(projOrSln).
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* @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
|
||||
* lines-of-code
|
||||
*/
|
||||
|
||||
import csharp
|
||||
|
||||
@@ -165,13 +165,15 @@ build steps, you may need to explicitly define each step in the command line.
|
||||
|
||||
.. pull-quote:: Creating databases for Go
|
||||
|
||||
For Go, you should always use the CodeQL autobuilder. Install the Go
|
||||
toolchain (version 1.11 or later) and, if there are dependencies, the
|
||||
appropriate dependency manager (such as `dep
|
||||
For Go, install the Go toolchain (version 1.11 or later) and, if there
|
||||
are dependencies, the appropriate dependency manager (such as `dep
|
||||
<https://golang.github.io/dep/>`__).
|
||||
|
||||
Do not specify any build commands, as you will override the autobuilder
|
||||
invocation, which will create an empty database.
|
||||
The Go autobuilder attempts to automatically detect code written in Go in a repository,
|
||||
and only runs build scripts in an attempt to fetch dependencies. To force
|
||||
CodeQL to limit extraction to the files compiled by your build script, set the environment variable
|
||||
`CODEQL_EXTRACTOR_GO_BUILD_TRACING=on` or use the ``--command`` option to specify a
|
||||
build command.
|
||||
|
||||
Specifying build commands
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -200,6 +202,14 @@ commands that you can specify for compiled languages.
|
||||
|
||||
codeql database create csharp-database --language=csharp --command='dotnet build /p:UseSharedCompilation=false /t:rebuild'
|
||||
|
||||
- Go project built using the ``COEQL_EXTRACTOR_GO_BUILD_TRACING=on`` environment variable::
|
||||
|
||||
CODEQL_EXTRACTOR_GO_BUILD_TRACING=on codeql database create go-database --language=go
|
||||
|
||||
- Go project built using a custom build script::
|
||||
|
||||
codeql database create go-database --language=go --command='./scripts/build.sh'
|
||||
|
||||
- Java project built using Gradle::
|
||||
|
||||
codeql database create java-database --language=java --command='gradle clean test'
|
||||
|
||||
@@ -157,8 +157,11 @@ Python built-in support
|
||||
Tornado, Web framework
|
||||
PyYAML, Serialization
|
||||
dill, Serialization
|
||||
simplejson, Serialization
|
||||
ujson, Serialization
|
||||
fabric, Utility library
|
||||
invoke, Utility library
|
||||
idna, Utility library
|
||||
mysql-connector-python, Database
|
||||
MySQLdb, Database
|
||||
psycopg2, Database
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* Increase coverage of dataflow through Jackson JSON deserialized objects.
|
||||
@@ -6,6 +6,7 @@
|
||||
* or comments.
|
||||
* @kind metric
|
||||
* @tags summary
|
||||
* lines-of-code
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import org.python.util.PythonInterpreter;
|
||||
|
||||
public class JythonInjection extends HttpServlet {
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
response.setContentType("text/plain");
|
||||
String code = request.getParameter("code");
|
||||
PythonInterpreter interpreter = null;
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
|
||||
try {
|
||||
interpreter = new PythonInterpreter();
|
||||
interpreter.setOut(out);
|
||||
interpreter.setErr(out);
|
||||
|
||||
// BAD: allow execution of arbitrary Python code
|
||||
interpreter.exec(code);
|
||||
out.flush();
|
||||
|
||||
response.getWriter().print(out.toString());
|
||||
} catch(PyException ex) {
|
||||
response.getWriter().println(ex.getMessage());
|
||||
} finally {
|
||||
if (interpreter != null) {
|
||||
interpreter.close();
|
||||
}
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
response.setContentType("text/plain");
|
||||
String code = request.getParameter("code");
|
||||
PythonInterpreter interpreter = null;
|
||||
|
||||
try {
|
||||
interpreter = new PythonInterpreter();
|
||||
// BAD: allow execution of arbitrary Python code
|
||||
PyObject py = interpreter.eval(code);
|
||||
|
||||
response.getWriter().print(py.toString());
|
||||
} catch(PyException ex) {
|
||||
response.getWriter().println(ex.getMessage());
|
||||
} finally {
|
||||
if (interpreter != null) {
|
||||
interpreter.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>Python has been the most widely used programming language in recent years, and Jython
|
||||
(formerly known as JPython) is a popular Java implementation of Python. It allows
|
||||
embedded Python scripting inside Java applications and provides an interactive interpreter
|
||||
that can be used to interact with Java packages or with running Java applications. 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>In general, including user input in Jython expression should be avoided. If user input
|
||||
must be included in an expression, it should be then evaluated in a safe context that
|
||||
doesn't allow arbitrary code invocation.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>The following code could execute arbitrary code in Jython Interpreter</p>
|
||||
<sample src="JythonInjection.java" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
Jython Organization: <a href="https://jython.readthedocs.io/en/latest/JythonAndJavaIntegration/">Jython and Java Integration</a>
|
||||
</li>
|
||||
<li>
|
||||
PortSwigger: <a href="https://portswigger.net/kb/issues/00100f10_python-code-injection">Python code injection</a>
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
112
java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql
Normal file
112
java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql
Normal file
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* @name Injection in Jython
|
||||
* @description Evaluation of a user-controlled malicious expression in Java Python
|
||||
* interpreter may lead to remote code execution.
|
||||
* @kind path-problem
|
||||
* @id java/jython-injection
|
||||
* @tags security
|
||||
* external/cwe/cwe-094
|
||||
* external/cwe/cwe-095
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.frameworks.spring.SpringController
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/** The class `org.python.util.PythonInterpreter`. */
|
||||
class PythonInterpreter extends RefType {
|
||||
PythonInterpreter() { this.hasQualifiedName("org.python.util", "PythonInterpreter") }
|
||||
}
|
||||
|
||||
/** A method that evaluates, compiles or executes a Jython expression. */
|
||||
class InterpretExprMethod extends Method {
|
||||
InterpretExprMethod() {
|
||||
this.getDeclaringType().getAnAncestor*() instanceof PythonInterpreter and
|
||||
getName().matches(["exec%", "run%", "eval", "compile"])
|
||||
}
|
||||
}
|
||||
|
||||
/** The class `org.python.core.BytecodeLoader`. */
|
||||
class BytecodeLoader extends RefType {
|
||||
BytecodeLoader() { this.hasQualifiedName("org.python.core", "BytecodeLoader") }
|
||||
}
|
||||
|
||||
/** Holds if a Jython expression if evaluated, compiled or executed. */
|
||||
predicate runsCode(MethodAccess ma, Expr sink) {
|
||||
exists(Method m | m = ma.getMethod() |
|
||||
m instanceof InterpretExprMethod and
|
||||
sink = ma.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
/** A method that loads Java class data. */
|
||||
class LoadClassMethod extends Method {
|
||||
LoadClassMethod() {
|
||||
this.getDeclaringType().getAnAncestor*() instanceof BytecodeLoader and
|
||||
hasName(["makeClass", "makeCode"])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `ma` is a call to a class-loading method, and `sink` is the byte array
|
||||
* representing the class to be loaded.
|
||||
*/
|
||||
predicate loadsClass(MethodAccess ma, Expr sink) {
|
||||
exists(Method m, int i | m = ma.getMethod() |
|
||||
m instanceof LoadClassMethod and
|
||||
m.getParameter(i).getType() instanceof Array and // makeClass(java.lang.String name, byte[] data, ...)
|
||||
sink = ma.getArgument(i)
|
||||
)
|
||||
}
|
||||
|
||||
/** The class `org.python.core.Py`. */
|
||||
class Py extends RefType {
|
||||
Py() { this.hasQualifiedName("org.python.core", "Py") }
|
||||
}
|
||||
|
||||
/** A method declared on class `Py` or one of its descendants that compiles Python code. */
|
||||
class PyCompileMethod extends Method {
|
||||
PyCompileMethod() {
|
||||
this.getDeclaringType().getAnAncestor*() instanceof Py and
|
||||
getName().matches("compile%")
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if source code is compiled with `PyCompileMethod`. */
|
||||
predicate compile(MethodAccess ma, Expr sink) {
|
||||
exists(Method m | m = ma.getMethod() |
|
||||
m instanceof PyCompileMethod and
|
||||
sink = ma.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
/** An expression loaded by Jython. */
|
||||
class CodeInjectionSink extends DataFlow::ExprNode {
|
||||
MethodAccess methodAccess;
|
||||
|
||||
CodeInjectionSink() {
|
||||
runsCode(methodAccess, this.getExpr()) or
|
||||
loadsClass(methodAccess, this.getExpr()) or
|
||||
compile(methodAccess, this.getExpr())
|
||||
}
|
||||
|
||||
MethodAccess getMethodAccess() { result = methodAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint configuration for tracking flow from `RemoteFlowSource` to a Jython method call
|
||||
* `CodeInjectionSink` that executes injected code.
|
||||
*/
|
||||
class CodeInjectionConfiguration extends TaintTracking::Configuration {
|
||||
CodeInjectionConfiguration() { this = "CodeInjectionConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof CodeInjectionSink }
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, CodeInjectionConfiguration conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink.getNode().(CodeInjectionSink).getMethodAccess(), source, sink, "Jython evaluate $@.",
|
||||
source.getNode(), "user input"
|
||||
@@ -0,0 +1,40 @@
|
||||
import org.mozilla.javascript.ClassShutter;
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.Scriptable;
|
||||
|
||||
public class RhinoInjection extends HttpServlet {
|
||||
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
response.setContentType("text/plain");
|
||||
String code = request.getParameter("code");
|
||||
Context ctx = Context.enter();
|
||||
try {
|
||||
{
|
||||
// BAD: allow arbitrary Java and JavaScript code to be executed
|
||||
Scriptable scope = ctx.initStandardObjects();
|
||||
}
|
||||
|
||||
{
|
||||
// GOOD: enable the safe mode
|
||||
Scriptable scope = ctx.initSafeStandardObjects();
|
||||
}
|
||||
|
||||
{
|
||||
// GOOD: enforce a constraint on allowed classes
|
||||
Scriptable scope = ctx.initStandardObjects();
|
||||
ctx.setClassShutter(new ClassShutter() {
|
||||
public boolean visibleToScripts(String className) {
|
||||
return className.startsWith("com.example.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Object result = ctx.evaluateString(scope, code, "<code>", 1, null);
|
||||
response.getWriter().print(Context.toString(result));
|
||||
} catch(RhinoException ex) {
|
||||
response.getWriter().println(ex.getMessage());
|
||||
} finally {
|
||||
Context.exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>The ScriptEngine API has been available since the release of Java 6.
|
||||
It allows applications to interact with scripts written in languages such as JavaScript.</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>Use "Cloudbees Rhino Sandbox" or sandboxing with SecurityManager or use <a href="https://www.graalvm.org/">graalvm</a> instead.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>The following code could execute random JavaScript code</p>
|
||||
<sample src="ScriptEngine.java" />
|
||||
<sample src="NashornScriptEngine.java" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
CERT coding standard: <a href="https://wiki.sei.cmu.edu/confluence/display/java/IDS52-J.+Prevent+code+injection">ScriptEngine code injection</a>
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -1,51 +0,0 @@
|
||||
/**
|
||||
* @name ScriptEngine evaluation
|
||||
* @description Malicious Javascript code could cause arbitrary command execution at the OS level
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id java/unsafe-eval
|
||||
* @tags security
|
||||
* external/cwe/cwe-094
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import DataFlow::PathGraph
|
||||
|
||||
class ScriptEngineMethod extends Method {
|
||||
ScriptEngineMethod() {
|
||||
this.getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "ScriptEngine") and
|
||||
this.hasName("eval")
|
||||
}
|
||||
}
|
||||
|
||||
predicate scriptEngine(MethodAccess ma, Expr sink) {
|
||||
exists(Method m | m = ma.getMethod() |
|
||||
m instanceof ScriptEngineMethod and
|
||||
sink = ma.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
class ScriptEngineSink extends DataFlow::ExprNode {
|
||||
ScriptEngineSink() { scriptEngine(_, this.getExpr()) }
|
||||
|
||||
MethodAccess getMethodAccess() { scriptEngine(result, this.getExpr()) }
|
||||
}
|
||||
|
||||
class ScriptEngineConfiguration extends TaintTracking::Configuration {
|
||||
ScriptEngineConfiguration() { this = "ScriptEngineConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source instanceof RemoteFlowSource
|
||||
or
|
||||
source instanceof LocalUserInput
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof ScriptEngineSink }
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, ScriptEngineConfiguration conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink.getNode().(ScriptEngineSink).getMethodAccess(), source, sink, "ScriptEngine eval $@.",
|
||||
source.getNode(), "user input"
|
||||
@@ -0,0 +1,52 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>The Java Scripting API has been available since the release of Java 6. It allows
|
||||
applications to interact with scripts written in languages such as JavaScript. It serves
|
||||
as an embedded scripting engine inside Java applications which allows Java-to-JavaScript
|
||||
interoperability and provides a seamless integration between the two languages. If an
|
||||
expression is built using attacker-controlled data, and then evaluated in a powerful
|
||||
context, it may allow the attacker to run arbitrary code.</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>In general, including user input in a Java Script Engine expression should be avoided.
|
||||
If user input must be included in the expression, it should be then evaluated in a safe
|
||||
context that doesn't allow arbitrary code invocation. Use "Cloudbees Rhino Sandbox" or
|
||||
sandboxing with SecurityManager, which will be deprecated in a future release, or use
|
||||
<a href="https://www.graalvm.org/">GraalVM</a> instead.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>The following code could execute user-supplied JavaScript code in <code>ScriptEngine</code></p>
|
||||
<sample src="ScriptEngine.java" />
|
||||
<sample src="NashornScriptEngine.java" />
|
||||
|
||||
<p>The following example shows two ways of using Rhino expression. In the 'BAD' case,
|
||||
an unsafe context is initialized with <code>initStandardObjects</code> that allows arbitrary
|
||||
Java code to be executed. In the 'GOOD' case, a safe context is initialized with
|
||||
<code>initSafeStandardObjects</code> or <code>setClassShutter</code>.</p>
|
||||
<sample src="RhinoInjection.java" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
CERT coding standard: <a href="https://wiki.sei.cmu.edu/confluence/display/java/IDS52-J.+Prevent+code+injection">ScriptEngine code injection</a>
|
||||
</li>
|
||||
<li>
|
||||
GraalVM: <a href="https://www.graalvm.org/reference-manual/js/NashornMigrationGuide/#secure-by-default">Secure by Default</a>
|
||||
</li>
|
||||
<li>
|
||||
Mozilla Rhino: <a href="https://github.com/mozilla/rhino">Rhino: JavaScript in Java</a>
|
||||
</li>
|
||||
<li>
|
||||
Rhino Sandbox: <a href="https://github.com/javadelight/delight-rhino-sandbox">A sandbox to execute JavaScript code with Rhino in Java</a>
|
||||
</li>
|
||||
<li>
|
||||
GuardRails: <a href="https://docs.guardrails.io/docs/en/vulnerabilities/java/insecure_use_of_dangerous_function#code-injection">Code Injection</a>
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
146
java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql
Normal file
146
java/ql/src/experimental/Security/CWE/CWE-094/ScriptInjection.ql
Normal file
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* @name Injection in Java Script Engine
|
||||
* @description Evaluation of user-controlled data using the Java Script Engine may
|
||||
* lead to remote code execution.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id java/unsafe-eval
|
||||
* @tags security
|
||||
* external/cwe/cwe-094
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/** A method of ScriptEngine that allows code injection. */
|
||||
class ScriptEngineMethod extends Method {
|
||||
ScriptEngineMethod() {
|
||||
this.getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "ScriptEngine") and
|
||||
this.hasName("eval")
|
||||
or
|
||||
this.getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "Compilable") and
|
||||
this.hasName("compile")
|
||||
or
|
||||
this.getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "ScriptEngineFactory") and
|
||||
this.hasName(["getProgram", "getMethodCallSyntax"])
|
||||
}
|
||||
}
|
||||
|
||||
/** The context class `org.mozilla.javascript.Context` of Rhino Java Script Engine. */
|
||||
class RhinoContext extends RefType {
|
||||
RhinoContext() { this.hasQualifiedName("org.mozilla.javascript", "Context") }
|
||||
}
|
||||
|
||||
/** A method that evaluates a Rhino expression with `org.mozilla.javascript.Context`. */
|
||||
class RhinoEvaluateExpressionMethod extends Method {
|
||||
RhinoEvaluateExpressionMethod() {
|
||||
this.getDeclaringType().getAnAncestor*() instanceof RhinoContext and
|
||||
this.hasName([
|
||||
"evaluateString", "evaluateReader", "compileFunction", "compileReader", "compileString"
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method that compiles a Rhino expression with
|
||||
* `org.mozilla.javascript.optimizer.ClassCompiler`.
|
||||
*/
|
||||
class RhinoCompileClassMethod extends Method {
|
||||
RhinoCompileClassMethod() {
|
||||
this.getDeclaringType()
|
||||
.getASupertype*()
|
||||
.hasQualifiedName("org.mozilla.javascript.optimizer", "ClassCompiler") and
|
||||
this.hasName("compileToClassFiles")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method that defines a Java class from a Rhino expression with
|
||||
* `org.mozilla.javascript.GeneratedClassLoader`.
|
||||
*/
|
||||
class RhinoDefineClassMethod extends Method {
|
||||
RhinoDefineClassMethod() {
|
||||
this.getDeclaringType()
|
||||
.getASupertype*()
|
||||
.hasQualifiedName("org.mozilla.javascript", "GeneratedClassLoader") and
|
||||
this.hasName("defineClass")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `ma` is a call to a `ScriptEngineMethod` and `sink` is an argument that
|
||||
* will be executed.
|
||||
*/
|
||||
predicate isScriptArgument(MethodAccess ma, Expr sink) {
|
||||
exists(ScriptEngineMethod m |
|
||||
m = ma.getMethod() and
|
||||
if m.getDeclaringType().getASupertype*().hasQualifiedName("javax.script", "ScriptEngineFactory")
|
||||
then sink = ma.getArgument(_) // all arguments allow script injection
|
||||
else sink = ma.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a Rhino expression evaluation method is vulnerable to code injection.
|
||||
*/
|
||||
predicate evaluatesRhinoExpression(MethodAccess ma, Expr sink) {
|
||||
exists(RhinoEvaluateExpressionMethod m | m = ma.getMethod() |
|
||||
(
|
||||
if ma.getMethod().getName() = "compileReader"
|
||||
then sink = ma.getArgument(0) // The first argument is the input reader
|
||||
else sink = ma.getArgument(1) // The second argument is the JavaScript or Java input
|
||||
) and
|
||||
not exists(MethodAccess ca |
|
||||
ca.getMethod().hasName(["initSafeStandardObjects", "setClassShutter"]) and // safe mode or `ClassShutter` constraint is enforced
|
||||
ma.getQualifier() = ca.getQualifier().(VarAccess).getVariable().getAnAccess()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a Rhino expression compilation method is vulnerable to code injection.
|
||||
*/
|
||||
predicate compilesScript(MethodAccess ma, Expr sink) {
|
||||
exists(RhinoCompileClassMethod m | m = ma.getMethod() | sink = ma.getArgument(0))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a Rhino class loading method is vulnerable to code injection.
|
||||
*/
|
||||
predicate definesRhinoClass(MethodAccess ma, Expr sink) {
|
||||
exists(RhinoDefineClassMethod m | m = ma.getMethod() | sink = ma.getArgument(1))
|
||||
}
|
||||
|
||||
/** A script injection sink. */
|
||||
class ScriptInjectionSink extends DataFlow::ExprNode {
|
||||
MethodAccess methodAccess;
|
||||
|
||||
ScriptInjectionSink() {
|
||||
isScriptArgument(methodAccess, this.getExpr()) or
|
||||
evaluatesRhinoExpression(methodAccess, this.getExpr()) or
|
||||
compilesScript(methodAccess, this.getExpr()) or
|
||||
definesRhinoClass(methodAccess, this.getExpr())
|
||||
}
|
||||
|
||||
/** An access to the method associated with this sink. */
|
||||
MethodAccess getMethodAccess() { result = methodAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint tracking configuration that tracks flow from `RemoteFlowSource` to an argument
|
||||
* of a method call that executes injected script.
|
||||
*/
|
||||
class ScriptInjectionConfiguration extends TaintTracking::Configuration {
|
||||
ScriptInjectionConfiguration() { this = "ScriptInjectionConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof ScriptInjectionSink }
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, ScriptInjectionConfiguration conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink.getNode().(ScriptInjectionSink).getMethodAccess(), source, sink,
|
||||
"Java Script Engine evaluate $@.", source.getNode(), "user input"
|
||||
@@ -0,0 +1,46 @@
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
@Controller
|
||||
public class SpringUrlRedirect {
|
||||
|
||||
private final static String VALID_REDIRECT = "http://127.0.0.1";
|
||||
|
||||
@GetMapping("url1")
|
||||
public RedirectView bad1(String redirectUrl, HttpServletResponse response) throws Exception {
|
||||
RedirectView rv = new RedirectView();
|
||||
rv.setUrl(redirectUrl);
|
||||
return rv;
|
||||
}
|
||||
|
||||
@GetMapping("url2")
|
||||
public String bad2(String redirectUrl) {
|
||||
String url = "redirect:" + redirectUrl;
|
||||
return url;
|
||||
}
|
||||
|
||||
@GetMapping("url3")
|
||||
public RedirectView bad3(String redirectUrl) {
|
||||
RedirectView rv = new RedirectView(redirectUrl);
|
||||
return rv;
|
||||
}
|
||||
|
||||
@GetMapping("url4")
|
||||
public ModelAndView bad4(String redirectUrl) {
|
||||
return new ModelAndView("redirect:" + redirectUrl);
|
||||
}
|
||||
|
||||
@GetMapping("url5")
|
||||
public RedirectView good1(String redirectUrl) {
|
||||
RedirectView rv = new RedirectView();
|
||||
if (redirectUrl.startsWith(VALID_REDIRECT)){
|
||||
rv.setUrl(redirectUrl);
|
||||
}else {
|
||||
rv.setUrl(VALID_REDIRECT);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>Directly incorporating user input into a URL redirect request without validating the input
|
||||
can facilitate phishing attacks. In these attacks, unsuspecting users can be redirected to a
|
||||
malicious site that looks very similar to the real site they intend to visit, but which is
|
||||
controlled by the attacker.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>To guard against untrusted URL redirection, it is advisable to avoid putting user input
|
||||
directly into a redirect URL. Instead, maintain a list of authorized
|
||||
redirects on the server; then choose from that list based on the user input provided.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>The following examples show the bad case and the good case respectively.
|
||||
In <code>bad1</code> method and <code>bad2</code> method and <code>bad3</code> method and
|
||||
<code>bad4</code> method, shows an HTTP request parameter being used directly in a URL redirect
|
||||
without validating the input, which facilitates phishing attacks. In <code>good1</code> method,
|
||||
shows how to solve this problem by verifying whether the user input is a known fixed string beginning.
|
||||
</p>
|
||||
|
||||
<sample src="SpringUrlRedirect.java" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
<li>A Guide To Spring Redirects: <a href="https://www.baeldung.com/spring-redirect-and-forward">Spring Redirects</a>.</li>
|
||||
<li>Url redirection - attack and defense: <a href="https://www.virtuesecurity.com/kb/url-redirection-attack-and-defense/">Url Redirection</a>.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* @name Spring url redirection from remote source
|
||||
* @description Spring url redirection based on unvalidated user-input
|
||||
* may cause redirection to malicious web sites.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id java/spring-unvalidated-url-redirection
|
||||
* @tags security
|
||||
* external/cwe-601
|
||||
*/
|
||||
|
||||
import java
|
||||
import SpringUrlRedirect
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import DataFlow::PathGraph
|
||||
|
||||
private class StartsWithSanitizer extends DataFlow::BarrierGuard {
|
||||
StartsWithSanitizer() {
|
||||
this.(MethodAccess).getMethod().hasName("startsWith") and
|
||||
this.(MethodAccess).getMethod().getDeclaringType() instanceof TypeString and
|
||||
this.(MethodAccess).getMethod().getNumberOfParameters() = 1
|
||||
}
|
||||
|
||||
override predicate checks(Expr e, boolean branch) {
|
||||
e = this.(MethodAccess).getQualifier() and branch = true
|
||||
}
|
||||
}
|
||||
|
||||
class SpringUrlRedirectFlowConfig extends TaintTracking::Configuration {
|
||||
SpringUrlRedirectFlowConfig() { this = "SpringUrlRedirectFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof SpringUrlRedirectSink }
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof StartsWithSanitizer
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
// Exclude the case where the left side of the concatenated string is not `redirect:`.
|
||||
// E.g: `String url = "/path?token=" + request.getParameter("token");`
|
||||
// Note this is quite a broad sanitizer (it will also sanitize the right-hand side of `url = "http://" + request.getParameter("token")`);
|
||||
// Consider making this stricter in future.
|
||||
exists(AddExpr ae |
|
||||
ae.getRightOperand() = node.asExpr() and
|
||||
not ae instanceof RedirectBuilderExpr
|
||||
)
|
||||
or
|
||||
exists(MethodAccess ma, int index |
|
||||
ma.getMethod().hasName("format") and
|
||||
ma.getMethod().getDeclaringType() instanceof TypeString and
|
||||
ma.getArgument(index) = node.asExpr() and
|
||||
(
|
||||
index != 0 and
|
||||
not ma.getArgument(0).(CompileTimeConstantExpr).getStringValue().regexpMatch("^%s.*")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, SpringUrlRedirectFlowConfig conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Potentially untrusted URL redirection due to $@.",
|
||||
source.getNode(), "user-provided value"
|
||||
@@ -0,0 +1,73 @@
|
||||
import java
|
||||
import DataFlow
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.dataflow.DataFlow2
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.frameworks.spring.SpringController
|
||||
|
||||
/**
|
||||
* A concatenate expression using the string `redirect:` or `ajaxredirect:` or `forward:` on the left.
|
||||
*
|
||||
* E.g: `"redirect:" + redirectUrl`
|
||||
*/
|
||||
class RedirectBuilderExpr extends AddExpr {
|
||||
RedirectBuilderExpr() {
|
||||
this.getLeftOperand().(CompileTimeConstantExpr).getStringValue() in [
|
||||
"redirect:", "ajaxredirect:", "forward:"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `StringBuilder.append` or `StringBuffer.append` method, and the parameter value is
|
||||
* `"redirect:"` or `"ajaxredirect:"` or `"forward:"`.
|
||||
*
|
||||
* E.g: `StringBuilder.append("redirect:")`
|
||||
*/
|
||||
class RedirectAppendCall extends MethodAccess {
|
||||
RedirectAppendCall() {
|
||||
this.getMethod().hasName("append") and
|
||||
this.getMethod().getDeclaringType() instanceof StringBuildingType and
|
||||
this.getArgument(0).(CompileTimeConstantExpr).getStringValue() in [
|
||||
"redirect:", "ajaxredirect:", "forward:"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/** A URL redirection sink from spring controller method. */
|
||||
class SpringUrlRedirectSink extends DataFlow::Node {
|
||||
SpringUrlRedirectSink() {
|
||||
exists(RedirectBuilderExpr rbe |
|
||||
rbe.getRightOperand() = this.asExpr() and
|
||||
any(SpringRequestMappingMethod sqmm).polyCalls*(this.getEnclosingCallable())
|
||||
)
|
||||
or
|
||||
exists(MethodAccess ma, RedirectAppendCall rac |
|
||||
DataFlow2::localExprFlow(rac.getQualifier(), ma.getQualifier()) and
|
||||
ma.getMethod().hasName("append") and
|
||||
ma.getArgument(0) = this.asExpr() and
|
||||
any(SpringRequestMappingMethod sqmm).polyCalls*(this.getEnclosingCallable())
|
||||
)
|
||||
or
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod().hasName("setUrl") and
|
||||
ma.getMethod()
|
||||
.getDeclaringType()
|
||||
.hasQualifiedName("org.springframework.web.servlet.view", "AbstractUrlBasedView") and
|
||||
ma.getArgument(0) = this.asExpr()
|
||||
)
|
||||
or
|
||||
exists(ClassInstanceExpr cie |
|
||||
cie.getConstructedType()
|
||||
.hasQualifiedName("org.springframework.web.servlet.view", "RedirectView") and
|
||||
cie.getArgument(0) = this.asExpr()
|
||||
)
|
||||
or
|
||||
exists(ClassInstanceExpr cie |
|
||||
cie.getConstructedType().hasQualifiedName("org.springframework.web.servlet", "ModelAndView") and
|
||||
exists(RedirectBuilderExpr rbe |
|
||||
rbe = cie.getArgument(0) and rbe.getRightOperand() = this.asExpr()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -77,6 +77,7 @@ private module Frameworks {
|
||||
private import semmle.code.java.frameworks.ApacheHttp
|
||||
private import semmle.code.java.frameworks.apache.Lang
|
||||
private import semmle.code.java.frameworks.guava.Guava
|
||||
private import semmle.code.java.frameworks.jackson.JacksonSerializability
|
||||
private import semmle.code.java.security.ResponseSplitting
|
||||
private import semmle.code.java.security.XSS
|
||||
private import semmle.code.java.security.LdapInjection
|
||||
|
||||
@@ -9,6 +9,7 @@ import semmle.code.java.Reflection
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.DataFlow5
|
||||
import semmle.code.java.dataflow.FlowSteps
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
/**
|
||||
* A `@com.fasterxml.jackson.annotation.JsonIgnore` annoation.
|
||||
@@ -28,7 +29,7 @@ abstract class JacksonSerializableType extends Type { }
|
||||
* A method used for serializing objects using Jackson. The final parameter is the object to be
|
||||
* serialized.
|
||||
*/
|
||||
library class JacksonWriteValueMethod extends Method, TaintPreservingCallable {
|
||||
private class JacksonWriteValueMethod extends Method, TaintPreservingCallable {
|
||||
JacksonWriteValueMethod() {
|
||||
(
|
||||
getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectWriter") or
|
||||
@@ -50,8 +51,20 @@ library class JacksonWriteValueMethod extends Method, TaintPreservingCallable {
|
||||
}
|
||||
}
|
||||
|
||||
private class JacksonReadValueMethod extends Method, TaintPreservingCallable {
|
||||
JacksonReadValueMethod() {
|
||||
(
|
||||
getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectReader") or
|
||||
getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectMapper")
|
||||
) and
|
||||
hasName(["readValue", "readValues"])
|
||||
}
|
||||
|
||||
override predicate returnsTaintFrom(int arg) { arg = 0 }
|
||||
}
|
||||
|
||||
/** A type whose values are explicitly serialized in a call to a Jackson method. */
|
||||
library class ExplicitlyWrittenJacksonSerializableType extends JacksonSerializableType {
|
||||
private class ExplicitlyWrittenJacksonSerializableType extends JacksonSerializableType {
|
||||
ExplicitlyWrittenJacksonSerializableType() {
|
||||
exists(MethodAccess ma |
|
||||
// A call to a Jackson write method...
|
||||
@@ -63,7 +76,7 @@ library class ExplicitlyWrittenJacksonSerializableType extends JacksonSerializab
|
||||
}
|
||||
|
||||
/** A type used in a `JacksonSerializableField` declaration. */
|
||||
library class FieldReferencedJacksonSerializableType extends JacksonSerializableType {
|
||||
private class FieldReferencedJacksonSerializableType extends JacksonSerializableType {
|
||||
FieldReferencedJacksonSerializableType() {
|
||||
exists(JacksonSerializableField f | usesType(f.getType(), this))
|
||||
}
|
||||
@@ -96,17 +109,24 @@ private class TypeLiteralToJacksonDatabindFlowConfiguration extends DataFlow5::C
|
||||
}
|
||||
|
||||
/** A type whose values are explicitly deserialized in a call to a Jackson method. */
|
||||
library class ExplicitlyReadJacksonDeserializableType extends JacksonDeserializableType {
|
||||
private class ExplicitlyReadJacksonDeserializableType extends JacksonDeserializableType {
|
||||
ExplicitlyReadJacksonDeserializableType() {
|
||||
exists(TypeLiteralToJacksonDatabindFlowConfiguration conf |
|
||||
usesType(conf.getSourceWithFlowToJacksonDatabind().getTypeName().getType(), this)
|
||||
)
|
||||
or
|
||||
exists(MethodAccess ma |
|
||||
// A call to a Jackson read method...
|
||||
ma.getMethod() instanceof JacksonReadValueMethod and
|
||||
// ...where `this` is used in the final argument, indicating that this type will be deserialized.
|
||||
usesType(ma.getArgument(ma.getNumArgument() - 1).getType(), this)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A type used in a `JacksonDeserializableField` declaration. */
|
||||
library class FieldReferencedJacksonDeSerializableType extends JacksonDeserializableType {
|
||||
FieldReferencedJacksonDeSerializableType() {
|
||||
private class FieldReferencedJacksonDeserializableType extends JacksonDeserializableType {
|
||||
FieldReferencedJacksonDeserializableType() {
|
||||
exists(JacksonDeserializableField f | usesType(f.getType(), this))
|
||||
}
|
||||
}
|
||||
@@ -135,6 +155,21 @@ class JacksonDeserializableField extends DeserializableField {
|
||||
}
|
||||
}
|
||||
|
||||
/** A call to a field that may be deserialized using the Jackson JSON framework. */
|
||||
private class JacksonDeserializableFieldAccess extends FieldAccess {
|
||||
JacksonDeserializableFieldAccess() { getField() instanceof JacksonDeserializableField }
|
||||
}
|
||||
|
||||
/**
|
||||
* When an object is deserialized by the Jackson JSON framework using a tainted input source,
|
||||
* the fields that the framework deserialized are themselves tainted input data.
|
||||
*/
|
||||
private class JacksonDeserializedTaintStep extends AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
DataFlow::getFieldQualifier(node2.asExpr().(JacksonDeserializableFieldAccess)) = node1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `addMixInAnnotations` or `addMixIn` Jackson method.
|
||||
*
|
||||
@@ -239,3 +274,13 @@ class JacksonMixedInCallable extends Callable {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class JacksonModel extends SummaryModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
"com.fasterxml.jackson.databind;ObjectMapper;true;valueToTree;;;Argument[0];ReturnValue;taint",
|
||||
"com.fasterxml.jackson.databind;ObjectMapper;true;convertValue;;;Argument[0];ReturnValue;taint"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
edges
|
||||
| JythonInjection.java:28:23:28:50 | getParameter(...) : String | JythonInjection.java:36:30:36:33 | code |
|
||||
| JythonInjection.java:53:23:53:50 | getParameter(...) : String | JythonInjection.java:58:44:58:47 | code |
|
||||
| JythonInjection.java:73:23:73:50 | getParameter(...) : String | JythonInjection.java:81:35:81:38 | code |
|
||||
| JythonInjection.java:97:23:97:50 | getParameter(...) : String | JythonInjection.java:106:61:106:75 | getBytes(...) |
|
||||
nodes
|
||||
| JythonInjection.java:28:23:28:50 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| JythonInjection.java:36:30:36:33 | code | semmle.label | code |
|
||||
| JythonInjection.java:53:23:53:50 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| JythonInjection.java:58:44:58:47 | code | semmle.label | code |
|
||||
| JythonInjection.java:73:23:73:50 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| JythonInjection.java:81:35:81:38 | code | semmle.label | code |
|
||||
| JythonInjection.java:97:23:97:50 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| JythonInjection.java:106:61:106:75 | getBytes(...) | semmle.label | getBytes(...) |
|
||||
| JythonInjection.java:131:40:131:63 | getInputStream(...) | semmle.label | getInputStream(...) |
|
||||
#select
|
||||
| JythonInjection.java:36:13:36:34 | exec(...) | JythonInjection.java:28:23:28:50 | getParameter(...) : String | JythonInjection.java:36:30:36:33 | code | Jython evaluate $@. | JythonInjection.java:28:23:28:50 | getParameter(...) | user input |
|
||||
| JythonInjection.java:58:27:58:48 | eval(...) | JythonInjection.java:53:23:53:50 | getParameter(...) : String | JythonInjection.java:58:44:58:47 | code | Jython evaluate $@. | JythonInjection.java:53:23:53:50 | getParameter(...) | user input |
|
||||
| JythonInjection.java:81:13:81:39 | runsource(...) | JythonInjection.java:73:23:73:50 | getParameter(...) : String | JythonInjection.java:81:35:81:38 | code | Jython evaluate $@. | JythonInjection.java:73:23:73:50 | getParameter(...) | user input |
|
||||
| JythonInjection.java:106:29:106:134 | makeCode(...) | JythonInjection.java:97:23:97:50 | getParameter(...) : String | JythonInjection.java:106:61:106:75 | getBytes(...) | Jython evaluate $@. | JythonInjection.java:97:23:97:50 | getParameter(...) | user input |
|
||||
| JythonInjection.java:131:29:131:109 | compile(...) | JythonInjection.java:131:40:131:63 | getInputStream(...) | JythonInjection.java:131:40:131:63 | getInputStream(...) | Jython evaluate $@. | JythonInjection.java:131:40:131:63 | getInputStream(...) | user input |
|
||||
@@ -0,0 +1,144 @@
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.python.core.BytecodeLoader;
|
||||
import org.python.core.Py;
|
||||
import org.python.core.PyCode;
|
||||
import org.python.core.PyException;
|
||||
import org.python.core.PyObject;
|
||||
import org.python.util.InteractiveInterpreter;
|
||||
import org.python.util.PythonInterpreter;
|
||||
|
||||
public class JythonInjection extends HttpServlet {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public JythonInjection() {
|
||||
super();
|
||||
}
|
||||
|
||||
// BAD: allow execution of arbitrary Python code
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
response.setContentType("text/plain");
|
||||
String code = request.getParameter("code");
|
||||
PythonInterpreter interpreter = null;
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
|
||||
try {
|
||||
interpreter = new PythonInterpreter();
|
||||
interpreter.setOut(out);
|
||||
interpreter.setErr(out);
|
||||
interpreter.exec(code);
|
||||
out.flush();
|
||||
|
||||
response.getWriter().print(out.toString());
|
||||
} catch(PyException ex) {
|
||||
response.getWriter().println(ex.getMessage());
|
||||
} finally {
|
||||
if (interpreter != null) {
|
||||
interpreter.close();
|
||||
}
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
// BAD: allow execution of arbitrary Python code
|
||||
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
response.setContentType("text/plain");
|
||||
String code = request.getParameter("code");
|
||||
PythonInterpreter interpreter = null;
|
||||
|
||||
try {
|
||||
interpreter = new PythonInterpreter();
|
||||
PyObject py = interpreter.eval(code);
|
||||
|
||||
response.getWriter().print(py.toString());
|
||||
} catch(PyException ex) {
|
||||
response.getWriter().println(ex.getMessage());
|
||||
} finally {
|
||||
if (interpreter != null) {
|
||||
interpreter.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BAD: allow arbitrary Jython expression to run
|
||||
protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
response.setContentType("text/plain");
|
||||
String code = request.getParameter("code");
|
||||
InteractiveInterpreter interpreter = null;
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
|
||||
try {
|
||||
interpreter = new InteractiveInterpreter();
|
||||
interpreter.setOut(out);
|
||||
interpreter.setErr(out);
|
||||
interpreter.runsource(code);
|
||||
out.flush();
|
||||
|
||||
response.getWriter().print(out.toString());
|
||||
} catch(PyException ex) {
|
||||
response.getWriter().println(ex.getMessage());
|
||||
} finally {
|
||||
if (interpreter != null) {
|
||||
interpreter.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BAD: load arbitrary class file to execute
|
||||
protected void doTrace(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
response.setContentType("text/plain");
|
||||
String code = request.getParameter("code");
|
||||
PythonInterpreter interpreter = null;
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
|
||||
try {
|
||||
interpreter = new PythonInterpreter();
|
||||
interpreter.setOut(out);
|
||||
interpreter.setErr(out);
|
||||
|
||||
PyCode pyCode = BytecodeLoader.makeCode("test", code.getBytes(), getServletContext().getRealPath("/com/example/test.pyc"));
|
||||
interpreter.exec(pyCode);
|
||||
out.flush();
|
||||
|
||||
response.getWriter().print(out.toString());
|
||||
} catch(PyException ex) {
|
||||
response.getWriter().println(ex.getMessage());
|
||||
} finally {
|
||||
if (interpreter != null) {
|
||||
interpreter.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BAD: Compile Python code to execute
|
||||
protected void doHead(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
response.setContentType("text/plain");
|
||||
PythonInterpreter interpreter = null;
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
|
||||
try {
|
||||
interpreter = new PythonInterpreter();
|
||||
interpreter.setOut(out);
|
||||
interpreter.setErr(out);
|
||||
|
||||
PyCode pyCode = Py.compile(request.getInputStream(), "Test.py", org.python.core.CompileMode.eval);
|
||||
interpreter.exec(pyCode);
|
||||
out.flush();
|
||||
|
||||
response.getWriter().print(out.toString());
|
||||
} catch(PyException ex) {
|
||||
response.getWriter().println(ex.getMessage());
|
||||
} finally {
|
||||
if (interpreter != null) {
|
||||
interpreter.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-094/JythonInjection.ql
|
||||
@@ -0,0 +1,91 @@
|
||||
import java.io.IOException;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.mozilla.javascript.ClassShutter;
|
||||
import org.mozilla.javascript.CompilerEnvirons;
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.DefiningClassLoader;
|
||||
import org.mozilla.javascript.Scriptable;
|
||||
import org.mozilla.javascript.RhinoException;
|
||||
import org.mozilla.javascript.optimizer.ClassCompiler;
|
||||
|
||||
/**
|
||||
* Servlet implementation class RhinoServlet
|
||||
*/
|
||||
public class RhinoServlet extends HttpServlet {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public RhinoServlet() {
|
||||
super();
|
||||
}
|
||||
|
||||
// BAD: allow arbitrary Java and JavaScript code to be executed
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
response.setContentType("text/plain");
|
||||
String code = request.getParameter("code");
|
||||
Context ctx = Context.enter();
|
||||
try {
|
||||
Scriptable scope = ctx.initStandardObjects();
|
||||
Object result = ctx.evaluateString(scope, code, "<code>", 1, null);
|
||||
response.getWriter().print(Context.toString(result));
|
||||
} catch(RhinoException ex) {
|
||||
response.getWriter().println(ex.getMessage());
|
||||
} finally {
|
||||
Context.exit();
|
||||
}
|
||||
}
|
||||
|
||||
// GOOD: enable the safe mode
|
||||
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
response.setContentType("text/plain");
|
||||
String code = request.getParameter("code");
|
||||
Context ctx = Context.enter();
|
||||
try {
|
||||
Scriptable scope = ctx.initSafeStandardObjects();
|
||||
Object result = ctx.evaluateString(scope, code, "<code>", 1, null);
|
||||
response.getWriter().print(Context.toString(result));
|
||||
} catch(RhinoException ex) {
|
||||
response.getWriter().println(ex.getMessage());
|
||||
} finally {
|
||||
Context.exit();
|
||||
}
|
||||
}
|
||||
|
||||
// GOOD: enforce a constraint on allowed classes
|
||||
protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
response.setContentType("text/plain");
|
||||
String code = request.getParameter("code");
|
||||
Context ctx = Context.enter();
|
||||
try {
|
||||
Scriptable scope = ctx.initStandardObjects();
|
||||
ctx.setClassShutter(new ClassShutter() {
|
||||
public boolean visibleToScripts(String className) {
|
||||
return className.startsWith("com.example.");
|
||||
}
|
||||
});
|
||||
|
||||
Object result = ctx.evaluateString(scope, code, "<code>", 1, null);
|
||||
response.getWriter().print(Context.toString(result));
|
||||
} catch(RhinoException ex) {
|
||||
response.getWriter().println(ex.getMessage());
|
||||
} finally {
|
||||
Context.exit();
|
||||
}
|
||||
}
|
||||
|
||||
// BAD: allow arbitrary code to be compiled for subsequent execution
|
||||
protected void doGet2(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
String code = request.getParameter("code");
|
||||
ClassCompiler compiler = new ClassCompiler(new CompilerEnvirons());
|
||||
Object[] objs = compiler.compileToClassFiles(code, "/sourceLocation", 1, "mainClassName");
|
||||
}
|
||||
|
||||
// BAD: allow arbitrary code to be loaded for subsequent execution
|
||||
protected void doPost2(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
String code = request.getParameter("code");
|
||||
Class clazz = new DefiningClassLoader().defineClass("Powerfunc", code.getBytes());
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
edges
|
||||
| ScriptEngineTest.java:8:44:8:55 | input : String | ScriptEngineTest.java:12:37:12:41 | input |
|
||||
| ScriptEngineTest.java:15:51:15:62 | input : String | ScriptEngineTest.java:19:31:19:35 | input |
|
||||
| ScriptEngineTest.java:23:58:23:69 | input : String | ScriptEngineTest.java:27:31:27:35 | input |
|
||||
| ScriptEngineTest.java:30:46:30:57 | input : String | ScriptEngineTest.java:34:31:34:35 | input |
|
||||
| ScriptEngineTest.java:37:26:37:38 | args : String[] | ScriptEngineTest.java:38:56:38:62 | ...[...] : String |
|
||||
| ScriptEngineTest.java:37:26:37:38 | args : String[] | ScriptEngineTest.java:39:63:39:69 | ...[...] : String |
|
||||
| ScriptEngineTest.java:37:26:37:38 | args : String[] | ScriptEngineTest.java:40:70:40:76 | ...[...] : String |
|
||||
| ScriptEngineTest.java:37:26:37:38 | args : String[] | ScriptEngineTest.java:41:58:41:64 | ...[...] : String |
|
||||
| ScriptEngineTest.java:38:56:38:62 | ...[...] : String | ScriptEngineTest.java:8:44:8:55 | input : String |
|
||||
| ScriptEngineTest.java:39:63:39:69 | ...[...] : String | ScriptEngineTest.java:15:51:15:62 | input : String |
|
||||
| ScriptEngineTest.java:40:70:40:76 | ...[...] : String | ScriptEngineTest.java:23:58:23:69 | input : String |
|
||||
| ScriptEngineTest.java:41:58:41:64 | ...[...] : String | ScriptEngineTest.java:30:46:30:57 | input : String |
|
||||
nodes
|
||||
| ScriptEngineTest.java:8:44:8:55 | input : String | semmle.label | input : String |
|
||||
| ScriptEngineTest.java:12:37:12:41 | input | semmle.label | input |
|
||||
| ScriptEngineTest.java:15:51:15:62 | input : String | semmle.label | input : String |
|
||||
| ScriptEngineTest.java:19:31:19:35 | input | semmle.label | input |
|
||||
| ScriptEngineTest.java:23:58:23:69 | input : String | semmle.label | input : String |
|
||||
| ScriptEngineTest.java:27:31:27:35 | input | semmle.label | input |
|
||||
| ScriptEngineTest.java:30:46:30:57 | input : String | semmle.label | input : String |
|
||||
| ScriptEngineTest.java:34:31:34:35 | input | semmle.label | input |
|
||||
| ScriptEngineTest.java:37:26:37:38 | args : String[] | semmle.label | args : String[] |
|
||||
| ScriptEngineTest.java:38:56:38:62 | ...[...] : String | semmle.label | ...[...] : String |
|
||||
| ScriptEngineTest.java:39:63:39:69 | ...[...] : String | semmle.label | ...[...] : String |
|
||||
| ScriptEngineTest.java:40:70:40:76 | ...[...] : String | semmle.label | ...[...] : String |
|
||||
| ScriptEngineTest.java:41:58:41:64 | ...[...] : String | semmle.label | ...[...] : String |
|
||||
#select
|
||||
| ScriptEngineTest.java:12:19:12:42 | eval(...) | ScriptEngineTest.java:37:26:37:38 | args : String[] | ScriptEngineTest.java:12:37:12:41 | input | ScriptEngine eval $@. | ScriptEngineTest.java:37:26:37:38 | args | user input |
|
||||
| ScriptEngineTest.java:19:19:19:36 | eval(...) | ScriptEngineTest.java:37:26:37:38 | args : String[] | ScriptEngineTest.java:19:31:19:35 | input | ScriptEngine eval $@. | ScriptEngineTest.java:37:26:37:38 | args | user input |
|
||||
| ScriptEngineTest.java:27:19:27:36 | eval(...) | ScriptEngineTest.java:37:26:37:38 | args : String[] | ScriptEngineTest.java:27:31:27:35 | input | ScriptEngine eval $@. | ScriptEngineTest.java:37:26:37:38 | args | user input |
|
||||
| ScriptEngineTest.java:34:19:34:36 | eval(...) | ScriptEngineTest.java:37:26:37:38 | args : String[] | ScriptEngineTest.java:34:31:34:35 | input | ScriptEngine eval $@. | ScriptEngineTest.java:37:26:37:38 | args | user input |
|
||||
@@ -1 +0,0 @@
|
||||
experimental/Security/CWE/CWE-094/ScriptEngine.ql
|
||||
@@ -1,9 +1,21 @@
|
||||
import javax.script.AbstractScriptEngine;
|
||||
import javax.script.Compilable;
|
||||
import javax.script.CompiledScript;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import javax.script.ScriptEngineFactory;
|
||||
import javax.script.ScriptException;
|
||||
|
||||
import jdk.nashorn.api.scripting.NashornScriptEngine;
|
||||
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
|
||||
import javax.script.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
public class ScriptEngineTest {
|
||||
public class ScriptEngineTest extends HttpServlet {
|
||||
|
||||
public void testWithScriptEngineReference(String input) throws ScriptException {
|
||||
ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
|
||||
@@ -33,26 +45,59 @@ public class ScriptEngineTest {
|
||||
MyCustomScriptEngine engine = (MyCustomScriptEngine) factory.getScriptEngine(new String[] { "-scripting" });
|
||||
Object result = engine.eval(input);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws ScriptException {
|
||||
new ScriptEngineTest().testWithScriptEngineReference(args[0]);
|
||||
new ScriptEngineTest().testNashornWithScriptEngineReference(args[0]);
|
||||
new ScriptEngineTest().testNashornWithNashornScriptEngineReference(args[0]);
|
||||
new ScriptEngineTest().testCustomScriptEngineReference(args[0]);
|
||||
|
||||
public void testScriptEngineCompilable(String input) throws ScriptException {
|
||||
NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
|
||||
Compilable engine = (Compilable) factory.getScriptEngine(new String[] { "-scripting" });
|
||||
CompiledScript script = engine.compile(input);
|
||||
Object result = script.eval();
|
||||
}
|
||||
|
||||
|
||||
public void testScriptEngineGetProgram(String input) throws ScriptException {
|
||||
ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
|
||||
ScriptEngine engine = scriptEngineManager.getEngineByName("nashorn");
|
||||
String program = engine.getFactory().getProgram(input);
|
||||
Object result = engine.eval(program);
|
||||
}
|
||||
|
||||
private static class MyCustomScriptEngine extends AbstractScriptEngine {
|
||||
public Object eval(String var1) throws ScriptException {
|
||||
return null;
|
||||
}
|
||||
public Object eval(String var1) throws ScriptException { return null; }
|
||||
|
||||
@Override
|
||||
public ScriptEngineFactory getFactory() { return null; }
|
||||
}
|
||||
|
||||
private static class MyCustomFactory implements ScriptEngineFactory {
|
||||
public MyCustomFactory() {
|
||||
}
|
||||
|
||||
public ScriptEngine getScriptEngine() { return null; }
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScriptEngine getScriptEngine() { return null; }
|
||||
|
||||
public ScriptEngine getScriptEngine(String... args) { return null; }
|
||||
|
||||
@Override
|
||||
public String getEngineName() { return null; }
|
||||
|
||||
@Override
|
||||
public String getMethodCallSyntax(final String obj, final String method, final String... args) { return null; }
|
||||
|
||||
@Override
|
||||
public String getProgram(final String... statements) { return null; }
|
||||
}
|
||||
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
try {
|
||||
String code = request.getParameter("code");
|
||||
|
||||
new ScriptEngineTest().testWithScriptEngineReference(code);
|
||||
new ScriptEngineTest().testNashornWithScriptEngineReference(code);
|
||||
new ScriptEngineTest().testNashornWithNashornScriptEngineReference(code);
|
||||
new ScriptEngineTest().testCustomScriptEngineReference(code);
|
||||
new ScriptEngineTest().testScriptEngineCompilable(code);
|
||||
new ScriptEngineTest().testScriptEngineGetProgram(code);
|
||||
} catch (ScriptException se) {
|
||||
throw new IOException(se.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
edges
|
||||
| RhinoServlet.java:28:23:28:50 | getParameter(...) : String | RhinoServlet.java:32:55:32:58 | code |
|
||||
| RhinoServlet.java:81:23:81:50 | getParameter(...) : String | RhinoServlet.java:83:54:83:57 | code |
|
||||
| RhinoServlet.java:88:23:88:50 | getParameter(...) : String | RhinoServlet.java:89:74:89:88 | getBytes(...) |
|
||||
| ScriptEngineTest.java:20:44:20:55 | input : String | ScriptEngineTest.java:24:37:24:41 | input |
|
||||
| ScriptEngineTest.java:27:51:27:62 | input : String | ScriptEngineTest.java:31:31:31:35 | input |
|
||||
| ScriptEngineTest.java:35:58:35:69 | input : String | ScriptEngineTest.java:39:31:39:35 | input |
|
||||
| ScriptEngineTest.java:42:46:42:57 | input : String | ScriptEngineTest.java:46:31:46:35 | input |
|
||||
| ScriptEngineTest.java:49:41:49:52 | input : String | ScriptEngineTest.java:52:42:52:46 | input |
|
||||
| ScriptEngineTest.java:56:41:56:52 | input : String | ScriptEngineTest.java:59:51:59:55 | input |
|
||||
| ScriptEngineTest.java:91:18:91:45 | getParameter(...) : String | ScriptEngineTest.java:93:57:93:60 | code : String |
|
||||
| ScriptEngineTest.java:91:18:91:45 | getParameter(...) : String | ScriptEngineTest.java:94:64:94:67 | code : String |
|
||||
| ScriptEngineTest.java:91:18:91:45 | getParameter(...) : String | ScriptEngineTest.java:95:71:95:74 | code : String |
|
||||
| ScriptEngineTest.java:91:18:91:45 | getParameter(...) : String | ScriptEngineTest.java:96:59:96:62 | code : String |
|
||||
| ScriptEngineTest.java:91:18:91:45 | getParameter(...) : String | ScriptEngineTest.java:97:54:97:57 | code : String |
|
||||
| ScriptEngineTest.java:91:18:91:45 | getParameter(...) : String | ScriptEngineTest.java:98:54:98:57 | code : String |
|
||||
| ScriptEngineTest.java:93:57:93:60 | code : String | ScriptEngineTest.java:20:44:20:55 | input : String |
|
||||
| ScriptEngineTest.java:94:64:94:67 | code : String | ScriptEngineTest.java:27:51:27:62 | input : String |
|
||||
| ScriptEngineTest.java:95:71:95:74 | code : String | ScriptEngineTest.java:35:58:35:69 | input : String |
|
||||
| ScriptEngineTest.java:96:59:96:62 | code : String | ScriptEngineTest.java:42:46:42:57 | input : String |
|
||||
| ScriptEngineTest.java:97:54:97:57 | code : String | ScriptEngineTest.java:49:41:49:52 | input : String |
|
||||
| ScriptEngineTest.java:98:54:98:57 | code : String | ScriptEngineTest.java:56:41:56:52 | input : String |
|
||||
nodes
|
||||
| RhinoServlet.java:28:23:28:50 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| RhinoServlet.java:32:55:32:58 | code | semmle.label | code |
|
||||
| RhinoServlet.java:81:23:81:50 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| RhinoServlet.java:83:54:83:57 | code | semmle.label | code |
|
||||
| RhinoServlet.java:88:23:88:50 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| RhinoServlet.java:89:74:89:88 | getBytes(...) | semmle.label | getBytes(...) |
|
||||
| ScriptEngineTest.java:20:44:20:55 | input : String | semmle.label | input : String |
|
||||
| ScriptEngineTest.java:24:37:24:41 | input | semmle.label | input |
|
||||
| ScriptEngineTest.java:27:51:27:62 | input : String | semmle.label | input : String |
|
||||
| ScriptEngineTest.java:31:31:31:35 | input | semmle.label | input |
|
||||
| ScriptEngineTest.java:35:58:35:69 | input : String | semmle.label | input : String |
|
||||
| ScriptEngineTest.java:39:31:39:35 | input | semmle.label | input |
|
||||
| ScriptEngineTest.java:42:46:42:57 | input : String | semmle.label | input : String |
|
||||
| ScriptEngineTest.java:46:31:46:35 | input | semmle.label | input |
|
||||
| ScriptEngineTest.java:49:41:49:52 | input : String | semmle.label | input : String |
|
||||
| ScriptEngineTest.java:52:42:52:46 | input | semmle.label | input |
|
||||
| ScriptEngineTest.java:56:41:56:52 | input : String | semmle.label | input : String |
|
||||
| ScriptEngineTest.java:59:51:59:55 | input | semmle.label | input |
|
||||
| ScriptEngineTest.java:91:18:91:45 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| ScriptEngineTest.java:93:57:93:60 | code : String | semmle.label | code : String |
|
||||
| ScriptEngineTest.java:94:64:94:67 | code : String | semmle.label | code : String |
|
||||
| ScriptEngineTest.java:95:71:95:74 | code : String | semmle.label | code : String |
|
||||
| ScriptEngineTest.java:96:59:96:62 | code : String | semmle.label | code : String |
|
||||
| ScriptEngineTest.java:97:54:97:57 | code : String | semmle.label | code : String |
|
||||
| ScriptEngineTest.java:98:54:98:57 | code : String | semmle.label | code : String |
|
||||
#select
|
||||
| RhinoServlet.java:32:29:32:78 | evaluateString(...) | RhinoServlet.java:28:23:28:50 | getParameter(...) : String | RhinoServlet.java:32:55:32:58 | code | Java Script Engine evaluate $@. | RhinoServlet.java:28:23:28:50 | getParameter(...) | user input |
|
||||
| RhinoServlet.java:83:25:83:97 | compileToClassFiles(...) | RhinoServlet.java:81:23:81:50 | getParameter(...) : String | RhinoServlet.java:83:54:83:57 | code | Java Script Engine evaluate $@. | RhinoServlet.java:81:23:81:50 | getParameter(...) | user input |
|
||||
| RhinoServlet.java:89:23:89:89 | defineClass(...) | RhinoServlet.java:88:23:88:50 | getParameter(...) : String | RhinoServlet.java:89:74:89:88 | getBytes(...) | Java Script Engine evaluate $@. | RhinoServlet.java:88:23:88:50 | getParameter(...) | user input |
|
||||
| ScriptEngineTest.java:24:19:24:42 | eval(...) | ScriptEngineTest.java:91:18:91:45 | getParameter(...) : String | ScriptEngineTest.java:24:37:24:41 | input | Java Script Engine evaluate $@. | ScriptEngineTest.java:91:18:91:45 | getParameter(...) | user input |
|
||||
| ScriptEngineTest.java:31:19:31:36 | eval(...) | ScriptEngineTest.java:91:18:91:45 | getParameter(...) : String | ScriptEngineTest.java:31:31:31:35 | input | Java Script Engine evaluate $@. | ScriptEngineTest.java:91:18:91:45 | getParameter(...) | user input |
|
||||
| ScriptEngineTest.java:39:19:39:36 | eval(...) | ScriptEngineTest.java:91:18:91:45 | getParameter(...) : String | ScriptEngineTest.java:39:31:39:35 | input | Java Script Engine evaluate $@. | ScriptEngineTest.java:91:18:91:45 | getParameter(...) | user input |
|
||||
| ScriptEngineTest.java:46:19:46:36 | eval(...) | ScriptEngineTest.java:91:18:91:45 | getParameter(...) : String | ScriptEngineTest.java:46:31:46:35 | input | Java Script Engine evaluate $@. | ScriptEngineTest.java:91:18:91:45 | getParameter(...) | user input |
|
||||
| ScriptEngineTest.java:52:27:52:47 | compile(...) | ScriptEngineTest.java:91:18:91:45 | getParameter(...) : String | ScriptEngineTest.java:52:42:52:46 | input | Java Script Engine evaluate $@. | ScriptEngineTest.java:91:18:91:45 | getParameter(...) | user input |
|
||||
| ScriptEngineTest.java:59:20:59:56 | getProgram(...) | ScriptEngineTest.java:91:18:91:45 | getParameter(...) : String | ScriptEngineTest.java:59:51:59:55 | input | Java Script Engine evaluate $@. | ScriptEngineTest.java:91:18:91:45 | getParameter(...) | user input |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-094/ScriptInjection.ql
|
||||
@@ -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:${testdir}/../../../../stubs/java-ee-el:${testdir}/../../../../stubs/juel-2.2:${testdir}/../../../stubs/groovy-all-3.0.7:${testdir}/../../../../stubs/servlet-api-2.4
|
||||
//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:${testdir}/../../../../stubs/jython-2.7.2:${testdir}/../../../../experimental/stubs/rhino-1.7.13
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
edges
|
||||
| SpringUrlRedirect.java:13:30:13:47 | redirectUrl : String | SpringUrlRedirect.java:15:19:15:29 | redirectUrl |
|
||||
| SpringUrlRedirect.java:20:24:20:41 | redirectUrl : String | SpringUrlRedirect.java:21:36:21:46 | redirectUrl |
|
||||
| SpringUrlRedirect.java:26:30:26:47 | redirectUrl : String | SpringUrlRedirect.java:27:44:27:54 | redirectUrl |
|
||||
| SpringUrlRedirect.java:32:30:32:47 | redirectUrl : String | SpringUrlRedirect.java:33:47:33:57 | redirectUrl |
|
||||
| SpringUrlRedirect.java:37:24:37:41 | redirectUrl : String | SpringUrlRedirect.java:40:29:40:39 | redirectUrl |
|
||||
| SpringUrlRedirect.java:45:24:45:41 | redirectUrl : String | SpringUrlRedirect.java:48:30:48:40 | redirectUrl |
|
||||
| SpringUrlRedirect.java:53:24:53:41 | redirectUrl : String | SpringUrlRedirect.java:54:30:54:66 | format(...) |
|
||||
| SpringUrlRedirect.java:58:24:58:41 | redirectUrl : String | SpringUrlRedirect.java:59:30:59:76 | format(...) |
|
||||
nodes
|
||||
| SpringUrlRedirect.java:13:30:13:47 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:15:19:15:29 | redirectUrl | semmle.label | redirectUrl |
|
||||
| SpringUrlRedirect.java:20:24:20:41 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:21:36:21:46 | redirectUrl | semmle.label | redirectUrl |
|
||||
| SpringUrlRedirect.java:26:30:26:47 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:27:44:27:54 | redirectUrl | semmle.label | redirectUrl |
|
||||
| SpringUrlRedirect.java:32:30:32:47 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:33:47:33:57 | redirectUrl | semmle.label | redirectUrl |
|
||||
| SpringUrlRedirect.java:37:24:37:41 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:40:29:40:39 | redirectUrl | semmle.label | redirectUrl |
|
||||
| SpringUrlRedirect.java:45:24:45:41 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:48:30:48:40 | redirectUrl | semmle.label | redirectUrl |
|
||||
| SpringUrlRedirect.java:53:24:53:41 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:54:30:54:66 | format(...) | semmle.label | format(...) |
|
||||
| SpringUrlRedirect.java:58:24:58:41 | redirectUrl : String | semmle.label | redirectUrl : String |
|
||||
| SpringUrlRedirect.java:59:30:59:76 | format(...) | semmle.label | format(...) |
|
||||
#select
|
||||
| SpringUrlRedirect.java:15:19:15:29 | redirectUrl | SpringUrlRedirect.java:13:30:13:47 | redirectUrl : String | SpringUrlRedirect.java:15:19:15:29 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:13:30:13:47 | redirectUrl | user-provided value |
|
||||
| SpringUrlRedirect.java:21:36:21:46 | redirectUrl | SpringUrlRedirect.java:20:24:20:41 | redirectUrl : String | SpringUrlRedirect.java:21:36:21:46 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:20:24:20:41 | redirectUrl | user-provided value |
|
||||
| SpringUrlRedirect.java:27:44:27:54 | redirectUrl | SpringUrlRedirect.java:26:30:26:47 | redirectUrl : String | SpringUrlRedirect.java:27:44:27:54 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:26:30:26:47 | redirectUrl | user-provided value |
|
||||
| SpringUrlRedirect.java:33:47:33:57 | redirectUrl | SpringUrlRedirect.java:32:30:32:47 | redirectUrl : String | SpringUrlRedirect.java:33:47:33:57 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:32:30:32:47 | redirectUrl | user-provided value |
|
||||
| SpringUrlRedirect.java:40:29:40:39 | redirectUrl | SpringUrlRedirect.java:37:24:37:41 | redirectUrl : String | SpringUrlRedirect.java:40:29:40:39 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:37:24:37:41 | redirectUrl | user-provided value |
|
||||
| SpringUrlRedirect.java:48:30:48:40 | redirectUrl | SpringUrlRedirect.java:45:24:45:41 | redirectUrl : String | SpringUrlRedirect.java:48:30:48:40 | redirectUrl | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:45:24:45:41 | redirectUrl | user-provided value |
|
||||
| SpringUrlRedirect.java:54:30:54:66 | format(...) | SpringUrlRedirect.java:53:24:53:41 | redirectUrl : String | SpringUrlRedirect.java:54:30:54:66 | format(...) | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:53:24:53:41 | redirectUrl | user-provided value |
|
||||
| SpringUrlRedirect.java:59:30:59:76 | format(...) | SpringUrlRedirect.java:58:24:58:41 | redirectUrl : String | SpringUrlRedirect.java:59:30:59:76 | format(...) | Potentially untrusted URL redirection due to $@. | SpringUrlRedirect.java:58:24:58:41 | redirectUrl | user-provided value |
|
||||
@@ -0,0 +1,83 @@
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
@Controller
|
||||
public class SpringUrlRedirect {
|
||||
|
||||
private final static String VALID_REDIRECT = "http://127.0.0.1";
|
||||
|
||||
@GetMapping("url1")
|
||||
public RedirectView bad1(String redirectUrl, HttpServletResponse response) throws Exception {
|
||||
RedirectView rv = new RedirectView();
|
||||
rv.setUrl(redirectUrl);
|
||||
return rv;
|
||||
}
|
||||
|
||||
@GetMapping("url2")
|
||||
public String bad2(String redirectUrl) {
|
||||
String url = "redirect:" + redirectUrl;
|
||||
return url;
|
||||
}
|
||||
|
||||
@GetMapping("url3")
|
||||
public RedirectView bad3(String redirectUrl) {
|
||||
RedirectView rv = new RedirectView(redirectUrl);
|
||||
return rv;
|
||||
}
|
||||
|
||||
@GetMapping("url4")
|
||||
public ModelAndView bad4(String redirectUrl) {
|
||||
return new ModelAndView("redirect:" + redirectUrl);
|
||||
}
|
||||
|
||||
@GetMapping("url5")
|
||||
public String bad5(String redirectUrl) {
|
||||
StringBuffer stringBuffer = new StringBuffer();
|
||||
stringBuffer.append("redirect:");
|
||||
stringBuffer.append(redirectUrl);
|
||||
return stringBuffer.toString();
|
||||
}
|
||||
|
||||
@GetMapping("url6")
|
||||
public String bad6(String redirectUrl) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.append("redirect:");
|
||||
stringBuilder.append(redirectUrl);
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
@GetMapping("url7")
|
||||
public String bad7(String redirectUrl) {
|
||||
return "redirect:" + String.format("%s/?aaa", redirectUrl);
|
||||
}
|
||||
|
||||
@GetMapping("url8")
|
||||
public String bad8(String redirectUrl, String token) {
|
||||
return "redirect:" + String.format(redirectUrl + "?token=%s", token);
|
||||
}
|
||||
|
||||
@GetMapping("url9")
|
||||
public RedirectView good1(String redirectUrl) {
|
||||
RedirectView rv = new RedirectView();
|
||||
if (redirectUrl.startsWith(VALID_REDIRECT)){
|
||||
rv.setUrl(redirectUrl);
|
||||
}else {
|
||||
rv.setUrl(VALID_REDIRECT);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
@GetMapping("url10")
|
||||
public ModelAndView good2(String token) {
|
||||
String url = "/edit?token=" + token;
|
||||
return new ModelAndView("redirect:" + url);
|
||||
}
|
||||
|
||||
@GetMapping("url11")
|
||||
public String good3(String status) {
|
||||
return "redirect:" + String.format("/stories/search/criteria?status=%s", status);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-601/SpringUrlRedirect.ql
|
||||
@@ -0,0 +1 @@
|
||||
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/springframework-5.2.3/
|
||||
@@ -0,0 +1,56 @@
|
||||
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// API class
|
||||
|
||||
package org.mozilla.javascript;
|
||||
|
||||
/**
|
||||
Embeddings that wish to filter Java classes that are visible to scripts
|
||||
through the LiveConnect, should implement this interface.
|
||||
|
||||
@see Context#setClassShutter(ClassShutter)
|
||||
@since 1.5 Release 4
|
||||
@author Norris Boyd
|
||||
*/
|
||||
|
||||
public interface ClassShutter {
|
||||
|
||||
/**
|
||||
* Return true iff the Java class with the given name should be exposed
|
||||
* to scripts.
|
||||
* <p>
|
||||
* An embedding may filter which Java classes are exposed through
|
||||
* LiveConnect to JavaScript scripts.
|
||||
* <p>
|
||||
* Due to the fact that there is no package reflection in Java,
|
||||
* this method will also be called with package names. There
|
||||
* is no way for Rhino to tell if "Packages.a.b" is a package name
|
||||
* or a class that doesn't exist. What Rhino does is attempt
|
||||
* to load each segment of "Packages.a.b.c": It first attempts to
|
||||
* load class "a", then attempts to load class "a.b", then
|
||||
* finally attempts to load class "a.b.c". On a Rhino installation
|
||||
* without any ClassShutter set, and without any of the
|
||||
* above classes, the expression "Packages.a.b.c" will result in
|
||||
* a [JavaPackage a.b.c] and not an error.
|
||||
* <p>
|
||||
* With ClassShutter supplied, Rhino will first call
|
||||
* visibleToScripts before attempting to look up the class name. If
|
||||
* visibleToScripts returns false, the class name lookup is not
|
||||
* performed and subsequent Rhino execution assumes the class is
|
||||
* not present. So for "java.lang.System.out.println" the lookup
|
||||
* of "java.lang.System" is skipped and thus Rhino assumes that
|
||||
* "java.lang.System" doesn't exist. So then for "java.lang.System.out",
|
||||
* Rhino attempts to load the class "java.lang.System.out" because
|
||||
* it assumes that "java.lang.System" is a package name.
|
||||
* <p>
|
||||
* @param fullClassName the full name of the class (including the package
|
||||
* name, with '.' as a delimiter). For example the
|
||||
* standard string class is "java.lang.String"
|
||||
* @return whether or not to reveal this class to scripts
|
||||
*/
|
||||
public boolean visibleToScripts(String fullClassName);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.javascript;
|
||||
|
||||
public class CompilerEnvirons {
|
||||
public CompilerEnvirons() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,695 @@
|
||||
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// API class
|
||||
|
||||
package org.mozilla.javascript;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* This class represents the runtime context of an executing script.
|
||||
*
|
||||
* Before executing a script, an instance of Context must be created
|
||||
* and associated with the thread that will be executing the script.
|
||||
* The Context will be used to store information about the executing
|
||||
* of the script such as the call stack. Contexts are associated with
|
||||
* the current thread using the {@link #call(ContextAction)}
|
||||
* or {@link #enter()} methods.<p>
|
||||
*
|
||||
* Different forms of script execution are supported. Scripts may be
|
||||
* evaluated from the source directly, or first compiled and then later
|
||||
* executed. Interactive execution is also supported.<p>
|
||||
*
|
||||
* Some aspects of script execution, such as type conversions and
|
||||
* object creation, may be accessed directly through methods of
|
||||
* Context.
|
||||
*
|
||||
* @see Scriptable
|
||||
* @author Norris Boyd
|
||||
* @author Brendan Eich
|
||||
*/
|
||||
|
||||
public class Context
|
||||
implements Closeable
|
||||
{
|
||||
/**
|
||||
* Creates a new Context. The context will be associated with the {@link
|
||||
* ContextFactory#getGlobal() global context factory}.
|
||||
*
|
||||
* Note that the Context must be associated with a thread before
|
||||
* it can be used to execute a script.
|
||||
* @deprecated this constructor is deprecated because it creates a
|
||||
* dependency on a static singleton context factory. Use
|
||||
* {@link ContextFactory#enter()} or
|
||||
* {@link ContextFactory#call(ContextAction)} instead. If you subclass
|
||||
* this class, consider using {@link #Context(ContextFactory)} constructor
|
||||
* instead in the subclasses' constructors.
|
||||
*/
|
||||
@Deprecated
|
||||
public Context()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new context. Provided as a preferred super constructor for
|
||||
* subclasses in place of the deprecated default public constructor.
|
||||
* @param factory the context factory associated with this context (most
|
||||
* likely, the one that created the context). Can not be null. The context
|
||||
* features are inherited from the factory, and the context will also
|
||||
* otherwise use its factory's services.
|
||||
* @throws IllegalArgumentException if factory parameter is null.
|
||||
*/
|
||||
protected Context(ContextFactory factory)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current Context.
|
||||
*
|
||||
* The current Context is per-thread; this method looks up
|
||||
* the Context associated with the current thread. <p>
|
||||
*
|
||||
* @return the Context associated with the current thread, or
|
||||
* null if no context is associated with the current
|
||||
* thread.
|
||||
* @see ContextFactory#enterContext()
|
||||
* @see ContextFactory#call(ContextAction)
|
||||
*/
|
||||
public static Context getCurrentContext()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as calling {@link ContextFactory#enterContext()} on the global
|
||||
* ContextFactory instance.
|
||||
* @return a Context associated with the current thread
|
||||
* @see #getCurrentContext()
|
||||
* @see #exit()
|
||||
* @see #call(ContextAction)
|
||||
*/
|
||||
public static Context enter()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Context associated with the current thread, using
|
||||
* the given Context if need be.
|
||||
* <p>
|
||||
* The same as <code>enter()</code> except that <code>cx</code>
|
||||
* is associated with the current thread and returned if
|
||||
* the current thread has no associated context and <code>cx</code>
|
||||
* is not associated with any other thread.
|
||||
* @param cx a Context to associate with the thread if possible
|
||||
* @return a Context associated with the current thread
|
||||
* @deprecated use {@link ContextFactory#enterContext(Context)} instead as
|
||||
* this method relies on usage of a static singleton "global" ContextFactory.
|
||||
* @see ContextFactory#enterContext(Context)
|
||||
* @see ContextFactory#call(ContextAction)
|
||||
*/
|
||||
@Deprecated
|
||||
public static Context enter(Context cx)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
static final Context enter(Context cx, ContextFactory factory)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exit a block of code requiring a Context.
|
||||
*
|
||||
* Calling <code>exit()</code> will remove the association between
|
||||
* the current thread and a Context if the prior call to
|
||||
* {@link ContextFactory#enterContext()} on this thread newly associated a
|
||||
* Context with this thread. Once the current thread no longer has an
|
||||
* associated Context, it cannot be used to execute JavaScript until it is
|
||||
* again associated with a Context.
|
||||
* @see ContextFactory#enterContext()
|
||||
*/
|
||||
public static void exit()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@link ContextFactory} instance used to create this Context.
|
||||
*/
|
||||
public final ContextFactory getFactory()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this is a sealed Context. A sealed Context instance does not
|
||||
* allow to modify any of its properties and will throw an exception
|
||||
* on any such attempt.
|
||||
* @see #seal(Object sealKey)
|
||||
*/
|
||||
public final boolean isSealed()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Seal this Context object so any attempt to modify any of its properties
|
||||
* including calling {@link #enter()} and {@link #exit()} methods will
|
||||
* throw an exception.
|
||||
* <p>
|
||||
* If <code>sealKey</code> is not null, calling
|
||||
* {@link #unseal(Object sealKey)} with the same key unseals
|
||||
* the object. If <code>sealKey</code> is null, unsealing is no longer possible.
|
||||
*
|
||||
* @see #isSealed()
|
||||
* @see #unseal(Object)
|
||||
*/
|
||||
public final void seal(Object sealKey)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Unseal previously sealed Context object.
|
||||
* The <code>sealKey</code> argument should not be null and should match
|
||||
* <code>sealKey</code> suplied with the last call to
|
||||
* {@link #seal(Object)} or an exception will be thrown.
|
||||
*
|
||||
* @see #isSealed()
|
||||
* @see #seal(Object sealKey)
|
||||
*/
|
||||
public final void unseal(Object sealKey)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current language version.
|
||||
* <p>
|
||||
* The language version number affects JavaScript semantics as detailed
|
||||
* in the overview documentation.
|
||||
*
|
||||
* @return an integer that is one of VERSION_1_0, VERSION_1_1, etc.
|
||||
*/
|
||||
public final int getLanguageVersion()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the language version.
|
||||
*
|
||||
* <p>
|
||||
* Setting the language version will affect functions and scripts compiled
|
||||
* subsequently. See the overview documentation for version-specific
|
||||
* behavior.
|
||||
*
|
||||
* @param version the version as specified by VERSION_1_0, VERSION_1_1, etc.
|
||||
*/
|
||||
public void setLanguageVersion(int version)
|
||||
{
|
||||
}
|
||||
|
||||
public static boolean isValidLanguageVersion(int version)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void checkLanguageVersion(int version)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the implementation version.
|
||||
*
|
||||
* <p>
|
||||
* The implementation version is of the form
|
||||
* <pre>
|
||||
* "<i>name langVer</i> <code>release</code> <i>relNum date</i>"
|
||||
* </pre>
|
||||
* where <i>name</i> is the name of the product, <i>langVer</i> is
|
||||
* the language version, <i>relNum</i> is the release number, and
|
||||
* <i>date</i> is the release date for that specific
|
||||
* release in the form "yyyy mm dd".
|
||||
*
|
||||
* @return a string that encodes the product, language version, release
|
||||
* number, and date.
|
||||
*/
|
||||
public final String getImplementationVersion() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the standard objects.
|
||||
*
|
||||
* Creates instances of the standard objects and their constructors
|
||||
* (Object, String, Number, Date, etc.), setting up 'scope' to act
|
||||
* as a global object as in ECMA 15.1.<p>
|
||||
*
|
||||
* This method must be called to initialize a scope before scripts
|
||||
* can be evaluated in that scope.<p>
|
||||
*
|
||||
* This method does not affect the Context it is called upon.
|
||||
*
|
||||
* @return the initialized scope
|
||||
*/
|
||||
public final ScriptableObject initStandardObjects()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the standard objects, leaving out those that offer access directly
|
||||
* to Java classes. This sets up "scope" to have access to all the standard
|
||||
* JavaScript classes, but does not create global objects for any top-level
|
||||
* Java packages. In addition, the "Packages," "JavaAdapter," and
|
||||
* "JavaImporter" classes, and the "getClass" function, are not
|
||||
* initialized.
|
||||
*
|
||||
* The result of this function is a scope that may be safely used in a "sandbox"
|
||||
* environment where it is not desirable to give access to Java code from JavaScript.
|
||||
*
|
||||
* Creates instances of the standard objects and their constructors
|
||||
* (Object, String, Number, Date, etc.), setting up 'scope' to act
|
||||
* as a global object as in ECMA 15.1.<p>
|
||||
*
|
||||
* This method must be called to initialize a scope before scripts
|
||||
* can be evaluated in that scope.<p>
|
||||
*
|
||||
* This method does not affect the Context it is called upon.
|
||||
*
|
||||
* @return the initialized scope
|
||||
*/
|
||||
public final ScriptableObject initSafeStandardObjects()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the standard objects.
|
||||
*
|
||||
* Creates instances of the standard objects and their constructors
|
||||
* (Object, String, Number, Date, etc.), setting up 'scope' to act
|
||||
* as a global object as in ECMA 15.1.<p>
|
||||
*
|
||||
* This method must be called to initialize a scope before scripts
|
||||
* can be evaluated in that scope.<p>
|
||||
*
|
||||
* This method does not affect the Context it is called upon.
|
||||
*
|
||||
* @param scope the scope to initialize, or null, in which case a new
|
||||
* object will be created to serve as the scope
|
||||
* @return the initialized scope. The method returns the value of the scope
|
||||
* argument if it is not null or newly allocated scope object which
|
||||
* is an instance {@link ScriptableObject}.
|
||||
*/
|
||||
public final Scriptable initStandardObjects(ScriptableObject scope)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the standard objects, leaving out those that offer access directly
|
||||
* to Java classes. This sets up "scope" to have access to all the standard
|
||||
* JavaScript classes, but does not create global objects for any top-level
|
||||
* Java packages. In addition, the "Packages," "JavaAdapter," and
|
||||
* "JavaImporter" classes, and the "getClass" function, are not
|
||||
* initialized.
|
||||
*
|
||||
* The result of this function is a scope that may be safely used in a "sandbox"
|
||||
* environment where it is not desirable to give access to Java code from JavaScript.
|
||||
*
|
||||
* Creates instances of the standard objects and their constructors
|
||||
* (Object, String, Number, Date, etc.), setting up 'scope' to act
|
||||
* as a global object as in ECMA 15.1.<p>
|
||||
*
|
||||
* This method must be called to initialize a scope before scripts
|
||||
* can be evaluated in that scope.<p>
|
||||
*
|
||||
* This method does not affect the Context it is called upon.
|
||||
*
|
||||
* @param scope the scope to initialize, or null, in which case a new
|
||||
* object will be created to serve as the scope
|
||||
* @return the initialized scope. The method returns the value of the scope
|
||||
* argument if it is not null or newly allocated scope object which
|
||||
* is an instance {@link ScriptableObject}.
|
||||
*/
|
||||
public final Scriptable initSafeStandardObjects(ScriptableObject scope)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the standard objects.
|
||||
*
|
||||
* Creates instances of the standard objects and their constructors
|
||||
* (Object, String, Number, Date, etc.), setting up 'scope' to act
|
||||
* as a global object as in ECMA 15.1.<p>
|
||||
*
|
||||
* This method must be called to initialize a scope before scripts
|
||||
* can be evaluated in that scope.<p>
|
||||
*
|
||||
* This method does not affect the Context it is called upon.<p>
|
||||
*
|
||||
* This form of the method also allows for creating "sealed" standard
|
||||
* objects. An object that is sealed cannot have properties added, changed,
|
||||
* or removed. This is useful to create a "superglobal" that can be shared
|
||||
* among several top-level objects. Note that sealing is not allowed in
|
||||
* the current ECMA/ISO language specification, but is likely for
|
||||
* the next version.
|
||||
*
|
||||
* @param scope the scope to initialize, or null, in which case a new
|
||||
* object will be created to serve as the scope
|
||||
* @param sealed whether or not to create sealed standard objects that
|
||||
* cannot be modified.
|
||||
* @return the initialized scope. The method returns the value of the scope
|
||||
* argument if it is not null or newly allocated scope object.
|
||||
* @since 1.4R3
|
||||
*/
|
||||
public ScriptableObject initStandardObjects(ScriptableObject scope,
|
||||
boolean sealed)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the standard objects, leaving out those that offer access directly
|
||||
* to Java classes. This sets up "scope" to have access to all the standard
|
||||
* JavaScript classes, but does not create global objects for any top-level
|
||||
* Java packages. In addition, the "Packages," "JavaAdapter," and
|
||||
* "JavaImporter" classes, and the "getClass" function, are not
|
||||
* initialized.
|
||||
*
|
||||
* The result of this function is a scope that may be safely used in a "sandbox"
|
||||
* environment where it is not desirable to give access to Java code from JavaScript.
|
||||
*
|
||||
* Creates instances of the standard objects and their constructors
|
||||
* (Object, String, Number, Date, etc.), setting up 'scope' to act
|
||||
* as a global object as in ECMA 15.1.<p>
|
||||
*
|
||||
* This method must be called to initialize a scope before scripts
|
||||
* can be evaluated in that scope.<p>
|
||||
*
|
||||
* This method does not affect the Context it is called upon.<p>
|
||||
*
|
||||
* This form of the method also allows for creating "sealed" standard
|
||||
* objects. An object that is sealed cannot have properties added, changed,
|
||||
* or removed. This is useful to create a "superglobal" that can be shared
|
||||
* among several top-level objects. Note that sealing is not allowed in
|
||||
* the current ECMA/ISO language specification, but is likely for
|
||||
* the next version.
|
||||
*
|
||||
* @param scope the scope to initialize, or null, in which case a new
|
||||
* object will be created to serve as the scope
|
||||
* @param sealed whether or not to create sealed standard objects that
|
||||
* cannot be modified.
|
||||
* @return the initialized scope. The method returns the value of the scope
|
||||
* argument if it is not null or newly allocated scope object.
|
||||
* @since 1.7.6
|
||||
*/
|
||||
public ScriptableObject initSafeStandardObjects(ScriptableObject scope,
|
||||
boolean sealed)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the singleton object that represents the JavaScript Undefined value.
|
||||
*/
|
||||
public static Object getUndefinedValue()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate a JavaScript source string.
|
||||
*
|
||||
* The provided source name and line number are used for error messages
|
||||
* and for producing debug information.
|
||||
*
|
||||
* @param scope the scope to execute in
|
||||
* @param source the JavaScript source
|
||||
* @param sourceName a string describing the source, such as a filename
|
||||
* @param lineno the starting line number
|
||||
* @param securityDomain an arbitrary object that specifies security
|
||||
* information about the origin or owner of the script. For
|
||||
* implementations that don't care about security, this value
|
||||
* may be null.
|
||||
* @return the result of evaluating the string
|
||||
* @see org.mozilla.javascript.SecurityController
|
||||
*/
|
||||
public final Object evaluateString(Scriptable scope, String source,
|
||||
String sourceName, int lineno,
|
||||
Object securityDomain)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate a reader as JavaScript source.
|
||||
*
|
||||
* All characters of the reader are consumed.
|
||||
*
|
||||
* @param scope the scope to execute in
|
||||
* @param in the Reader to get JavaScript source from
|
||||
* @param sourceName a string describing the source, such as a filename
|
||||
* @param lineno the starting line number
|
||||
* @param securityDomain an arbitrary object that specifies security
|
||||
* information about the origin or owner of the script. For
|
||||
* implementations that don't care about security, this value
|
||||
* may be null.
|
||||
* @return the result of evaluating the source
|
||||
*
|
||||
* @exception IOException if an IOException was generated by the Reader
|
||||
*/
|
||||
public final Object evaluateReader(Scriptable scope, Reader in,
|
||||
String sourceName, int lineno,
|
||||
Object securityDomain)
|
||||
throws IOException
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @see #compileReader(Reader in, String sourceName, int lineno, Object securityDomain)
|
||||
*/
|
||||
@Deprecated
|
||||
public final Script compileReader(
|
||||
Scriptable scope, Reader in, String sourceName, int lineno, Object securityDomain)
|
||||
throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the source in the given reader.
|
||||
*
|
||||
* <p>Returns a script that may later be executed. Will consume all the source in the reader.
|
||||
*
|
||||
* @param in the input reader
|
||||
* @param sourceName a string describing the source, such as a filename
|
||||
* @param lineno the starting line number for reporting errors
|
||||
* @param securityDomain an arbitrary object that specifies security information about the
|
||||
* origin or owner of the script. For implementations that don't care about security, this
|
||||
* value may be null.
|
||||
* @return a script that may later be executed
|
||||
* @exception IOException if an IOException was generated by the Reader
|
||||
* @see org.mozilla.javascript.Script
|
||||
*/
|
||||
public final Script compileReader(
|
||||
Reader in, String sourceName, int lineno, Object securityDomain) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the source in the given string.
|
||||
*
|
||||
* <p>Returns a script that may later be executed.
|
||||
*
|
||||
* @param source the source string
|
||||
* @param sourceName a string describing the source, such as a filename
|
||||
* @param lineno the starting line number for reporting errors. Use 0 if the line number is
|
||||
* unknown.
|
||||
* @param securityDomain an arbitrary object that specifies security information about the
|
||||
* origin or owner of the script. For implementations that don't care about security, this
|
||||
* value may be null.
|
||||
* @return a script that may later be executed
|
||||
* @see org.mozilla.javascript.Script
|
||||
*/
|
||||
public final Script compileString(
|
||||
String source, String sourceName, int lineno, Object securityDomain) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a JavaScript function.
|
||||
*
|
||||
* <p>The function source must be a function definition as defined by ECMA (e.g., "function f(a)
|
||||
* { return a; }").
|
||||
*
|
||||
* @param scope the scope to compile relative to
|
||||
* @param source the function definition source
|
||||
* @param sourceName a string describing the source, such as a filename
|
||||
* @param lineno the starting line number
|
||||
* @param securityDomain an arbitrary object that specifies security information about the
|
||||
* origin or owner of the script. For implementations that don't care about security, this
|
||||
* value may be null.
|
||||
* @return a Function that may later be called
|
||||
* @see org.mozilla.javascript.Function
|
||||
*/
|
||||
public final Function compileFunction(
|
||||
Scriptable scope, String source, String sourceName, int lineno, Object securityDomain) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the value to a JavaScript boolean value.
|
||||
* <p>
|
||||
* See ECMA 9.2.
|
||||
*
|
||||
* @param value a JavaScript value
|
||||
* @return the corresponding boolean value converted using
|
||||
* the ECMA rules
|
||||
*/
|
||||
public static boolean toBoolean(Object value)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the value to a JavaScript Number value.
|
||||
* <p>
|
||||
* Returns a Java double for the JavaScript Number.
|
||||
* <p>
|
||||
* See ECMA 9.3.
|
||||
*
|
||||
* @param value a JavaScript value
|
||||
* @return the corresponding double value converted using
|
||||
* the ECMA rules
|
||||
*/
|
||||
public static double toNumber(Object value)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the value to a JavaScript String value.
|
||||
* <p>
|
||||
* See ECMA 9.8.
|
||||
* <p>
|
||||
* @param value a JavaScript value
|
||||
* @return the corresponding String value converted using
|
||||
* the ECMA rules
|
||||
*/
|
||||
public static String toString(Object value)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the value to an JavaScript object value.
|
||||
* <p>
|
||||
* Note that a scope must be provided to look up the constructors
|
||||
* for Number, Boolean, and String.
|
||||
* <p>
|
||||
* See ECMA 9.9.
|
||||
* <p>
|
||||
* Additionally, arbitrary Java objects and classes will be
|
||||
* wrapped in a Scriptable object with its Java fields and methods
|
||||
* reflected as JavaScript properties of the object.
|
||||
*
|
||||
* @param value any Java object
|
||||
* @param scope global scope containing constructors for Number,
|
||||
* Boolean, and String
|
||||
* @return new JavaScript object
|
||||
*/
|
||||
public static Scriptable toObject(Object value, Scriptable scope)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient method to convert java value to its closest representation
|
||||
* in JavaScript.
|
||||
* <p>
|
||||
* If value is an instance of String, Number, Boolean, Function or
|
||||
* Scriptable, it is returned as it and will be treated as the corresponding
|
||||
* JavaScript type of string, number, boolean, function and object.
|
||||
* <p>
|
||||
* Note that for Number instances during any arithmetic operation in
|
||||
* JavaScript the engine will always use the result of
|
||||
* <code>Number.doubleValue()</code> resulting in a precision loss if
|
||||
* the number can not fit into double.
|
||||
* <p>
|
||||
* If value is an instance of Character, it will be converted to string of
|
||||
* length 1 and its JavaScript type will be string.
|
||||
* <p>
|
||||
* The rest of values will be wrapped as LiveConnect objects
|
||||
* by calling {@link WrapFactory#wrap(Context cx, Scriptable scope,
|
||||
* Object obj, Class staticType)} as in:
|
||||
* <pre>
|
||||
* Context cx = Context.getCurrentContext();
|
||||
* return cx.getWrapFactory().wrap(cx, scope, value, null);
|
||||
* </pre>
|
||||
*
|
||||
* @param value any Java object
|
||||
* @param scope top scope object
|
||||
* @return value suitable to pass to any API that takes JavaScript values.
|
||||
*/
|
||||
public static Object javaToJS(Object value, Scriptable scope)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a JavaScript value into the desired type.
|
||||
* Uses the semantics defined with LiveConnect3 and throws an
|
||||
* Illegal argument exception if the conversion cannot be performed.
|
||||
* @param value the JavaScript value to convert
|
||||
* @param desiredType the Java type to convert to. Primitive Java
|
||||
* types are represented using the TYPE fields in the corresponding
|
||||
* wrapper class in java.lang.
|
||||
* @return the converted value
|
||||
* @throws EvaluatorException if the conversion cannot be performed
|
||||
*/
|
||||
public static Object jsToJava(Object value, Class<?> desiredType)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the LiveConnect access filter for this context.
|
||||
* <p> {@link ClassShutter} may only be set if it is currently null.
|
||||
* Otherwise a SecurityException is thrown.
|
||||
* @param shutter a ClassShutter object
|
||||
* @throws SecurityException if there is already a ClassShutter
|
||||
* object for this Context
|
||||
*/
|
||||
public synchronized final void setClassShutter(ClassShutter shutter)
|
||||
{
|
||||
}
|
||||
|
||||
final synchronized ClassShutter getClassShutter()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public interface ClassShutterSetter {
|
||||
public void setClassShutter(ClassShutter shutter);
|
||||
public ClassShutter getClassShutter();
|
||||
}
|
||||
|
||||
public final synchronized ClassShutterSetter getClassShutterSetter() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,314 @@
|
||||
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// API class
|
||||
|
||||
package org.mozilla.javascript;
|
||||
|
||||
/**
|
||||
* Factory class that Rhino runtime uses to create new {@link Context}
|
||||
* instances. A <code>ContextFactory</code> can also notify listeners
|
||||
* about context creation and release.
|
||||
* <p>
|
||||
* When the Rhino runtime needs to create new {@link Context} instance during
|
||||
* execution of {@link Context#enter()} or {@link Context}, it will call
|
||||
* {@link #makeContext()} of the current global ContextFactory.
|
||||
* See {@link #getGlobal()} and {@link #initGlobal(ContextFactory)}.
|
||||
* <p>
|
||||
* It is also possible to use explicit ContextFactory instances for Context
|
||||
* creation. This is useful to have a set of independent Rhino runtime
|
||||
* instances under single JVM. See {@link #call(ContextAction)}.
|
||||
* <p>
|
||||
* The following example demonstrates Context customization to terminate
|
||||
* scripts running more then 10 seconds and to provide better compatibility
|
||||
* with JavaScript code using MSIE-specific features.
|
||||
* <pre>
|
||||
* import org.mozilla.javascript.*;
|
||||
*
|
||||
* class MyFactory extends ContextFactory
|
||||
* {
|
||||
*
|
||||
* // Custom {@link Context} to store execution time.
|
||||
* private static class MyContext extends Context
|
||||
* {
|
||||
* long startTime;
|
||||
* }
|
||||
*
|
||||
* static {
|
||||
* // Initialize GlobalFactory with custom factory
|
||||
* ContextFactory.initGlobal(new MyFactory());
|
||||
* }
|
||||
*
|
||||
* // Override {@link #makeContext()}
|
||||
* protected Context makeContext()
|
||||
* {
|
||||
* MyContext cx = new MyContext();
|
||||
* // Make Rhino runtime to call observeInstructionCount
|
||||
* // each 10000 bytecode instructions
|
||||
* cx.setInstructionObserverThreshold(10000);
|
||||
* return cx;
|
||||
* }
|
||||
*
|
||||
* // Override {@link #hasFeature(Context, int)}
|
||||
* public boolean hasFeature(Context cx, int featureIndex)
|
||||
* {
|
||||
* // Turn on maximum compatibility with MSIE scripts
|
||||
* switch (featureIndex) {
|
||||
* case {@link Context#FEATURE_NON_ECMA_GET_YEAR}:
|
||||
* return true;
|
||||
*
|
||||
* case {@link Context#FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME}:
|
||||
* return true;
|
||||
*
|
||||
* case {@link Context#FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER}:
|
||||
* return true;
|
||||
*
|
||||
* case {@link Context#FEATURE_PARENT_PROTO_PROPERTIES}:
|
||||
* return false;
|
||||
* }
|
||||
* return super.hasFeature(cx, featureIndex);
|
||||
* }
|
||||
*
|
||||
* // Override {@link #observeInstructionCount(Context, int)}
|
||||
* protected void observeInstructionCount(Context cx, int instructionCount)
|
||||
* {
|
||||
* MyContext mcx = (MyContext)cx;
|
||||
* long currentTime = System.currentTimeMillis();
|
||||
* if (currentTime - mcx.startTime > 10*1000) {
|
||||
* // More then 10 seconds from Context creation time:
|
||||
* // it is time to stop the script.
|
||||
* // Throw Error instance to ensure that script will never
|
||||
* // get control back through catch or finally.
|
||||
* throw new Error();
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* // Override {@link #doTopCall(Callable,
|
||||
Context, Scriptable,
|
||||
Scriptable, Object[])}
|
||||
* protected Object doTopCall(Callable callable,
|
||||
* Context cx, Scriptable scope,
|
||||
* Scriptable thisObj, Object[] args)
|
||||
* {
|
||||
* MyContext mcx = (MyContext)cx;
|
||||
* mcx.startTime = System.currentTimeMillis();
|
||||
*
|
||||
* return super.doTopCall(callable, cx, scope, thisObj, args);
|
||||
* }
|
||||
*
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
public class ContextFactory
|
||||
{
|
||||
|
||||
/**
|
||||
* Listener of {@link Context} creation and release events.
|
||||
*/
|
||||
public interface Listener
|
||||
{
|
||||
/**
|
||||
* Notify about newly created {@link Context} object.
|
||||
*/
|
||||
public void contextCreated(Context cx);
|
||||
|
||||
/**
|
||||
* Notify that the specified {@link Context} instance is no longer
|
||||
* associated with the current thread.
|
||||
*/
|
||||
public void contextReleased(Context cx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get global ContextFactory.
|
||||
*
|
||||
* @see #hasExplicitGlobal()
|
||||
* @see #initGlobal(ContextFactory)
|
||||
*/
|
||||
public static ContextFactory getGlobal()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if global factory was set.
|
||||
* Return true to indicate that {@link #initGlobal(ContextFactory)} was
|
||||
* already called and false to indicate that the global factory was not
|
||||
* explicitly set.
|
||||
*
|
||||
* @see #getGlobal()
|
||||
* @see #initGlobal(ContextFactory)
|
||||
*/
|
||||
public static boolean hasExplicitGlobal()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set global ContextFactory.
|
||||
* The method can only be called once.
|
||||
*
|
||||
* @see #getGlobal()
|
||||
* @see #hasExplicitGlobal()
|
||||
*/
|
||||
public synchronized static void initGlobal(ContextFactory factory)
|
||||
{
|
||||
}
|
||||
|
||||
public interface GlobalSetter {
|
||||
public void setContextFactoryGlobal(ContextFactory factory);
|
||||
public ContextFactory getContextFactoryGlobal();
|
||||
}
|
||||
|
||||
public synchronized static GlobalSetter getGlobalSetter() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link Context} instance to be associated with the current
|
||||
* thread.
|
||||
* This is a callback method used by Rhino to create {@link Context}
|
||||
* instance when it is necessary to associate one with the current
|
||||
* execution thread. <code>makeContext()</code> is allowed to call
|
||||
* {@link Context#seal(Object)} on the result to prevent
|
||||
* {@link Context} changes by hostile scripts or applets.
|
||||
*/
|
||||
protected Context makeContext()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of {@link Context#hasFeature(int featureIndex)}.
|
||||
* This can be used to customize {@link Context} without introducing
|
||||
* additional subclasses.
|
||||
*/
|
||||
protected boolean hasFeature(Context cx, int featureIndex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ClassLoader to use when searching for Java classes.
|
||||
* Unless it was explicitly initialized with
|
||||
* {@link #initApplicationClassLoader(ClassLoader)} the method returns
|
||||
* null to indicate that Thread.getContextClassLoader() should be used.
|
||||
*/
|
||||
public final ClassLoader getApplicationClassLoader()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set explicit class loader to use when searching for Java classes.
|
||||
*
|
||||
* @see #getApplicationClassLoader()
|
||||
*/
|
||||
public final void initApplicationClassLoader(ClassLoader loader)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this is a sealed ContextFactory.
|
||||
* @see #seal()
|
||||
*/
|
||||
public final boolean isSealed()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Seal this ContextFactory so any attempt to modify it like to add or
|
||||
* remove its listeners will throw an exception.
|
||||
* @see #isSealed()
|
||||
*/
|
||||
public final void seal()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a context associated with the current thread, creating one if need
|
||||
* be. The Context stores the execution state of the JavaScript engine, so
|
||||
* it is required that the context be entered before execution may begin.
|
||||
* Once a thread has entered a Context, then getCurrentContext() may be
|
||||
* called to find the context that is associated with the current thread.
|
||||
* <p>
|
||||
* Calling <code>enterContext()</code> will return either the Context
|
||||
* currently associated with the thread, or will create a new context and
|
||||
* associate it with the current thread. Each call to
|
||||
* <code>enterContext()</code> must have a matching call to
|
||||
* {@link Context#exit()}.
|
||||
* <pre>
|
||||
* Context cx = contextFactory.enterContext();
|
||||
* try {
|
||||
* ...
|
||||
* cx.evaluateString(...);
|
||||
* } finally {
|
||||
* Context.exit();
|
||||
* }
|
||||
* </pre>
|
||||
* Instead of using <code>enterContext()</code>, <code>exit()</code> pair consider
|
||||
* using {@link #call(ContextAction)} which guarantees proper association
|
||||
* of Context instances with the current thread.
|
||||
* With this method the above example becomes:
|
||||
* <pre>
|
||||
* ContextFactory.call(new ContextAction() {
|
||||
* public Object run(Context cx) {
|
||||
* ...
|
||||
* cx.evaluateString(...);
|
||||
* return null;
|
||||
* }
|
||||
* });
|
||||
* </pre>
|
||||
* @return a Context associated with the current thread
|
||||
* @see Context#getCurrentContext()
|
||||
* @see Context#exit()
|
||||
* @see #call(ContextAction)
|
||||
*/
|
||||
public Context enterContext()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #enterContext()} instead
|
||||
* @return a Context associated with the current thread
|
||||
*/
|
||||
@Deprecated
|
||||
public final Context enter()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link Context#exit()} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public final void exit()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Context associated with the current thread, using the given
|
||||
* Context if need be.
|
||||
* <p>
|
||||
* The same as <code>enterContext()</code> except that <code>cx</code>
|
||||
* is associated with the current thread and returned if the current thread
|
||||
* has no associated context and <code>cx</code> is not associated with any
|
||||
* other thread.
|
||||
* @param cx a Context to associate with the thread if possible
|
||||
* @return a Context associated with the current thread
|
||||
* @see #enterContext()
|
||||
* @see #call(ContextAction)
|
||||
* @throws IllegalStateException if <code>cx</code> is already associated
|
||||
* with a different thread
|
||||
*/
|
||||
public final Context enterContext(Context cx)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.javascript;
|
||||
|
||||
/**
|
||||
* Load generated classes.
|
||||
*
|
||||
* @author Norris Boyd
|
||||
*/
|
||||
public class DefiningClassLoader extends ClassLoader
|
||||
implements GeneratedClassLoader
|
||||
{
|
||||
public DefiningClassLoader() {
|
||||
}
|
||||
|
||||
public DefiningClassLoader(ClassLoader parentLoader) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> defineClass(String name, byte[] data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void linkClass(Class<?> cl) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> loadClass(String name, boolean resolve)
|
||||
throws ClassNotFoundException
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// API class
|
||||
|
||||
package org.mozilla.javascript;
|
||||
|
||||
/**
|
||||
* This is interface that all functions in JavaScript must implement. The interface provides for
|
||||
* calling functions and constructors.
|
||||
*
|
||||
* @see org.mozilla.javascript.Scriptable
|
||||
* @author Norris Boyd
|
||||
*/
|
||||
public interface Function extends Scriptable {
|
||||
/**
|
||||
* Call the function.
|
||||
*
|
||||
* <p>Note that the array of arguments is not guaranteed to have length greater than 0.
|
||||
*
|
||||
* @param cx the current Context for this thread
|
||||
* @param scope the scope to execute the function relative to. This is set to the value returned
|
||||
* by getParentScope() except when the function is called from a closure.
|
||||
* @param thisObj the JavaScript <code>this</code> object
|
||||
* @param args the array of arguments
|
||||
* @return the result of the call
|
||||
*/
|
||||
Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args);
|
||||
|
||||
/**
|
||||
* Call the function as a constructor.
|
||||
*
|
||||
* <p>This method is invoked by the runtime in order to satisfy a use of the JavaScript <code>
|
||||
* new</code> operator. This method is expected to create a new object and return it.
|
||||
*
|
||||
* @param cx the current Context for this thread
|
||||
* @param scope an enclosing scope of the caller except when the function is called from a
|
||||
* closure.
|
||||
* @param args the array of arguments
|
||||
* @return the allocated object
|
||||
*/
|
||||
Scriptable construct(Context cx, Scriptable scope, Object[] args);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// API class
|
||||
|
||||
package org.mozilla.javascript;
|
||||
|
||||
/**
|
||||
* Interface to define classes from generated byte code.
|
||||
*/
|
||||
public interface GeneratedClassLoader {
|
||||
|
||||
/**
|
||||
* Define a new Java class.
|
||||
* Classes created via this method should have the same class loader.
|
||||
*
|
||||
* @param name fully qualified class name
|
||||
* @param data class byte code
|
||||
* @return new class object
|
||||
*/
|
||||
public Class<?> defineClass(String name, byte[] data);
|
||||
|
||||
/**
|
||||
* Link the given class.
|
||||
*
|
||||
* @param cl Class instance returned from the previous call to
|
||||
* {@link #defineClass(String, byte[])}
|
||||
* @see java.lang.ClassLoader
|
||||
*/
|
||||
public void linkClass(Class<?> cl);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
|
||||
package org.mozilla.javascript;
|
||||
|
||||
/**
|
||||
* The class of exceptions thrown by the JavaScript engine.
|
||||
*/
|
||||
public abstract class RhinoException extends RuntimeException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// API class
|
||||
|
||||
package org.mozilla.javascript;
|
||||
|
||||
/**
|
||||
* All compiled scripts implement this interface.
|
||||
* <p>
|
||||
* This class encapsulates script execution relative to an
|
||||
* object scope.
|
||||
* @since 1.3
|
||||
* @author Norris Boyd
|
||||
*/
|
||||
|
||||
public interface Script {
|
||||
|
||||
/**
|
||||
* Execute the script.
|
||||
* <p>
|
||||
* The script is executed in a particular runtime Context, which
|
||||
* must be associated with the current thread.
|
||||
* The script is executed relative to a scope--definitions and
|
||||
* uses of global top-level variables and functions will access
|
||||
* properties of the scope object. For compliant ECMA
|
||||
* programs, the scope must be an object that has been initialized
|
||||
* as a global object using <code>Context.initStandardObjects</code>.
|
||||
* <p>
|
||||
*
|
||||
* @param cx the Context associated with the current thread
|
||||
* @param scope the scope to execute relative to
|
||||
* @return the result of executing the script
|
||||
* @see org.mozilla.javascript.Context#initStandardObjects()
|
||||
*/
|
||||
public Object exec(Context cx, Scriptable scope);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// API class
|
||||
|
||||
package org.mozilla.javascript;
|
||||
|
||||
/**
|
||||
* This is interface that all objects in JavaScript must implement.
|
||||
* The interface provides for the management of properties and for
|
||||
* performing conversions.
|
||||
* <p>
|
||||
* Host system implementors may find it easier to extend the ScriptableObject
|
||||
* class rather than implementing Scriptable when writing host objects.
|
||||
* <p>
|
||||
* There are many static methods defined in ScriptableObject that perform
|
||||
* the multiple calls to the Scriptable interface needed in order to
|
||||
* manipulate properties in prototype chains.
|
||||
* <p>
|
||||
*
|
||||
* @see org.mozilla.javascript.ScriptableObject
|
||||
* @author Norris Boyd
|
||||
* @author Nick Thompson
|
||||
* @author Brendan Eich
|
||||
*/
|
||||
|
||||
public interface Scriptable {
|
||||
|
||||
/**
|
||||
* Get the name of the set of objects implemented by this Java class.
|
||||
* This corresponds to the [[Class]] operation in ECMA and is used
|
||||
* by Object.prototype.toString() in ECMA.<p>
|
||||
* See ECMA 8.6.2 and 15.2.4.2.
|
||||
*/
|
||||
public String getClassName();
|
||||
|
||||
/**
|
||||
* Get a named property from the object.
|
||||
*
|
||||
* Looks property up in this object and returns the associated value
|
||||
* if found. Returns NOT_FOUND if not found.
|
||||
* Note that this method is not expected to traverse the prototype
|
||||
* chain. This is different from the ECMA [[Get]] operation.
|
||||
*
|
||||
* Depending on the property selector, the runtime will call
|
||||
* this method or the form of <code>get</code> that takes an
|
||||
* integer:
|
||||
* <table>
|
||||
* <tr><th>JavaScript code</th><th>Java code</th></tr>
|
||||
* <tr><td>a.b </td><td>a.get("b", a)</td></tr>
|
||||
* <tr><td>a["foo"] </td><td>a.get("foo", a)</td></tr>
|
||||
* <tr><td>a[3] </td><td>a.get(3, a)</td></tr>
|
||||
* <tr><td>a["3"] </td><td>a.get(3, a)</td></tr>
|
||||
* <tr><td>a[3.0] </td><td>a.get(3, a)</td></tr>
|
||||
* <tr><td>a["3.0"] </td><td>a.get("3.0", a)</td></tr>
|
||||
* <tr><td>a[1.1] </td><td>a.get("1.1", a)</td></tr>
|
||||
* <tr><td>a[-4] </td><td>a.get(-4, a)</td></tr>
|
||||
* </table>
|
||||
* <p>
|
||||
* The values that may be returned are limited to the following:
|
||||
* <UL>
|
||||
* <LI>java.lang.Boolean objects</LI>
|
||||
* <LI>java.lang.String objects</LI>
|
||||
* <LI>java.lang.Number objects</LI>
|
||||
* <LI>org.mozilla.javascript.Scriptable objects</LI>
|
||||
* <LI>null</LI>
|
||||
* <LI>The value returned by Context.getUndefinedValue()</LI>
|
||||
* <LI>NOT_FOUND</LI>
|
||||
* </UL>
|
||||
* @param name the name of the property
|
||||
* @param start the object in which the lookup began
|
||||
* @return the value of the property (may be null), or NOT_FOUND
|
||||
* @see org.mozilla.javascript.Context#getUndefinedValue
|
||||
*/
|
||||
public Object get(String name, Scriptable start);
|
||||
|
||||
/**
|
||||
* Get a property from the object selected by an integral index.
|
||||
*
|
||||
* Identical to <code>get(String, Scriptable)</code> except that
|
||||
* an integral index is used to select the property.
|
||||
*
|
||||
* @param index the numeric index for the property
|
||||
* @param start the object in which the lookup began
|
||||
* @return the value of the property (may be null), or NOT_FOUND
|
||||
* @see org.mozilla.javascript.Scriptable#get(String,Scriptable)
|
||||
*/
|
||||
public Object get(int index, Scriptable start);
|
||||
|
||||
/**
|
||||
* Indicates whether or not a named property is defined in an object.
|
||||
*
|
||||
* Does not traverse the prototype chain.<p>
|
||||
*
|
||||
* The property is specified by a String name
|
||||
* as defined for the <code>get</code> method.<p>
|
||||
*
|
||||
* @param name the name of the property
|
||||
* @param start the object in which the lookup began
|
||||
* @return true if and only if the named property is found in the object
|
||||
* @see org.mozilla.javascript.Scriptable#get(String, Scriptable)
|
||||
* @see org.mozilla.javascript.ScriptableObject#getProperty(Scriptable, String)
|
||||
*/
|
||||
public boolean has(String name, Scriptable start);
|
||||
|
||||
/**
|
||||
* Indicates whether or not an indexed property is defined in an object.
|
||||
*
|
||||
* Does not traverse the prototype chain.<p>
|
||||
*
|
||||
* The property is specified by an integral index
|
||||
* as defined for the <code>get</code> method.<p>
|
||||
*
|
||||
* @param index the numeric index for the property
|
||||
* @param start the object in which the lookup began
|
||||
* @return true if and only if the indexed property is found in the object
|
||||
* @see org.mozilla.javascript.Scriptable#get(int, Scriptable)
|
||||
* @see org.mozilla.javascript.ScriptableObject#getProperty(Scriptable, int)
|
||||
*/
|
||||
public boolean has(int index, Scriptable start);
|
||||
|
||||
/**
|
||||
* Sets a named property in this object.
|
||||
* <p>
|
||||
* The property is specified by a string name
|
||||
* as defined for <code>get</code>.
|
||||
* <p>
|
||||
* The possible values that may be passed in are as defined for
|
||||
* <code>get</code>. A class that implements this method may choose
|
||||
* to ignore calls to set certain properties, in which case those
|
||||
* properties are effectively read-only.<p>
|
||||
* For properties defined in a prototype chain,
|
||||
* use <code>putProperty</code> in ScriptableObject. <p>
|
||||
* Note that if a property <i>a</i> is defined in the prototype <i>p</i>
|
||||
* of an object <i>o</i>, then evaluating <code>o.a = 23</code> will cause
|
||||
* <code>set</code> to be called on the prototype <i>p</i> with
|
||||
* <i>o</i> as the <i>start</i> parameter.
|
||||
* To preserve JavaScript semantics, it is the Scriptable
|
||||
* object's responsibility to modify <i>o</i>. <p>
|
||||
* This design allows properties to be defined in prototypes and implemented
|
||||
* in terms of getters and setters of Java values without consuming slots
|
||||
* in each instance.
|
||||
* <p>
|
||||
* The values that may be set are limited to the following:
|
||||
* <UL>
|
||||
* <LI>java.lang.Boolean objects</LI>
|
||||
* <LI>java.lang.String objects</LI>
|
||||
* <LI>java.lang.Number objects</LI>
|
||||
* <LI>org.mozilla.javascript.Scriptable objects</LI>
|
||||
* <LI>null</LI>
|
||||
* <LI>The value returned by Context.getUndefinedValue()</LI>
|
||||
* </UL><p>
|
||||
* Arbitrary Java objects may be wrapped in a Scriptable by first calling
|
||||
* <code>Context.toObject</code>. This allows the property of a JavaScript
|
||||
* object to contain an arbitrary Java object as a value.<p>
|
||||
* Note that <code>has</code> will be called by the runtime first before
|
||||
* <code>set</code> is called to determine in which object the
|
||||
* property is defined.
|
||||
* Note that this method is not expected to traverse the prototype chain,
|
||||
* which is different from the ECMA [[Put]] operation.
|
||||
* @param name the name of the property
|
||||
* @param start the object whose property is being set
|
||||
* @param value value to set the property to
|
||||
* @see org.mozilla.javascript.Scriptable#has(String, Scriptable)
|
||||
* @see org.mozilla.javascript.Scriptable#get(String, Scriptable)
|
||||
* @see org.mozilla.javascript.ScriptableObject#putProperty(Scriptable, String, Object)
|
||||
* @see org.mozilla.javascript.Context#toObject(Object, Scriptable)
|
||||
*/
|
||||
public void put(String name, Scriptable start, Object value);
|
||||
|
||||
/**
|
||||
* Sets an indexed property in this object.
|
||||
* <p>
|
||||
* The property is specified by an integral index
|
||||
* as defined for <code>get</code>.<p>
|
||||
*
|
||||
* Identical to <code>put(String, Scriptable, Object)</code> except that
|
||||
* an integral index is used to select the property.
|
||||
*
|
||||
* @param index the numeric index for the property
|
||||
* @param start the object whose property is being set
|
||||
* @param value value to set the property to
|
||||
* @see org.mozilla.javascript.Scriptable#has(int, Scriptable)
|
||||
* @see org.mozilla.javascript.Scriptable#get(int, Scriptable)
|
||||
* @see org.mozilla.javascript.ScriptableObject#putProperty(Scriptable, int, Object)
|
||||
* @see org.mozilla.javascript.Context#toObject(Object, Scriptable)
|
||||
*/
|
||||
public void put(int index, Scriptable start, Object value);
|
||||
|
||||
/**
|
||||
* Removes a property from this object.
|
||||
* This operation corresponds to the ECMA [[Delete]] except that
|
||||
* the no result is returned. The runtime will guarantee that this
|
||||
* method is called only if the property exists. After this method
|
||||
* is called, the runtime will call Scriptable.has to see if the
|
||||
* property has been removed in order to determine the boolean
|
||||
* result of the delete operator as defined by ECMA 11.4.1.
|
||||
* <p>
|
||||
* A property can be made permanent by ignoring calls to remove
|
||||
* it.<p>
|
||||
* The property is specified by a String name
|
||||
* as defined for <code>get</code>.
|
||||
* <p>
|
||||
* To delete properties defined in a prototype chain,
|
||||
* see deleteProperty in ScriptableObject.
|
||||
* @param name the identifier for the property
|
||||
* @see org.mozilla.javascript.Scriptable#get(String, Scriptable)
|
||||
* @see org.mozilla.javascript.ScriptableObject#deleteProperty(Scriptable, String)
|
||||
*/
|
||||
public void delete(String name);
|
||||
|
||||
/**
|
||||
* Removes a property from this object.
|
||||
*
|
||||
* The property is specified by an integral index
|
||||
* as defined for <code>get</code>.
|
||||
* <p>
|
||||
* To delete properties defined in a prototype chain,
|
||||
* see deleteProperty in ScriptableObject.
|
||||
*
|
||||
* Identical to <code>delete(String)</code> except that
|
||||
* an integral index is used to select the property.
|
||||
*
|
||||
* @param index the numeric index for the property
|
||||
* @see org.mozilla.javascript.Scriptable#get(int, Scriptable)
|
||||
* @see org.mozilla.javascript.ScriptableObject#deleteProperty(Scriptable, int)
|
||||
*/
|
||||
public void delete(int index);
|
||||
|
||||
/**
|
||||
* Get the prototype of the object.
|
||||
* @return the prototype
|
||||
*/
|
||||
public Scriptable getPrototype();
|
||||
|
||||
/**
|
||||
* Set the prototype of the object.
|
||||
* @param prototype the prototype to set
|
||||
*/
|
||||
public void setPrototype(Scriptable prototype);
|
||||
|
||||
/**
|
||||
* Get the parent scope of the object.
|
||||
* @return the parent scope
|
||||
*/
|
||||
public Scriptable getParentScope();
|
||||
|
||||
/**
|
||||
* Set the parent scope of the object.
|
||||
* @param parent the parent scope to set
|
||||
*/
|
||||
public void setParentScope(Scriptable parent);
|
||||
|
||||
/**
|
||||
* Get an array of property ids.
|
||||
*
|
||||
* Not all property ids need be returned. Those properties
|
||||
* whose ids are not returned are considered non-enumerable.
|
||||
*
|
||||
* @return an array of Objects. Each entry in the array is either
|
||||
* a java.lang.String or a java.lang.Number
|
||||
*/
|
||||
public Object[] getIds();
|
||||
|
||||
/**
|
||||
* Get the default value of the object with a given hint.
|
||||
* The hints are String.class for type String, Number.class for type
|
||||
* Number, Scriptable.class for type Object, and Boolean.class for
|
||||
* type Boolean. <p>
|
||||
*
|
||||
* A <code>hint</code> of null means "no hint".
|
||||
*
|
||||
* See ECMA 8.6.2.6.
|
||||
*
|
||||
* @param hint the type hint
|
||||
* @return the default value
|
||||
*/
|
||||
public Object getDefaultValue(Class<?> hint);
|
||||
|
||||
/**
|
||||
* The instanceof operator.
|
||||
*
|
||||
* <p>
|
||||
* The JavaScript code "lhs instanceof rhs" causes rhs.hasInstance(lhs) to
|
||||
* be called.
|
||||
*
|
||||
* <p>
|
||||
* The return value is implementation dependent so that embedded host objects can
|
||||
* return an appropriate value. See the JS 1.3 language documentation for more
|
||||
* detail.
|
||||
*
|
||||
* <p>This operator corresponds to the proposed EMCA [[HasInstance]] operator.
|
||||
*
|
||||
* @param instance The value that appeared on the LHS of the instanceof
|
||||
* operator
|
||||
*
|
||||
* @return an implementation dependent value
|
||||
*/
|
||||
public boolean hasInstance(Scriptable instance);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// API class
|
||||
|
||||
package org.mozilla.javascript;
|
||||
|
||||
/**
|
||||
* This is the default implementation of the Scriptable interface. This
|
||||
* class provides convenient default behavior that makes it easier to
|
||||
* define host objects.
|
||||
* <p>
|
||||
* Various properties and methods of JavaScript objects can be conveniently
|
||||
* defined using methods of ScriptableObject.
|
||||
* <p>
|
||||
* Classes extending ScriptableObject must define the getClassName method.
|
||||
*
|
||||
* @see org.mozilla.javascript.Scriptable
|
||||
* @author Norris Boyd
|
||||
*/
|
||||
|
||||
public abstract class ScriptableObject implements Scriptable
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.javascript.optimizer;
|
||||
|
||||
import org.mozilla.javascript.CompilerEnvirons;
|
||||
|
||||
/**
|
||||
* Generates class files from script sources.
|
||||
*
|
||||
* since 1.5 Release 5
|
||||
* @author Igor Bukanov
|
||||
*/
|
||||
|
||||
public class ClassCompiler
|
||||
{
|
||||
/**
|
||||
* Construct ClassCompiler that uses the specified compiler environment
|
||||
* when generating classes.
|
||||
*/
|
||||
public ClassCompiler(CompilerEnvirons compilerEnv)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the class name to use for main method implementation.
|
||||
* The class must have a method matching
|
||||
* <code>public static void main(Script sc, String[] args)</code>, it will be
|
||||
* called when <code>main(String[] args)</code> is called in the generated
|
||||
* class. The class name should be fully qulified name and include the
|
||||
* package name like in <code>org.foo.Bar</code>.
|
||||
*/
|
||||
public void setMainMethodClass(String className)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the class for main method implementation.
|
||||
* @see #setMainMethodClass(String)
|
||||
*/
|
||||
public String getMainMethodClass()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the compiler environment the compiler uses.
|
||||
*/
|
||||
public CompilerEnvirons getCompilerEnv()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the class that the generated target will extend.
|
||||
*/
|
||||
public Class<?> getTargetExtends()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the class that the generated target will extend.
|
||||
*
|
||||
* @param extendsClass the class it extends
|
||||
*/
|
||||
public void setTargetExtends(Class<?> extendsClass)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the interfaces that the generated target will implement.
|
||||
*/
|
||||
public Class<?>[] getTargetImplements()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the interfaces that the generated target will implement.
|
||||
*
|
||||
* @param implementsClasses an array of Class objects, one for each
|
||||
* interface the target will extend
|
||||
*/
|
||||
public void setTargetImplements(Class<?>[] implementsClasses)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile JavaScript source into one or more Java class files.
|
||||
* The first compiled class will have name mainClassName.
|
||||
* If the results of {@link #getTargetExtends()} or
|
||||
* {@link #getTargetImplements()} are not null, then the first compiled
|
||||
* class will extend the specified super class and implement
|
||||
* specified interfaces.
|
||||
*
|
||||
* @return array where elements with even indexes specifies class name
|
||||
* and the following odd index gives class file body as byte[]
|
||||
* array. The initial element of the array always holds
|
||||
* mainClassName and array[1] holds its byte code.
|
||||
*/
|
||||
public Object[] compileToClassFiles(String source,
|
||||
String sourceLocation,
|
||||
int lineno,
|
||||
String mainClassName)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -3,33 +3,53 @@ import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.Iterator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.ObjectWriter;
|
||||
import com.fasterxml.jackson.databind.ObjectReader;
|
||||
|
||||
class Test {
|
||||
public static class Potato {
|
||||
private String name;
|
||||
|
||||
private String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
public static String taint() {
|
||||
return "tainted";
|
||||
}
|
||||
|
||||
public static void sink(Object any) {}
|
||||
|
||||
public static void jacksonObjectMapper() throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException {
|
||||
String s = taint();
|
||||
ObjectMapper om = new ObjectMapper();
|
||||
File file = new File("testFile");
|
||||
om.writeValue(file, s);
|
||||
sink(file); //$hasTaintFlow
|
||||
OutputStream out = new FileOutputStream(file);
|
||||
om.writeValue(out, s);
|
||||
sink(file); //$hasTaintFlow
|
||||
Writer writer = new StringWriter();
|
||||
om.writeValue(writer, s);
|
||||
sink(writer); //$hasTaintFlow
|
||||
JsonGenerator generator = new JsonFactory().createGenerator(new StringWriter());
|
||||
om.writeValue(generator, s);
|
||||
sink(generator); //$hasTaintFlow
|
||||
String t = om.writeValueAsString(s);
|
||||
System.out.println(t);
|
||||
sink(t); //$hasTaintFlow
|
||||
byte[] bs = om.writeValueAsBytes(s);
|
||||
String reconstructed = new String(bs, "utf-8");
|
||||
System.out.println(reconstructed);
|
||||
sink(bs); //$hasTaintFlow
|
||||
sink(reconstructed); //$hasTaintFlow
|
||||
}
|
||||
|
||||
public static void jacksonObjectWriter() throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException {
|
||||
@@ -37,16 +57,56 @@ class Test {
|
||||
ObjectWriter ow = new ObjectWriter();
|
||||
File file = new File("testFile");
|
||||
ow.writeValue(file, s);
|
||||
sink(file); //$hasTaintFlow
|
||||
OutputStream out = new FileOutputStream(file);
|
||||
ow.writeValue(out, s);
|
||||
sink(out); //$hasTaintFlow
|
||||
Writer writer = new StringWriter();
|
||||
ow.writeValue(writer, s);
|
||||
sink(writer); //$hasTaintFlow
|
||||
JsonGenerator generator = new JsonFactory().createGenerator(new StringWriter());
|
||||
ow.writeValue(generator, s);
|
||||
sink(generator); //$hasTaintFlow
|
||||
String t = ow.writeValueAsString(s);
|
||||
System.out.println(t);
|
||||
sink(t); //$hasTaintFlow
|
||||
byte[] bs = ow.writeValueAsBytes(s);
|
||||
String reconstructed = new String(bs, "utf-8");
|
||||
System.out.println(reconstructed);
|
||||
sink(bs); //$hasTaintFlow
|
||||
sink(reconstructed); //$hasTaintFlow
|
||||
}
|
||||
|
||||
public static void jacksonObjectReader() throws java.io.IOException {
|
||||
String s = taint();
|
||||
ObjectMapper om = new ObjectMapper();
|
||||
ObjectReader reader = om.readerFor(Potato.class);
|
||||
sink(reader.readValue(s)); //$hasTaintFlow
|
||||
sink(reader.readValue(s, Potato.class).name); //$hasTaintFlow
|
||||
sink(reader.readValue(s, Potato.class).getName()); //$hasTaintFlow
|
||||
}
|
||||
|
||||
public static void jacksonObjectReaderIterable() throws java.io.IOException {
|
||||
String s = taint();
|
||||
ObjectMapper om = new ObjectMapper();
|
||||
ObjectReader reader = om.readerFor(Potato.class);
|
||||
sink(reader.readValues(s)); //$hasTaintFlow
|
||||
Iterator<Potato> pIterator = reader.readValues(s, Potato.class);
|
||||
while(pIterator.hasNext()) {
|
||||
Potato p = pIterator.next();
|
||||
sink(p); //$hasTaintFlow
|
||||
sink(p.name); //$hasTaintFlow
|
||||
sink(p.getName()); //$hasTaintFlow
|
||||
}
|
||||
}
|
||||
|
||||
public static void jacksonTwoStepDeserialization() throws java.io.IOException {
|
||||
String s = taint();
|
||||
Map<String, Object> taintedParams = new HashMap<>();
|
||||
taintedParams.put("name", s);
|
||||
ObjectMapper om = new ObjectMapper();
|
||||
JsonNode jn = om.valueToTree(taintedParams);
|
||||
sink(jn); //$hasTaintFlow
|
||||
Potato p = om.convertValue(jn, Potato.class);
|
||||
sink(p); //$hasTaintFlow
|
||||
sink(p.getName()); //$hasTaintFlow
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:10:43:10:54 | value |
|
||||
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:13:73:13:84 | value |
|
||||
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:16:44:16:55 | value |
|
||||
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:19:36:19:47 | value |
|
||||
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:22:35:22:46 | value |
|
||||
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:26:36:26:47 | value |
|
||||
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:10:43:10:54 | value |
|
||||
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:13:73:13:84 | value |
|
||||
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:16:44:16:55 | value |
|
||||
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:19:36:19:47 | value |
|
||||
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:22:35:22:46 | value |
|
||||
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:26:36:26:47 | value |
|
||||
| Test.java:18:14:18:20 | taint(...) |
|
||||
| Test.java:21:17:21:20 | file [post update] |
|
||||
| Test.java:21:23:21:23 | s |
|
||||
| Test.java:22:43:22:46 | file |
|
||||
| Test.java:23:17:23:19 | out [post update] |
|
||||
| Test.java:23:22:23:22 | s |
|
||||
| Test.java:25:17:25:22 | writer [post update] |
|
||||
| Test.java:25:25:25:25 | s |
|
||||
| Test.java:27:17:27:25 | generator [post update] |
|
||||
| Test.java:27:28:27:28 | s |
|
||||
| Test.java:28:14:28:37 | writeValueAsString(...) |
|
||||
| Test.java:28:36:28:36 | s |
|
||||
| Test.java:29:22:29:22 | t |
|
||||
| Test.java:30:15:30:37 | writeValueAsBytes(...) |
|
||||
| Test.java:30:36:30:36 | s |
|
||||
| Test.java:31:26:31:48 | new String(...) |
|
||||
| Test.java:31:37:31:38 | bs |
|
||||
| Test.java:32:22:32:34 | reconstructed |
|
||||
| Test.java:36:14:36:20 | taint(...) |
|
||||
| Test.java:39:17:39:20 | file [post update] |
|
||||
| Test.java:39:23:39:23 | s |
|
||||
| Test.java:40:43:40:46 | file |
|
||||
| Test.java:41:17:41:19 | out [post update] |
|
||||
| Test.java:41:22:41:22 | s |
|
||||
| Test.java:43:17:43:22 | writer [post update] |
|
||||
| Test.java:43:25:43:25 | s |
|
||||
| Test.java:45:17:45:25 | generator [post update] |
|
||||
| Test.java:45:28:45:28 | s |
|
||||
| Test.java:46:14:46:37 | writeValueAsString(...) |
|
||||
| Test.java:46:36:46:36 | s |
|
||||
| Test.java:47:22:47:22 | t |
|
||||
| Test.java:48:15:48:37 | writeValueAsBytes(...) |
|
||||
| Test.java:48:36:48:36 | s |
|
||||
| Test.java:49:26:49:48 | new String(...) |
|
||||
| Test.java:49:37:49:38 | bs |
|
||||
| Test.java:50:22:50:34 | reconstructed |
|
||||
|
||||
@@ -1,17 +1,34 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
class Conf extends TaintTracking::Configuration {
|
||||
Conf() { this = "qltest:dataflow:jackson" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr().(MethodAccess).getMethod().hasName("taint")
|
||||
override predicate isSource(DataFlow::Node n) {
|
||||
n.asExpr().(MethodAccess).getMethod().hasName("taint")
|
||||
or
|
||||
n instanceof RemoteFlowSource
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { any() }
|
||||
override predicate isSink(DataFlow::Node n) {
|
||||
exists(MethodAccess ma | ma.getMethod().hasName("sink") | n.asExpr() = ma.getAnArgument())
|
||||
}
|
||||
}
|
||||
|
||||
from DataFlow::Node source, DataFlow::Node sink, Conf config
|
||||
where config.hasFlow(source, sink)
|
||||
select sink
|
||||
class HasFlowTest extends InlineExpectationsTest {
|
||||
HasFlowTest() { this = "HasFlowTest" }
|
||||
|
||||
override string getARelevantTag() { result = "hasTaintFlow" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "hasTaintFlow" and
|
||||
exists(DataFlow::Node src, DataFlow::Node sink, Conf conf | conf.hasFlow(src, sink) |
|
||||
sink.getLocation() = location and
|
||||
element = sink.toString() and
|
||||
value = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.fasterxml.jackson.databind;
|
||||
|
||||
public class JsonNode {
|
||||
import java.util.*;
|
||||
|
||||
public abstract class JsonNode implements Iterable<JsonNode> {
|
||||
public JsonNode() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.fasterxml.jackson.databind;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
public class MappingIterator<T> implements Iterator<T>, Closeable {
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -26,4 +26,16 @@ public class ObjectMapper {
|
||||
public String writeValueAsString(Object value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public ObjectReader readerFor(Class<?> type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T extends JsonNode> T valueToTree(Object fromValue) throws IllegalArgumentException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> T convertValue(Object fromValue, Class<T> toValueType) throws IllegalArgumentException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.fasterxml.jackson.databind;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class ObjectReader {
|
||||
public ObjectReader forType(Class<?> valueType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> T readValue(String src) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> T readValue(String src, Class<T> valueType) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> T readValue(byte[] content) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> T readValue(byte[] content, Class<T> valueType) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> T readValue(File src) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> T readValue(InputStream src) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> T readValue(InputStream src, Class<T> valueType) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> T readValue(Reader src) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> T readValue(Reader src, Class<T> valueType) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> MappingIterator<T> readValues(String src) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> MappingIterator<T> readValues(String src, Class<T> valueType) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> MappingIterator<T> readValues(byte[] content) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> MappingIterator<T> readValues(byte[] content, Class<T> valueType) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> MappingIterator<T> readValues(File src) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> MappingIterator<T> readValues(InputStream src) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> MappingIterator<T> readValues(InputStream src, Class<T> valueType) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> MappingIterator<T> readValues(Reader src) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> MappingIterator<T> readValues(Reader src, Class<T> valueType) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// Autogenerated AST node
|
||||
package org.python.antlr.base;
|
||||
|
||||
public abstract class mod {
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) Corporation for National Research Initiatives
|
||||
package org.python.core;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utility class for loading compiled Python modules and Java classes defined in Python modules.
|
||||
*/
|
||||
public class BytecodeLoader {
|
||||
|
||||
/**
|
||||
* Turn the Java class file data into a Java class.
|
||||
*
|
||||
* @param name fully-qualified binary name of the class
|
||||
* @param data a class file as a byte array
|
||||
* @param referents super-classes and interfaces that the new class will reference.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Class<?> makeClass(String name, byte[] data, Class<?>... referents) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn the Java class file data into a Java class.
|
||||
*
|
||||
* @param name the name of the class
|
||||
* @param referents super-classes and interfaces that the new class will reference.
|
||||
* @param data a class file as a byte array
|
||||
*/
|
||||
public static Class<?> makeClass(String name, List<Class<?>> referents, byte[] data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn the Java class file data for a compiled Python module into a {@code PyCode} object, by
|
||||
* constructing an instance of the named class and calling the instance's
|
||||
* {@link PyRunnable#getMain()}.
|
||||
*
|
||||
* @param name fully-qualified binary name of the class
|
||||
* @param data a class file as a byte array
|
||||
* @param filename to provide to the constructor of the named class
|
||||
* @return the {@code PyCode} object produced by the named class' {@code getMain}
|
||||
*/
|
||||
public static PyCode makeCode(String name, byte[] data, String filename) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.python.core;
|
||||
|
||||
public enum CompileMode {
|
||||
eval,
|
||||
single,
|
||||
exec;
|
||||
|
||||
public static CompileMode getMode(String mode) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// At some future point this will also be extended - in conjunction with
|
||||
// Py#compileFlags - to add
|
||||
// support for a compiler factory that user code can choose in place of the
|
||||
// normal compiler.
|
||||
// (Perhaps a better name might have been "CompilerOptions".)
|
||||
|
||||
package org.python.core;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class CompilerFlags implements Serializable {
|
||||
public CompilerFlags() {
|
||||
}
|
||||
|
||||
public CompilerFlags(int co_flags) {
|
||||
}
|
||||
}
|
||||
134
java/ql/test/stubs/jython-2.7.2/org/python/core/Py.java
Normal file
134
java/ql/test/stubs/jython-2.7.2/org/python/core/Py.java
Normal file
@@ -0,0 +1,134 @@
|
||||
// Copyright (c) Corporation for National Research Initiatives
|
||||
package org.python.core;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.python.antlr.base.mod;
|
||||
|
||||
public final class Py {
|
||||
/**
|
||||
Convert a given <code>PyObject</code> to an instance of a Java class.
|
||||
Identical to <code>o.__tojava__(c)</code> except that it will
|
||||
raise a <code>TypeError</code> if the conversion fails.
|
||||
@param o the <code>PyObject</code> to convert.
|
||||
@param c the class to convert it to.
|
||||
**/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T tojava(PyObject o, Class<T> c) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// ??pending: was @deprecated but is actually used by proxie code.
|
||||
// Can get rid of it?
|
||||
public static Object tojava(PyObject o, String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the PyObjectAdapter passed to {@link PySystemState#initialize} to turn o into a PyObject.
|
||||
*
|
||||
* @see ClassicPyObjectAdapter - default PyObjectAdapter type
|
||||
*/
|
||||
public static PyObject java2py(Object o) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the PyObjectAdapter passed to {@link PySystemState#initialize} to turn
|
||||
* <code>objects</code> into an array of PyObjects.
|
||||
*
|
||||
* @see ClassicPyObjectAdapter - default PyObjectAdapter type
|
||||
*/
|
||||
public static PyObject[] javas2pys(Object... objects) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static PyObject makeClass(String name, PyObject[] bases, PyCode code,
|
||||
PyObject[] closure_cells) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static PyObject makeClass(String name, PyObject base, PyObject dict) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Python class.
|
||||
*
|
||||
* @param name the String name of the class
|
||||
* @param bases an array of PyObject base classes
|
||||
* @param dict the class's namespace, containing the class body
|
||||
* definition
|
||||
* @return a new Python Class PyObject
|
||||
*/
|
||||
public static PyObject makeClass(String name, PyObject[] bases, PyObject dict) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static CompilerFlags getCompilerFlags() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static CompilerFlags getCompilerFlags(int flags, boolean dont_inherit) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static CompilerFlags getCompilerFlags(CompilerFlags flags, boolean dont_inherit) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// w/o compiler-flags
|
||||
public static PyCode compile(InputStream istream, String filename, CompileMode kind) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point for compiling modules.
|
||||
*
|
||||
* @param node Module node, coming from the parsing process
|
||||
* @param name Internal name for the compiled code. Typically generated by
|
||||
* calling {@link #getName()}.
|
||||
* @param filename Source file name
|
||||
* @param linenumbers True to track source line numbers on the generated
|
||||
* code
|
||||
* @param printResults True to call the sys.displayhook on the result of
|
||||
* the code
|
||||
* @param cflags Compiler flags
|
||||
* @return Code object for the compiled module
|
||||
*/
|
||||
public static PyCode compile_flags(mod node, String name, String filename,
|
||||
boolean linenumbers, boolean printResults,
|
||||
CompilerFlags cflags) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static PyCode compile_flags(mod node, String filename,
|
||||
CompileMode kind, CompilerFlags cflags) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles python source code coming from a file or another external stream
|
||||
*/
|
||||
public static PyCode compile_flags(InputStream istream, String filename,
|
||||
CompileMode kind, CompilerFlags cflags) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles python source code coming from String (raw bytes) data.
|
||||
*
|
||||
* If the String is properly decoded (from PyUnicode) the PyCF_SOURCE_IS_UTF8 flag
|
||||
* should be specified.
|
||||
*/
|
||||
public static PyCode compile_flags(String data, String filename,
|
||||
CompileMode kind, CompilerFlags cflags) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static PyObject compile_command_flags(String string, String filename,
|
||||
CompileMode kind, CompilerFlags cflags, boolean stdprompt) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
43
java/ql/test/stubs/jython-2.7.2/org/python/core/PyCode.java
Normal file
43
java/ql/test/stubs/jython-2.7.2/org/python/core/PyCode.java
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) Corporation for National Research Initiatives
|
||||
package org.python.core;
|
||||
|
||||
/**
|
||||
* A super class for all python code implementations.
|
||||
*/
|
||||
public abstract class PyCode extends PyObject
|
||||
{
|
||||
abstract public PyObject call(ThreadState state,
|
||||
PyObject args[], String keywords[],
|
||||
PyObject globals, PyObject[] defaults,
|
||||
PyObject closure);
|
||||
|
||||
abstract public PyObject call(ThreadState state,
|
||||
PyObject self, PyObject args[],
|
||||
String keywords[],
|
||||
PyObject globals, PyObject[] defaults,
|
||||
PyObject closure);
|
||||
|
||||
abstract public PyObject call(ThreadState state,
|
||||
PyObject globals, PyObject[] defaults,
|
||||
PyObject closure);
|
||||
|
||||
abstract public PyObject call(ThreadState state,
|
||||
PyObject arg1, PyObject globals,
|
||||
PyObject[] defaults, PyObject closure);
|
||||
|
||||
abstract public PyObject call(ThreadState state,
|
||||
PyObject arg1, PyObject arg2,
|
||||
PyObject globals, PyObject[] defaults,
|
||||
PyObject closure);
|
||||
|
||||
abstract public PyObject call(ThreadState state,
|
||||
PyObject arg1, PyObject arg2, PyObject arg3,
|
||||
PyObject globals, PyObject[] defaults,
|
||||
PyObject closure);
|
||||
|
||||
abstract public PyObject call(ThreadState state,
|
||||
PyObject arg1, PyObject arg2, PyObject arg3, PyObject arg4,
|
||||
PyObject globals, PyObject[] defaults,
|
||||
PyObject closure);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) Corporation for National Research Initiatives
|
||||
package org.python.core;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* A wrapper for all python exception. Note that the well-known python exceptions are <b>not</b>
|
||||
* subclasses of PyException. Instead the python exception class is stored in the <code>type</code>
|
||||
* field and value or class instance is stored in the <code>value</code> field.
|
||||
*/
|
||||
public class PyException extends RuntimeException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) Corporation for National Research Initiatives
|
||||
package org.python.core;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* All objects known to the Jython runtime system are represented by an instance of the class
|
||||
* {@code PyObject} or one of its subclasses.
|
||||
*/
|
||||
public class PyObject implements Serializable {
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
// Copyright (c) Corporation for National Research Initiatives
|
||||
package org.python.core;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* The "sys" module.
|
||||
*/
|
||||
// xxx Many have lamented, this should really be a module!
|
||||
// but it will require some refactoring to see this wish come true.
|
||||
public class PySystemState extends PyObject {
|
||||
public PySystemState() {
|
||||
}
|
||||
|
||||
public static void classDictInit(PyObject dict) {
|
||||
}
|
||||
|
||||
public ClassLoader getSyspathJavaLoader() {
|
||||
return null;
|
||||
}
|
||||
|
||||
// xxx fix this accessors
|
||||
public PyObject __findattr_ex__(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void __setattr__(String name, PyObject value) {
|
||||
}
|
||||
|
||||
public void __delattr__(String name) {
|
||||
}
|
||||
|
||||
public PyObject gettrace() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void settrace(PyObject tracefunc) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the current working directory to the specified path.
|
||||
*
|
||||
* path is assumed to be absolute and canonical (via os.path.realpath).
|
||||
*
|
||||
* @param path a path String
|
||||
*/
|
||||
public void setCurrentWorkingDir(String path) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a string representing the current working directory.
|
||||
*
|
||||
* @return a path String
|
||||
*/
|
||||
public String getCurrentWorkingDir() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a path. Returns the full path taking the current working directory into account.
|
||||
*
|
||||
* @param path a path String
|
||||
* @return a resolved path String
|
||||
*/
|
||||
public String getPath(String path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a path, returning a {@link File}, taking the current working directory into account.
|
||||
*
|
||||
* @param path a path <code>String</code>
|
||||
* @return a resolved <code>File</code>
|
||||
*/
|
||||
public File getFile(String path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public ClassLoader getClassLoader() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setClassLoader(ClassLoader classLoader) {
|
||||
}
|
||||
|
||||
public static Properties getBaseProperties() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static synchronized void initialize() {
|
||||
}
|
||||
|
||||
public static synchronized void initialize(Properties preProperties,
|
||||
Properties postProperties) {
|
||||
}
|
||||
|
||||
public static synchronized void initialize(Properties preProperties, Properties postProperties,
|
||||
String[] argv) {
|
||||
}
|
||||
|
||||
public static synchronized void initialize(Properties preProperties, Properties postProperties,
|
||||
String[] argv, ClassLoader classLoader) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a classpath directory to the list of places that are searched for java packages.
|
||||
* <p>
|
||||
* <b>Note</b>. Classes found in directory and sub-directory are not made available to jython by
|
||||
* this call. It only makes the java package found in the directory available. This call is
|
||||
* mostly useful if jython is embedded in an application that deals with its own class loaders.
|
||||
* A servlet container is a very good example. Calling
|
||||
* {@code add_classdir("<context>/WEB-INF/classes")} makes the java packages in WEB-INF classes
|
||||
* available to jython import. However the actual class loading is completely handled by the
|
||||
* servlet container's context classloader.
|
||||
*/
|
||||
public static void add_classdir(String directoryPath) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a .jar and .zip directory to the list of places that are searched for java .jar and .zip
|
||||
* files. The .jar and .zip files found will not be cached.
|
||||
* <p>
|
||||
* <b>Note</b>. Classes in .jar and .zip files found in the directory are not made available to
|
||||
* jython by this call. See the note for add_classdir(dir) for more details.
|
||||
*
|
||||
* @param directoryPath The name of a directory.
|
||||
*
|
||||
* @see #add_classdir
|
||||
*/
|
||||
public static void add_extdir(String directoryPath) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a .jar and .zip directory to the list of places that are searched for java .jar and .zip
|
||||
* files.
|
||||
* <p>
|
||||
* <b>Note</b>. Classes in .jar and .zip files found in the directory are not made available to
|
||||
* jython by this call. See the note for add_classdir(dir) for more details.
|
||||
*
|
||||
* @param directoryPath The name of a directory.
|
||||
* @param cache Controls if the packages in the zip and jar file should be cached.
|
||||
*
|
||||
* @see #add_classdir
|
||||
*/
|
||||
public static void add_extdir(String directoryPath, boolean cache) {
|
||||
}
|
||||
|
||||
// Not public by design. We can't rebind the displayhook if
|
||||
// a reflected function is inserted in the class dict.
|
||||
|
||||
/**
|
||||
* Exit a Python program with the given status.
|
||||
*
|
||||
* @param status the value to exit with
|
||||
* @throws PyException {@code SystemExit} always throws this exception. When caught at top level
|
||||
* the program will exit.
|
||||
*/
|
||||
public static void exit(PyObject status) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Exit a Python program with the status 0.
|
||||
*/
|
||||
public static void exit() {
|
||||
}
|
||||
|
||||
public static void exc_clear() {
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
}
|
||||
|
||||
public void close() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) Corporation for National Research Initiatives
|
||||
package org.python.core;
|
||||
|
||||
// a ThreadState refers to one PySystemState; this weak ref allows for tracking all ThreadState objects
|
||||
// that refer to a given PySystemState
|
||||
|
||||
public class ThreadState {
|
||||
|
||||
public PyException exception;
|
||||
|
||||
public ThreadState(PySystemState systemState) {
|
||||
setSystemState(systemState);
|
||||
}
|
||||
|
||||
public void setSystemState(PySystemState systemState) {
|
||||
}
|
||||
|
||||
public PySystemState getSystemState() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean enterRepr(PyObject obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void exitRepr(PyObject obj) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
// Copyright (c) Corporation for National Research Initiatives
|
||||
package org.python.util;
|
||||
|
||||
import org.python.core.*;
|
||||
|
||||
/**
|
||||
* This class provides the interface for compiling and running code that supports an interactive
|
||||
* interpreter.
|
||||
*/
|
||||
// Based on CPython-1.5.2's code module
|
||||
public class InteractiveInterpreter extends PythonInterpreter {
|
||||
|
||||
/**
|
||||
* Construct an InteractiveInterpreter with all default characteristics: default state (from
|
||||
* {@link Py#getSystemState()}), and a new empty dictionary of local variables.
|
||||
* */
|
||||
public InteractiveInterpreter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an InteractiveInterpreter with state (from {@link Py#getSystemState()}), and the
|
||||
* specified dictionary of local variables.
|
||||
*
|
||||
* @param locals dictionary to use, or if <code>null</code>, a new empty one will be created
|
||||
*/
|
||||
public InteractiveInterpreter(PyObject locals) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an InteractiveInterpreter with, and system state the specified dictionary of local
|
||||
* variables.
|
||||
*
|
||||
* @param locals dictionary to use, or if <code>null</code>, a new empty one will be created
|
||||
* @param systemState interpreter state, or if <code>null</code> use {@link Py#getSystemState()}
|
||||
*/
|
||||
public InteractiveInterpreter(PyObject locals, PySystemState systemState) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile and run some source in the interpreter, in the mode {@link CompileMode#single} which
|
||||
* is used for incremental compilation at the interactive console, known as {@code <input>}.
|
||||
*
|
||||
* @param source Python code
|
||||
* @return <code>true</code> to indicate a partial statement was entered
|
||||
*/
|
||||
public boolean runsource(String source) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile and run some source in the interpreter, in the mode {@link CompileMode#single} which
|
||||
* is used for incremental compilation at the interactive console.
|
||||
*
|
||||
* @param source Python code
|
||||
* @param filename name with which to label this console input (e.g. in error messages).
|
||||
* @return <code>true</code> to indicate a partial statement was entered
|
||||
*/
|
||||
public boolean runsource(String source, String filename) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile and run some source in the interpreter, according to the {@link CompileMode} given.
|
||||
* This method supports incremental compilation and interpretation through the return value,
|
||||
* where {@code true} signifies that more input is expected in order to complete the Python
|
||||
* statement. An interpreter can use this to decide whether to use {@code sys.ps1}
|
||||
* ("{@code >>> }") or {@code sys.ps2} ("{@code ... }") to prompt the next line. The arguments
|
||||
* are the same as the mandatory ones in the Python {@code compile()} command.
|
||||
* <p>
|
||||
* One the following can happen:
|
||||
* <ol>
|
||||
* <li>The input is incorrect; compilation raised an exception (SyntaxError or OverflowError). A
|
||||
* syntax traceback will be printed by calling {@link #showexception(PyException)}. Return is
|
||||
* {@code false}.</li>
|
||||
*
|
||||
* <li>The input is incomplete, and more input is required; compilation returned no code.
|
||||
* Nothing happens. Return is {@code true}.</li>
|
||||
*
|
||||
* <li>The input is complete; compilation returned a code object. The code is executed by
|
||||
* calling {@link #runcode(PyObject)} (which also handles run-time exceptions, except for
|
||||
* SystemExit). Return is {@code false}.</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param source Python code
|
||||
* @param filename name with which to label this console input (e.g. in error messages).
|
||||
* @param kind of compilation required: {@link CompileMode#eval}, {@link CompileMode#exec} or
|
||||
* {@link CompileMode#single}
|
||||
* @return {@code true} to indicate a partial statement was provided
|
||||
*/
|
||||
public boolean runsource(String source, String filename, CompileMode kind) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a code object. When an exception occurs, {@link #showexception(PyException)} is
|
||||
* called to display a stack trace, except in the case of SystemExit, which is re-raised.
|
||||
* <p>
|
||||
* A note about KeyboardInterrupt: this exception may occur elsewhere in this code, and may not
|
||||
* always be caught. The caller should be prepared to deal with it.
|
||||
**/
|
||||
|
||||
// Make this run in another thread somehow????
|
||||
public void runcode(PyObject code) {
|
||||
}
|
||||
|
||||
public void showexception(PyException exc) {
|
||||
}
|
||||
|
||||
public void write(String data) {
|
||||
}
|
||||
|
||||
public void resetbuffer() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
package org.python.util;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.python.core.PyCode;
|
||||
import org.python.core.PyObject;
|
||||
|
||||
/**
|
||||
* The PythonInterpreter class is a standard wrapper for a Jython interpreter for embedding in a
|
||||
* Java application.
|
||||
*/
|
||||
public class PythonInterpreter implements Closeable {
|
||||
|
||||
/**
|
||||
* Initializes the Jython runtime. This should only be called once, before any other Python
|
||||
* objects (including PythonInterpreter) are created.
|
||||
*
|
||||
* @param preProperties A set of properties. Typically System.getProperties() is used.
|
||||
* preProperties override properties from the registry file.
|
||||
* @param postProperties Another set of properties. Values like python.home, python.path and all
|
||||
* other values from the registry files can be added to this property set.
|
||||
* postProperties override system properties and registry properties.
|
||||
* @param argv Command line arguments, assigned to sys.argv.
|
||||
*/
|
||||
public static void
|
||||
initialize(Properties preProperties, Properties postProperties, String[] argv) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new interpreter with an empty local namespace.
|
||||
*/
|
||||
public PythonInterpreter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new interpreter with the ability to maintain a separate local namespace for each
|
||||
* thread (set by invoking setLocals()).
|
||||
*
|
||||
* @param dict a Python mapping object (e.g., a dictionary) for use as the default namespace
|
||||
*/
|
||||
public static PythonInterpreter threadLocalStateInterpreter(PyObject dict) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new interpreter with a specified local namespace.
|
||||
*
|
||||
* @param dict a Python mapping object (e.g., a dictionary) for use as the namespace
|
||||
*/
|
||||
public PythonInterpreter(PyObject dict) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a Python object to use for the standard output stream, <code>sys.stdout</code>. This
|
||||
* stream is used in a byte-oriented way (mostly) that depends on the type of file-like object.
|
||||
* The behaviour as implemented is:
|
||||
* <table border=1>
|
||||
* <caption>Stream behaviour for various object types</caption>
|
||||
* <tr align=center>
|
||||
* <td></td>
|
||||
* <td colspan=3>Python type of object <code>o</code> written</td>
|
||||
* </tr>
|
||||
* <tr align=left>
|
||||
* <th></th>
|
||||
* <th><code>str/bytes</code></th>
|
||||
* <th><code>unicode</code></th>
|
||||
* <th>Any other type</th>
|
||||
* </tr>
|
||||
* <tr align=left>
|
||||
* <th>{@link PyFile}</th>
|
||||
* <td>as bytes directly</td>
|
||||
* <td>respect {@link PyFile#encoding}</td>
|
||||
* <td>call <code>str(o)</code> first</td>
|
||||
* </tr>
|
||||
* <tr align=left>
|
||||
* <th>{@link PyFileWriter}</th>
|
||||
* <td>each byte value as a <code>char</code></td>
|
||||
* <td>write as Java <code>char</code>s</td>
|
||||
* <td>call <code>o.toString()</code> first</td>
|
||||
* </tr>
|
||||
* <tr align=left>
|
||||
* <th>Other {@link PyObject} <code>f</code></th>
|
||||
* <td>invoke <code>f.write(str(o))</code></td>
|
||||
* <td>invoke <code>f.write(o)</code></td>
|
||||
* <td>invoke <code>f.write(str(o))</code></td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*
|
||||
* @param outStream Python file-like object to use as the output stream
|
||||
*/
|
||||
public void setOut(PyObject outStream) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a {@link java.io.Writer} to use for the standard output stream, <code>sys.stdout</code>.
|
||||
* The behaviour as implemented is to output each object <code>o</code> by calling
|
||||
* <code>o.toString()</code> and writing this as UTF-16.
|
||||
*
|
||||
* @param outStream to use as the output stream
|
||||
*/
|
||||
public void setOut(java.io.Writer outStream) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a {@link java.io.OutputStream} to use for the standard output stream.
|
||||
*
|
||||
* @param outStream OutputStream to use as output stream
|
||||
*/
|
||||
public void setOut(java.io.OutputStream outStream) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a Python object to use for the standard output stream, <code>sys.stderr</code>. This
|
||||
* stream is used in a byte-oriented way (mostly) that depends on the type of file-like object,
|
||||
* in the same way as {@link #setOut(PyObject)}.
|
||||
*
|
||||
* @param outStream Python file-like object to use as the error output stream
|
||||
*/
|
||||
public void setErr(PyObject outStream) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a {@link java.io.Writer} to use for the standard output stream, <code>sys.stdout</code>.
|
||||
* The behaviour as implemented is to output each object <code>o</code> by calling
|
||||
* <code>o.toString()</code> and writing this as UTF-16.
|
||||
*
|
||||
* @param outStream to use as the error output stream
|
||||
*/
|
||||
public void setErr(java.io.Writer outStream) {
|
||||
}
|
||||
|
||||
public void setErr(java.io.OutputStream outStream) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates a string as a Python expression and returns the result.
|
||||
*/
|
||||
public PyObject eval(String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates a Python code object and returns the result.
|
||||
*/
|
||||
public PyObject eval(PyObject code) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a string of Python source in the local namespace.
|
||||
*
|
||||
* In some environments, such as Windows, Unicode characters in the script will be converted
|
||||
* into ascii question marks (?). This can be avoided by first compiling the fragment using
|
||||
* PythonInterpreter.compile(), and using the overridden form of this method which takes a
|
||||
* PyCode object. Code page declarations are not supported.
|
||||
*/
|
||||
public void exec(String s) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a Python code object in the local namespace.
|
||||
*/
|
||||
public void exec(PyObject code) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a file of Python source in the local namespace.
|
||||
*/
|
||||
public void execfile(String filename) {
|
||||
}
|
||||
|
||||
public void execfile(java.io.InputStream s) {
|
||||
}
|
||||
|
||||
public void execfile(java.io.InputStream s, String name) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles a string of Python source as either an expression (if possible) or a module.
|
||||
*
|
||||
* Designed for use by a JSR 223 implementation: "the Scripting API does not distinguish between
|
||||
* scripts which return values and those which do not, nor do they make the corresponding
|
||||
* distinction between evaluating or executing objects." (SCR.4.2.1)
|
||||
*/
|
||||
public PyCode compile(String script) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public PyCode compile(Reader reader) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public PyCode compile(String script, String filename) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public PyCode compile(Reader reader, String filename) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a variable in the local namespace.
|
||||
*
|
||||
* @param name the name of the variable
|
||||
* @param value the object to set the variable to (as converted to an appropriate Python object)
|
||||
*/
|
||||
public void set(String name, Object value) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a variable in the local namespace.
|
||||
*
|
||||
* @param name the name of the variable
|
||||
* @param value the Python object to set the variable to
|
||||
*/
|
||||
public void set(String name, PyObject value) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of a variable in the local namespace.
|
||||
*
|
||||
* @param name the name of the variable
|
||||
* @return the value of the variable, or null if that name isn't assigned
|
||||
*/
|
||||
public PyObject get(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of a variable in the local namespace.
|
||||
*
|
||||
* The value will be returned as an instance of the given Java class.
|
||||
* <code>interp.get("foo", Object.class)</code> will return the most appropriate generic Java
|
||||
* object.
|
||||
*
|
||||
* @param name the name of the variable
|
||||
* @param javaclass the class of object to return
|
||||
* @return the value of the variable as the given class, or null if that name isn't assigned
|
||||
*/
|
||||
public <T> T get(String name, Class<T> javaclass) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
}
|
||||
|
||||
public void close() {
|
||||
}
|
||||
}
|
||||
14
java/ql/test/stubs/scriptengine/javax/script/Bindings.java
Normal file
14
java/ql/test/stubs/scriptengine/javax/script/Bindings.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package javax.script;
|
||||
import java.util.Map;
|
||||
|
||||
public interface Bindings extends Map<String, Object> {
|
||||
public Object put(String name, Object value);
|
||||
|
||||
public void putAll(Map<? extends String, ? extends Object> toMerge);
|
||||
|
||||
public boolean containsKey(Object key);
|
||||
|
||||
public Object get(Object key);
|
||||
|
||||
public Object remove(Object key);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package javax.script;
|
||||
|
||||
import java.io.Reader;
|
||||
|
||||
public interface Compilable {
|
||||
public CompiledScript compile(String script) throws ScriptException;
|
||||
|
||||
public CompiledScript compile(Reader script) throws ScriptException;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package javax.script;
|
||||
|
||||
public abstract class CompiledScript {
|
||||
|
||||
public abstract Object eval(ScriptContext context) throws ScriptException;
|
||||
|
||||
public Object eval(Bindings bindings) throws ScriptException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object eval() throws ScriptException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public abstract ScriptEngine getEngine();
|
||||
|
||||
}
|
||||
@@ -2,5 +2,7 @@ package javax.script;
|
||||
|
||||
public interface ScriptEngine {
|
||||
Object eval(String var1) throws ScriptException;
|
||||
|
||||
public ScriptEngineFactory getFactory();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
package javax.script;
|
||||
|
||||
public interface ScriptEngineFactory {
|
||||
public String getEngineName();
|
||||
|
||||
public String getMethodCallSyntax(String obj, String m, String... args);
|
||||
|
||||
public String getProgram(String... statements);
|
||||
|
||||
ScriptEngine getScriptEngine();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package jdk.nashorn.api.scripting;
|
||||
|
||||
public interface ClassFilter {
|
||||
public boolean exposeToScripts(String className);
|
||||
}
|
||||
@@ -1,10 +1,31 @@
|
||||
package jdk.nashorn.api.scripting;
|
||||
|
||||
import javax.script.*;
|
||||
import java.io.Reader;
|
||||
|
||||
public final class NashornScriptEngine extends AbstractScriptEngine {
|
||||
import javax.script.AbstractScriptEngine;
|
||||
import javax.script.Compilable;
|
||||
import javax.script.CompiledScript;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineFactory;
|
||||
import javax.script.ScriptException;
|
||||
|
||||
public final class NashornScriptEngine extends AbstractScriptEngine implements Compilable {
|
||||
public Object eval(String var1) throws ScriptException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScriptEngineFactory getFactory() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompiledScript compile(final Reader reader) throws ScriptException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompiledScript compile(final String str) throws ScriptException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,20 +3,48 @@ package jdk.nashorn.api.scripting;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineFactory;
|
||||
|
||||
|
||||
public final class NashornScriptEngineFactory implements ScriptEngineFactory {
|
||||
|
||||
public NashornScriptEngineFactory() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEngineName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethodCallSyntax(final String obj, final String method, final String... args) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProgram(final String... statements) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScriptEngine getScriptEngine() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public ScriptEngine getScriptEngine(final ClassLoader appLoader) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public ScriptEngine getScriptEngine(String... args) {
|
||||
public ScriptEngine getScriptEngine(final ClassFilter classFilter) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public ScriptEngine getScriptEngine(final String... args) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public ScriptEngine getScriptEngine(final String[] args, final ClassLoader appLoader) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public ScriptEngine getScriptEngine(final String[] args, final ClassLoader appLoader, final ClassFilter classFilter) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
package org.springframework.web.servlet;
|
||||
|
||||
import java.util.Map;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
public class ModelAndView {
|
||||
@Nullable
|
||||
private Object view;
|
||||
@Nullable
|
||||
private HttpStatus status;
|
||||
private boolean cleared = false;
|
||||
|
||||
public ModelAndView() {
|
||||
}
|
||||
|
||||
public ModelAndView(String viewName) {
|
||||
this.view = viewName;
|
||||
}
|
||||
|
||||
public ModelAndView(View view) {
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
public ModelAndView(String viewName, @Nullable Map<String, ?> model) { }
|
||||
|
||||
public ModelAndView(View view, @Nullable Map<String, ?> model) { }
|
||||
|
||||
public ModelAndView(String viewName, HttpStatus status) { }
|
||||
|
||||
public ModelAndView(@Nullable String viewName, @Nullable Map<String, ?> model, @Nullable HttpStatus status) { }
|
||||
|
||||
public ModelAndView(String viewName, String modelName, Object modelObject) { }
|
||||
|
||||
public ModelAndView(View view, String modelName, Object modelObject) { }
|
||||
|
||||
public void setViewName(@Nullable String viewName) {
|
||||
this.view = viewName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getViewName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public void setView(@Nullable View view) { }
|
||||
|
||||
@Nullable
|
||||
public View getView() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean hasView() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isReference() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected Map<String, Object> getModelInternal() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Map<String, Object> getModel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setStatus(@Nullable HttpStatus status) { }
|
||||
|
||||
@Nullable
|
||||
public HttpStatus getStatus() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
public ModelAndView addObject(String attributeName, @Nullable Object attributeValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public ModelAndView addObject(Object attributeValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public ModelAndView addAllObjects(@Nullable Map<String, ?> modelMap) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void clear() { }
|
||||
|
||||
public boolean isEmpty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean wasCleared() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "";
|
||||
}
|
||||
|
||||
private String formatView() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.springframework.web.servlet;
|
||||
|
||||
import java.util.Map;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
public interface View {
|
||||
String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";
|
||||
String PATH_VARIABLES = View.class.getName() + ".pathVariables";
|
||||
String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType";
|
||||
|
||||
@Nullable
|
||||
default String getContentType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
void render(@Nullable Map<String, ?> var1, HttpServletRequest var2, HttpServletResponse var3) throws Exception;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.springframework.web.servlet.view;
|
||||
|
||||
import java.util.Locale;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
public abstract class AbstractUrlBasedView {
|
||||
@Nullable
|
||||
private String url;
|
||||
|
||||
protected AbstractUrlBasedView() { }
|
||||
|
||||
protected AbstractUrlBasedView(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public void setUrl(@Nullable String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getUrl() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception { }
|
||||
|
||||
protected boolean isUrlRequired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean checkResource(Locale locale) throws Exception {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
package org.springframework.web.servlet.view;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
public class RedirectView extends AbstractUrlBasedView {
|
||||
private static final Pattern URI_TEMPLATE_VARIABLE_PATTERN = Pattern.compile("\\{([^/]+?)\\}");
|
||||
private boolean contextRelative = false;
|
||||
private boolean http10Compatible = true;
|
||||
private boolean exposeModelAttributes = true;
|
||||
@Nullable
|
||||
private String encodingScheme;
|
||||
@Nullable
|
||||
private HttpStatus statusCode;
|
||||
private boolean expandUriTemplateVariables = true;
|
||||
private boolean propagateQueryParams = false;
|
||||
@Nullable
|
||||
private String[] hosts;
|
||||
|
||||
public RedirectView() { }
|
||||
|
||||
public RedirectView(String url) { }
|
||||
|
||||
public RedirectView(String url, boolean contextRelative) { }
|
||||
|
||||
public RedirectView(String url, boolean contextRelative, boolean http10Compatible) { }
|
||||
|
||||
public RedirectView(String url, boolean contextRelative, boolean http10Compatible, boolean exposeModelAttributes) { }
|
||||
|
||||
public void setContextRelative(boolean contextRelative) { }
|
||||
|
||||
public void setHttp10Compatible(boolean http10Compatible) { }
|
||||
|
||||
public void setExposeModelAttributes(boolean exposeModelAttributes) { }
|
||||
|
||||
public void setEncodingScheme(String encodingScheme) { }
|
||||
|
||||
public void setStatusCode(HttpStatus statusCode) { }
|
||||
|
||||
public void setExpandUriTemplateVariables(boolean expandUriTemplateVariables) { }
|
||||
|
||||
public void setPropagateQueryParams(boolean propagateQueryParams) { }
|
||||
|
||||
public boolean isPropagateQueryProperties() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setHosts(@Nullable String... hosts) { }
|
||||
|
||||
@Nullable
|
||||
public String[] getHosts() {
|
||||
return this.hosts;
|
||||
}
|
||||
|
||||
public boolean isRedirectView() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean isContextRequired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws IOException { }
|
||||
|
||||
protected final String createTargetUrl(Map<String, Object> model, HttpServletRequest request) throws UnsupportedEncodingException {
|
||||
return "";
|
||||
}
|
||||
|
||||
private String getContextPath(HttpServletRequest request) {
|
||||
return "";
|
||||
}
|
||||
|
||||
protected StringBuilder replaceUriTemplateVariables(String targetUrl, Map<String, Object> model, Map<String, String> currentUriVariables, String encodingScheme) throws UnsupportedEncodingException {
|
||||
return null;
|
||||
}
|
||||
|
||||
private Map<String, String> getCurrentRequestUriVariables(HttpServletRequest request) {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void appendCurrentQueryParams(StringBuilder targetUrl, HttpServletRequest request) { }
|
||||
|
||||
protected void appendQueryProperties(StringBuilder targetUrl, Map<String, Object> model, String encodingScheme) throws UnsupportedEncodingException { }
|
||||
|
||||
protected Map<String, Object> queryProperties(Map<String, Object> model) {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected boolean isEligibleProperty(String key, @Nullable Object value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean isEligibleValue(@Nullable Object value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected String urlEncode(String input, String encodingScheme) throws UnsupportedEncodingException {
|
||||
return "";
|
||||
}
|
||||
|
||||
protected String updateTargetUrl(String targetUrl, Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) {
|
||||
return "";
|
||||
}
|
||||
|
||||
protected void sendRedirect(HttpServletRequest request, HttpServletResponse response, String targetUrl, boolean http10Compatible) throws IOException { }
|
||||
|
||||
protected boolean isRemoteHost(String targetUrl) {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected HttpStatus getHttp11StatusCode(HttpServletRequest request, HttpServletResponse response, String targetUrl) {
|
||||
return this.statusCode;
|
||||
}
|
||||
}
|
||||
|
||||
4
javascript/change-notes/2021-05-18-clone.md
Normal file
4
javascript/change-notes/2021-05-18-clone.md
Normal file
@@ -0,0 +1,4 @@
|
||||
lgtm,codescanning
|
||||
* The dataflow libraries now model dataflow in the `clone` library.
|
||||
Affected packages are
|
||||
[clone](https://npmjs.com/package/clone)
|
||||
@@ -4,6 +4,7 @@
|
||||
* @description The total number of lines of JavaScript or TypeScript code across all files checked into the repository, except in `node_modules`. This is a useful metric of the size of a database. For all files that were seen during extraction, this query counts the lines of code, excluding whitespace or comments.
|
||||
* @kind metric
|
||||
* @tags summary
|
||||
* lines-of-code
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
@@ -183,6 +183,11 @@ module API {
|
||||
*/
|
||||
Node getPromised() { result = getASuccessor(Label::promised()) }
|
||||
|
||||
/**
|
||||
* Gets a node representing the error wrapped in the `Promise` object represented by this node.
|
||||
*/
|
||||
Node getPromisedError() { result = getASuccessor(Label::promisedError()) }
|
||||
|
||||
/**
|
||||
* Gets a string representation of the lexicographically least among all shortest access paths
|
||||
* from the root to this node.
|
||||
@@ -468,6 +473,9 @@ module API {
|
||||
or
|
||||
lbl = Label::promised() and
|
||||
PromiseFlow::storeStep(rhs, pred, Promises::valueProp())
|
||||
or
|
||||
lbl = Label::promisedError() and
|
||||
PromiseFlow::storeStep(rhs, pred, Promises::errorProp())
|
||||
)
|
||||
or
|
||||
exists(DataFlow::ClassNode cls, string name |
|
||||
@@ -482,6 +490,12 @@ module API {
|
||||
rhs = f.getAReturn()
|
||||
)
|
||||
or
|
||||
exists(DataFlow::FunctionNode f |
|
||||
base = MkAsyncFuncResult(f) and
|
||||
lbl = Label::promisedError() and
|
||||
rhs = f.getExceptionalReturn()
|
||||
)
|
||||
or
|
||||
exists(int i |
|
||||
lbl = Label::parameter(i) and
|
||||
argumentPassing(base, i, rhs)
|
||||
@@ -559,6 +573,9 @@ module API {
|
||||
or
|
||||
lbl = Label::promised() and
|
||||
PromiseFlow::loadStep(pred, ref, Promises::valueProp())
|
||||
or
|
||||
lbl = Label::promisedError() and
|
||||
PromiseFlow::loadStep(pred, ref, Promises::errorProp())
|
||||
)
|
||||
or
|
||||
exists(DataFlow::Node def, DataFlow::FunctionNode fn |
|
||||
@@ -962,6 +979,9 @@ private module Label {
|
||||
|
||||
/** Gets the `promised` edge label connecting a promise to its contained value. */
|
||||
string promised() { result = "promised" }
|
||||
|
||||
/** Gets the `promisedError` edge label connecting a promise to its rejected value. */
|
||||
string promisedError() { result = "promisedError" }
|
||||
}
|
||||
|
||||
private class NodeModuleSourcesNodes extends DataFlow::SourceNode::Range {
|
||||
|
||||
@@ -174,3 +174,17 @@ private class ExtendCallTaintStep extends TaintTracking::SharedTaintStep {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private import semmle.javascript.dataflow.internal.PreCallGraphStep
|
||||
|
||||
/**
|
||||
* A step for the `clone` package.
|
||||
*/
|
||||
private class CloneStep extends PreCallGraphStep {
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(DataFlow::CallNode call | call = DataFlow::moduleImport("clone").getACall() |
|
||||
pred = call.getArgument(0) and
|
||||
succ = call
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -725,7 +725,7 @@ class TypeAccess extends @typeaccess, TypeExpr, TypeRef {
|
||||
spec.getImportedName() = exportedName and
|
||||
this = spec.getLocal().(TypeDecl).getLocalTypeName().getAnAccess()
|
||||
or
|
||||
spec instanceof ImportNamespaceSpecifier and
|
||||
(spec instanceof ImportNamespaceSpecifier or spec instanceof ImportDefaultSpecifier) and
|
||||
this =
|
||||
spec.getLocal().(LocalNamespaceDecl).getLocalNamespaceName().getAMemberAccess(exportedName)
|
||||
)
|
||||
|
||||
@@ -239,7 +239,6 @@ module DataFlow {
|
||||
private TypeAnnotation getFallbackTypeAnnotation() {
|
||||
exists(BindingPattern pattern |
|
||||
this = valueNode(pattern) and
|
||||
not ast_node_type(pattern, _) and
|
||||
result = pattern.getTypeAnnotation()
|
||||
)
|
||||
or
|
||||
|
||||
@@ -206,19 +206,14 @@ module ClientRequest {
|
||||
/**
|
||||
* A model of a URL request made using the `axios` library.
|
||||
*/
|
||||
class AxiosUrlRequest extends ClientRequest::Range {
|
||||
class AxiosUrlRequest extends ClientRequest::Range, API::CallNode {
|
||||
string method;
|
||||
|
||||
AxiosUrlRequest() {
|
||||
exists(string moduleName, DataFlow::SourceNode callee | this = callee.getACall() |
|
||||
moduleName = "axios" and
|
||||
(
|
||||
callee = DataFlow::moduleImport(moduleName) and method = "request"
|
||||
or
|
||||
callee = DataFlow::moduleMember(moduleName, method) and
|
||||
(method = httpMethodName() or method = "request")
|
||||
)
|
||||
)
|
||||
this = API::moduleImport("axios").getACall() and method = "request"
|
||||
or
|
||||
this = API::moduleImport("axios").getMember(method).getACall() and
|
||||
method = [httpMethodName(), "request"]
|
||||
}
|
||||
|
||||
private int getOptionsArgIndex() {
|
||||
@@ -247,12 +242,10 @@ module ClientRequest {
|
||||
method = "request" and
|
||||
result = getOptionArgument(0, "data")
|
||||
or
|
||||
(method = "post" or method = "put" or method = "put") and
|
||||
(result = getArgument(1) or result = getOptionArgument(2, "data"))
|
||||
method = ["post", "put"] and
|
||||
result = [getArgument(1), getOptionArgument(2, "data")]
|
||||
or
|
||||
exists(string name | name = "headers" or name = "params" |
|
||||
result = getOptionArgument([0 .. 2], name)
|
||||
)
|
||||
result = getOptionArgument([0 .. 2], ["headers", "params"])
|
||||
}
|
||||
|
||||
/** Gets the response type from the options passed in. */
|
||||
@@ -275,6 +268,10 @@ module ClientRequest {
|
||||
responseType = getResponseType() and
|
||||
promise = true and
|
||||
result = this
|
||||
or
|
||||
responseType = getResponseType() and
|
||||
promise = false and
|
||||
result = getReturn().getPromisedError().getMember("response").getAnImmediateUse()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,9 @@ module Express {
|
||||
or
|
||||
// `app = express.createServer()`
|
||||
result = DataFlow::moduleMember("express", "createServer").getAnInvocation()
|
||||
or
|
||||
// `app = express().disable(x)`, and other chaining methods
|
||||
result = appCreation().getAMemberCall(["engine", "set", "param", "enable", "disable", "on"])
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -58,7 +58,8 @@ module HeuristicNames {
|
||||
*/
|
||||
string maybeAccountInfo() {
|
||||
result = "(?is).*acc(ou)?nt.*" or
|
||||
result = "(?is).*(puid|username|userid).*"
|
||||
result = "(?is).*(puid|username|userid).*" or
|
||||
result = "(?s).*([uU]|^|_|[a-z](?=U))([uU][iI][dD]).*"
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user