mirror of
https://github.com/github/codeql.git
synced 2026-04-29 10:45:15 +02:00
Merge branch 'master' into get-an-assigned-value-join-order
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -21,4 +21,3 @@
|
||||
/codeql/
|
||||
|
||||
csharp/extractor/Semmle.Extraction.CSharp.Driver/Properties/launchSettings.json
|
||||
.vscode
|
||||
|
||||
27
.vscode/tasks.json
vendored
Normal file
27
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
// To run a task, select the `Terminal | Run Task...` menu option, and then select the task from
|
||||
// the list in the dropdown, or invoke the `Tasks: Run Task` command from the command palette/
|
||||
// To bind a keyboard shortcut to invoke a task, see https://code.visualstudio.com/docs/editor/tasks#_binding-keyboard-shortcuts-to-tasks.
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Sync Identical Files",
|
||||
"type": "process",
|
||||
// Non-Windows OS will usually have Python 3 already installed at /usr/bin/python3.
|
||||
"command": "python3",
|
||||
"args": [
|
||||
"config/sync-files.py",
|
||||
"--latest"
|
||||
],
|
||||
"group": "build",
|
||||
"windows": {
|
||||
// On Windows, use whatever Python interpreter is configured for this workspace. The default is
|
||||
// just `python`, so if Python is already on the path, this will find it.
|
||||
"command": "${config:python.pythonPath}",
|
||||
},
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
12
README.md
12
README.md
@@ -14,3 +14,15 @@ We welcome contributions to our standard library and standard checks. Do you hav
|
||||
## License
|
||||
|
||||
The code in this repository is licensed under the [MIT License](LICENSE) by [GitHub](https://github.com).
|
||||
|
||||
## Visual Studio Code integration
|
||||
|
||||
If you use Visual Studio Code to work in this repository, there are a few integration features to make development easier.
|
||||
|
||||
### CodeQL for Visual Studio Code
|
||||
|
||||
You can install the [CodeQL for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-codeql) extension to get syntax highlighting, IntelliSense, and code navigation for the QL language, as well as unit test support for testing CodeQL libraries and queries.
|
||||
|
||||
### Tasks
|
||||
|
||||
The `.vscode/tasks.json` file defines custom tasks specific to working in this repository. To invoke one of these tasks, select the `Terminal | Run Task...` menu option, and then select the desired task from the dropdown. You can also invoke the `Tasks: Run Task` command from the command palette.
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
* Support for the following frameworks and libraries has been improved:
|
||||
- [jGrowl](https://github.com/stanlemon/jGrowl)
|
||||
- [jQuery](https://jquery.com/)
|
||||
|
||||
## New queries
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ def choose_latest_file(files):
|
||||
|
||||
local_error_count = 0
|
||||
def emit_local_error(path, line, error):
|
||||
print('ERROR: ' + path + ':' + line + " - " + error)
|
||||
print('ERROR: ' + path + ':' + str(line) + " - " + error)
|
||||
global local_error_count
|
||||
local_error_count += 1
|
||||
|
||||
|
||||
@@ -2293,12 +2293,13 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
|
||||
exists(
|
||||
AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing,
|
||||
LocalCallContext localCC
|
||||
|
|
||||
pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and
|
||||
localCC = getLocalCallContext(cc, enclosing)
|
||||
exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
||||
ap = ap0
|
||||
@@ -2331,20 +2332,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoLocalStep(
|
||||
PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc,
|
||||
AccessPath ap0, Configuration conf
|
||||
) {
|
||||
midnode = mid.getNode() and
|
||||
cc = mid.getCallContext() and
|
||||
conf = mid.getConfiguration() and
|
||||
localFlowBigStep(midnode, _, _, _, conf, _) and
|
||||
enclosing = midnode.getEnclosingCallable() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
ap0 = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
readDirect(node1, f, node2) and
|
||||
|
||||
@@ -2293,12 +2293,13 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
|
||||
exists(
|
||||
AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing,
|
||||
LocalCallContext localCC
|
||||
|
|
||||
pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and
|
||||
localCC = getLocalCallContext(cc, enclosing)
|
||||
exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
||||
ap = ap0
|
||||
@@ -2331,20 +2332,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoLocalStep(
|
||||
PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc,
|
||||
AccessPath ap0, Configuration conf
|
||||
) {
|
||||
midnode = mid.getNode() and
|
||||
cc = mid.getCallContext() and
|
||||
conf = mid.getConfiguration() and
|
||||
localFlowBigStep(midnode, _, _, _, conf, _) and
|
||||
enclosing = midnode.getEnclosingCallable() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
ap0 = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
readDirect(node1, f, node2) and
|
||||
|
||||
@@ -2293,12 +2293,13 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
|
||||
exists(
|
||||
AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing,
|
||||
LocalCallContext localCC
|
||||
|
|
||||
pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and
|
||||
localCC = getLocalCallContext(cc, enclosing)
|
||||
exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
||||
ap = ap0
|
||||
@@ -2331,20 +2332,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoLocalStep(
|
||||
PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc,
|
||||
AccessPath ap0, Configuration conf
|
||||
) {
|
||||
midnode = mid.getNode() and
|
||||
cc = mid.getCallContext() and
|
||||
conf = mid.getConfiguration() and
|
||||
localFlowBigStep(midnode, _, _, _, conf, _) and
|
||||
enclosing = midnode.getEnclosingCallable() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
ap0 = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
readDirect(node1, f, node2) and
|
||||
|
||||
@@ -2293,12 +2293,13 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
|
||||
exists(
|
||||
AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing,
|
||||
LocalCallContext localCC
|
||||
|
|
||||
pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and
|
||||
localCC = getLocalCallContext(cc, enclosing)
|
||||
exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
||||
ap = ap0
|
||||
@@ -2331,20 +2332,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoLocalStep(
|
||||
PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc,
|
||||
AccessPath ap0, Configuration conf
|
||||
) {
|
||||
midnode = mid.getNode() and
|
||||
cc = mid.getCallContext() and
|
||||
conf = mid.getConfiguration() and
|
||||
localFlowBigStep(midnode, _, _, _, conf, _) and
|
||||
enclosing = midnode.getEnclosingCallable() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
ap0 = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
readDirect(node1, f, node2) and
|
||||
|
||||
@@ -2293,12 +2293,13 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
|
||||
exists(
|
||||
AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing,
|
||||
LocalCallContext localCC
|
||||
|
|
||||
pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and
|
||||
localCC = getLocalCallContext(cc, enclosing)
|
||||
exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
||||
ap = ap0
|
||||
@@ -2331,20 +2332,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoLocalStep(
|
||||
PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc,
|
||||
AccessPath ap0, Configuration conf
|
||||
) {
|
||||
midnode = mid.getNode() and
|
||||
cc = mid.getCallContext() and
|
||||
conf = mid.getConfiguration() and
|
||||
localFlowBigStep(midnode, _, _, _, conf, _) and
|
||||
enclosing = midnode.getEnclosingCallable() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
ap0 = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
readDirect(node1, f, node2) and
|
||||
|
||||
@@ -43,7 +43,7 @@ class Node extends TNode {
|
||||
/**
|
||||
* INTERNAL: Do not use. Alternative name for `getFunction`.
|
||||
*/
|
||||
Function getEnclosingCallable() { result = this.getFunction() }
|
||||
final Function getEnclosingCallable() { result = unique(Function f | f = this.getFunction() | f) }
|
||||
|
||||
/** Gets the type of this node. */
|
||||
Type getType() { none() } // overridden in subclasses
|
||||
|
||||
@@ -2293,12 +2293,13 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
|
||||
exists(
|
||||
AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing,
|
||||
LocalCallContext localCC
|
||||
|
|
||||
pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and
|
||||
localCC = getLocalCallContext(cc, enclosing)
|
||||
exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
||||
ap = ap0
|
||||
@@ -2331,20 +2332,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoLocalStep(
|
||||
PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc,
|
||||
AccessPath ap0, Configuration conf
|
||||
) {
|
||||
midnode = mid.getNode() and
|
||||
cc = mid.getCallContext() and
|
||||
conf = mid.getConfiguration() and
|
||||
localFlowBigStep(midnode, _, _, _, conf, _) and
|
||||
enclosing = midnode.getEnclosingCallable() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
ap0 = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
readDirect(node1, f, node2) and
|
||||
|
||||
@@ -2293,12 +2293,13 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
|
||||
exists(
|
||||
AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing,
|
||||
LocalCallContext localCC
|
||||
|
|
||||
pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and
|
||||
localCC = getLocalCallContext(cc, enclosing)
|
||||
exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
||||
ap = ap0
|
||||
@@ -2331,20 +2332,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoLocalStep(
|
||||
PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc,
|
||||
AccessPath ap0, Configuration conf
|
||||
) {
|
||||
midnode = mid.getNode() and
|
||||
cc = mid.getCallContext() and
|
||||
conf = mid.getConfiguration() and
|
||||
localFlowBigStep(midnode, _, _, _, conf, _) and
|
||||
enclosing = midnode.getEnclosingCallable() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
ap0 = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
readDirect(node1, f, node2) and
|
||||
|
||||
@@ -2293,12 +2293,13 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
|
||||
exists(
|
||||
AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing,
|
||||
LocalCallContext localCC
|
||||
|
|
||||
pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and
|
||||
localCC = getLocalCallContext(cc, enclosing)
|
||||
exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
||||
ap = ap0
|
||||
@@ -2331,20 +2332,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoLocalStep(
|
||||
PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc,
|
||||
AccessPath ap0, Configuration conf
|
||||
) {
|
||||
midnode = mid.getNode() and
|
||||
cc = mid.getCallContext() and
|
||||
conf = mid.getConfiguration() and
|
||||
localFlowBigStep(midnode, _, _, _, conf, _) and
|
||||
enclosing = midnode.getEnclosingCallable() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
ap0 = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
readDirect(node1, f, node2) and
|
||||
|
||||
@@ -2293,12 +2293,13 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
|
||||
exists(
|
||||
AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing,
|
||||
LocalCallContext localCC
|
||||
|
|
||||
pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and
|
||||
localCC = getLocalCallContext(cc, enclosing)
|
||||
exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
||||
ap = ap0
|
||||
@@ -2331,20 +2332,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoLocalStep(
|
||||
PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc,
|
||||
AccessPath ap0, Configuration conf
|
||||
) {
|
||||
midnode = mid.getNode() and
|
||||
cc = mid.getCallContext() and
|
||||
conf = mid.getConfiguration() and
|
||||
localFlowBigStep(midnode, _, _, _, conf, _) and
|
||||
enclosing = midnode.getEnclosingCallable() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
ap0 = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
readDirect(node1, f, node2) and
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Provides predicates for mapping the `FunctionInput` and `FunctionOutput`
|
||||
* classes used in function models to the corresponding instructions.
|
||||
*/
|
||||
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
|
||||
/**
|
||||
* Gets the instruction that goes into `input` for `call`.
|
||||
*/
|
||||
Instruction callInput(CallInstruction call, FunctionInput input) {
|
||||
// A positional argument
|
||||
exists(int index |
|
||||
result = call.getPositionalArgument(index) and
|
||||
input.isParameter(index)
|
||||
)
|
||||
or
|
||||
// A value pointed to by a positional argument
|
||||
exists(ReadSideEffectInstruction read |
|
||||
result = read and
|
||||
read.getPrimaryInstruction() = call and
|
||||
input.isParameterDeref(read.getIndex())
|
||||
)
|
||||
or
|
||||
// The qualifier pointer
|
||||
result = call.getThisArgument() and
|
||||
input.isQualifierAddress()
|
||||
//TODO: qualifier deref
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instruction that holds the `output` for `call`.
|
||||
*/
|
||||
Instruction callOutput(CallInstruction call, FunctionOutput output) {
|
||||
// The return value
|
||||
result = call and
|
||||
output.isReturnValue()
|
||||
or
|
||||
// The side effect of a call on the value pointed to by a positional argument
|
||||
exists(WriteSideEffectInstruction effect |
|
||||
result = effect and
|
||||
effect.getPrimaryInstruction() = call and
|
||||
output.isParameterDeref(effect.getIndex())
|
||||
)
|
||||
// TODO: qualifiers, return value dereference
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import ModelUtil
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
|
||||
@@ -45,6 +48,25 @@ private predicate localInstructionTaintStep(Instruction nodeFrom, Instruction no
|
||||
)
|
||||
or
|
||||
nodeTo.(LoadInstruction).getSourceAddress() = nodeFrom
|
||||
or
|
||||
modeledInstructionTaintStep(nodeFrom, nodeTo)
|
||||
or
|
||||
// Flow through partial reads of arrays and unions
|
||||
nodeTo.(LoadInstruction).getSourceValueOperand().getAnyDef() = nodeFrom and
|
||||
not nodeFrom.isResultConflated() and
|
||||
(
|
||||
nodeFrom.getResultType() instanceof ArrayType or
|
||||
nodeFrom.getResultType() instanceof Union
|
||||
)
|
||||
or
|
||||
// Flow from an element to an array or union that contains it.
|
||||
nodeTo.(ChiInstruction).getPartial() = nodeFrom and
|
||||
not nodeTo.isResultConflated() and
|
||||
exists(Type t | nodeTo.getResultLanguageType().hasType(t, false) |
|
||||
t instanceof Union
|
||||
or
|
||||
t instanceof ArrayType
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,3 +104,34 @@ predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
|
||||
* but not in local taint.
|
||||
*/
|
||||
predicate defaultTaintBarrier(DataFlow::Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if taint can flow from `instrIn` to `instrOut` through a call to a
|
||||
* modeled function.
|
||||
*/
|
||||
predicate modeledInstructionTaintStep(Instruction instrIn, Instruction instrOut) {
|
||||
exists(CallInstruction call, TaintFunction func, FunctionInput modelIn, FunctionOutput modelOut |
|
||||
instrIn = callInput(call, modelIn) and
|
||||
instrOut = callOutput(call, modelOut) and
|
||||
call.getStaticCallTarget() = func and
|
||||
func.hasTaintFlow(modelIn, modelOut)
|
||||
)
|
||||
or
|
||||
// Taint flow from one argument to another and data flow from an argument to a
|
||||
// return value. This happens in functions like `strcat` and `memcpy`. We
|
||||
// could model this flow in two separate steps, but that would add reverse
|
||||
// flow from the write side-effect to the call instruction, which may not be
|
||||
// desirable.
|
||||
exists(
|
||||
CallInstruction call, Function func, FunctionInput modelIn, OutParameterDeref modelMidOut,
|
||||
int indexMid, InParameter modelMidIn, OutReturnValue modelOut
|
||||
|
|
||||
instrIn = callInput(call, modelIn) and
|
||||
instrOut = callOutput(call, modelOut) and
|
||||
call.getStaticCallTarget() = func and
|
||||
func.(TaintFunction).hasTaintFlow(modelIn, modelMidOut) and
|
||||
func.(DataFlowFunction).hasDataFlow(modelMidIn, modelOut) and
|
||||
modelMidOut.isParameterDeref(indexMid) and
|
||||
modelMidIn.isParameter(indexMid)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -89,6 +89,18 @@ class MallocAllocationFunction extends AllocationFunction {
|
||||
or
|
||||
// kmem_zalloc(size, flags)
|
||||
name = "kmem_zalloc" and sizeArg = 0
|
||||
or
|
||||
// CRYPTO_malloc(size_t num, const char *file, int line)
|
||||
name = "CRYPTO_malloc" and sizeArg = 0
|
||||
or
|
||||
// CRYPTO_zalloc(size_t num, const char *file, int line)
|
||||
name = "CRYPTO_zalloc" and sizeArg = 0
|
||||
or
|
||||
// CRYPTO_secure_malloc(size_t num, const char *file, int line)
|
||||
name = "CRYPTO_secure_malloc" and sizeArg = 0
|
||||
or
|
||||
// CRYPTO_secure_zalloc(size_t num, const char *file, int line)
|
||||
name = "CRYPTO_secure_zalloc" and sizeArg = 0
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -169,6 +181,9 @@ class ReallocAllocationFunction extends AllocationFunction {
|
||||
or
|
||||
// CoTaskMemRealloc(ptr, size)
|
||||
name = "CoTaskMemRealloc" and sizeArg = 1 and reallocArg = 0
|
||||
or
|
||||
// CRYPTO_realloc(void *addr, size_t num, const char *file, int line);
|
||||
name = "CRYPTO_realloc" and sizeArg = 1 and reallocArg = 0
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -255,6 +270,36 @@ class OperatorNewAllocationFunction extends AllocationFunction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The predicate analyzes a `sizeExpr`, which is an argument to an allocation
|
||||
* function like malloc, and tries to split it into an expression `lengthExpr`
|
||||
* that describes the length of the allocated array, and the size of the allocated
|
||||
* element type `sizeof`.
|
||||
* If this is not possible, the allocation is considered to be of size 1 and of
|
||||
* length `sizeExpr`.
|
||||
*/
|
||||
private predicate deconstructSizeExpr(Expr sizeExpr, Expr lengthExpr, int sizeof) {
|
||||
if
|
||||
sizeExpr instanceof MulExpr and
|
||||
exists(SizeofOperator sizeofOp, Expr lengthOp |
|
||||
sizeofOp = sizeExpr.(MulExpr).getAnOperand() and
|
||||
lengthOp = sizeExpr.(MulExpr).getAnOperand() and
|
||||
not lengthOp instanceof SizeofOperator and
|
||||
exists(sizeofOp.getValue().toInt())
|
||||
)
|
||||
then
|
||||
exists(SizeofOperator sizeofOp |
|
||||
sizeofOp = sizeExpr.(MulExpr).getAnOperand() and
|
||||
lengthExpr = sizeExpr.(MulExpr).getAnOperand() and
|
||||
not lengthExpr instanceof SizeofOperator and
|
||||
sizeof = sizeofOp.getValue().toInt()
|
||||
)
|
||||
else (
|
||||
lengthExpr = sizeExpr and
|
||||
sizeof = 1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An allocation expression that is a function call, such as call to `malloc`.
|
||||
*/
|
||||
@@ -272,7 +317,17 @@ class CallAllocationExpr extends AllocationExpr, FunctionCall {
|
||||
not exists(NewOrNewArrayExpr new | new.getAllocatorCall() = this)
|
||||
}
|
||||
|
||||
override Expr getSizeExpr() { result = getArgument(target.getSizeArg()) }
|
||||
override Expr getSizeExpr() {
|
||||
exists(Expr sizeExpr | sizeExpr = getArgument(target.getSizeArg()) |
|
||||
if exists(target.getSizeMult())
|
||||
then result = sizeExpr
|
||||
else
|
||||
exists(Expr lengthExpr |
|
||||
deconstructSizeExpr(sizeExpr, lengthExpr, _) and
|
||||
result = lengthExpr
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override int getSizeMult() {
|
||||
// malloc with multiplier argument that is a constant
|
||||
@@ -280,7 +335,7 @@ class CallAllocationExpr extends AllocationExpr, FunctionCall {
|
||||
or
|
||||
// malloc with no multiplier argument
|
||||
not exists(target.getSizeMult()) and
|
||||
result = 1
|
||||
deconstructSizeExpr(getArgument(target.getSizeArg()), _, result)
|
||||
}
|
||||
|
||||
override int getSizeBytes() { result = getSizeExpr().getValue().toInt() * getSizeMult() }
|
||||
|
||||
@@ -19,6 +19,10 @@ class StandardDeallocationFunction extends DeallocationFunction {
|
||||
name = "free" and freedArg = 0
|
||||
or
|
||||
name = "realloc" and freedArg = 0
|
||||
or
|
||||
name = "CRYPTO_free" and freedArg = 0
|
||||
or
|
||||
name = "CRYPTO_secure_free" and freedArg = 0
|
||||
)
|
||||
or
|
||||
hasGlobalOrStdName(name) and
|
||||
|
||||
@@ -149,3 +149,15 @@ void directOperatorCall() {
|
||||
ptr = operator new(sizeof(int));
|
||||
operator delete(ptr);
|
||||
}
|
||||
|
||||
void *malloc(size_t);
|
||||
|
||||
void testMalloc(size_t count) {
|
||||
malloc(5);
|
||||
malloc(5 * sizeof(int));
|
||||
malloc(count);
|
||||
malloc(count * sizeof(int));
|
||||
malloc(count * sizeof(int) + 1);
|
||||
malloc(((int) count) * sizeof(void *));
|
||||
malloc(sizeof(void*) * sizeof(int));
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ allocationFunctions
|
||||
| allocators.cpp:122:7:122:20 | operator new[] | getPlacementArgument = 1, getSizeArg = 0 |
|
||||
| allocators.cpp:123:7:123:18 | operator new | getSizeArg = 0, requiresDealloc |
|
||||
| allocators.cpp:124:7:124:20 | operator new[] | getSizeArg = 0, requiresDealloc |
|
||||
| allocators.cpp:153:7:153:12 | malloc | getSizeArg = 0, requiresDealloc |
|
||||
| file://:0:0:0:0 | operator new | getSizeArg = 0, requiresDealloc |
|
||||
| file://:0:0:0:0 | operator new | getSizeArg = 0, requiresDealloc |
|
||||
| file://:0:0:0:0 | operator new[] | getSizeArg = 0, requiresDealloc |
|
||||
@@ -84,6 +85,13 @@ allocationExprs
|
||||
| allocators.cpp:143:13:143:28 | new[] | getSizeBytes = 400, requiresDealloc |
|
||||
| allocators.cpp:144:13:144:31 | new[] | getSizeExpr = x, getSizeMult = 900, requiresDealloc |
|
||||
| allocators.cpp:149:8:149:19 | call to operator new | getSizeBytes = 4, getSizeExpr = sizeof(int), getSizeMult = 1, requiresDealloc |
|
||||
| allocators.cpp:156:3:156:8 | call to malloc | getSizeBytes = 5, getSizeExpr = 5, getSizeMult = 1, requiresDealloc |
|
||||
| allocators.cpp:157:3:157:8 | call to malloc | getSizeBytes = 20, getSizeExpr = 5, getSizeMult = 4, requiresDealloc |
|
||||
| allocators.cpp:158:3:158:8 | call to malloc | getSizeExpr = count, getSizeMult = 1, requiresDealloc |
|
||||
| allocators.cpp:159:3:159:8 | call to malloc | getSizeExpr = count, getSizeMult = 4, requiresDealloc |
|
||||
| allocators.cpp:160:3:160:8 | call to malloc | getSizeExpr = ... + ..., getSizeMult = 1, requiresDealloc |
|
||||
| allocators.cpp:161:3:161:8 | call to malloc | getSizeExpr = count, getSizeMult = 8, requiresDealloc |
|
||||
| allocators.cpp:162:3:162:8 | call to malloc | getSizeBytes = 32, getSizeExpr = ... * ..., getSizeMult = 1, requiresDealloc |
|
||||
deallocationFunctions
|
||||
| allocators.cpp:11:6:11:20 | operator delete | getFreedArg = 0 |
|
||||
| allocators.cpp:12:6:12:22 | operator delete[] | getFreedArg = 0 |
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
| taint.cpp:93:11:93:11 | taint.cpp:71:22:71:27 | AST only |
|
||||
| taint.cpp:94:11:94:11 | taint.cpp:72:7:72:12 | AST only |
|
||||
| taint.cpp:109:7:109:13 | taint.cpp:105:12:105:17 | IR only |
|
||||
| taint.cpp:110:7:110:13 | taint.cpp:105:12:105:17 | IR only |
|
||||
| taint.cpp:111:7:111:13 | taint.cpp:106:12:106:17 | IR only |
|
||||
| taint.cpp:112:7:112:13 | taint.cpp:106:12:106:17 | IR only |
|
||||
| taint.cpp:130:7:130:9 | taint.cpp:127:8:127:13 | IR only |
|
||||
| taint.cpp:137:7:137:9 | taint.cpp:120:11:120:16 | AST only |
|
||||
| taint.cpp:173:8:173:13 | taint.cpp:164:19:164:24 | AST only |
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
| taint.cpp:16:8:16:14 | source1 | taint.cpp:12:22:12:27 | call to source |
|
||||
| taint.cpp:17:8:17:16 | ++ ... | taint.cpp:12:22:12:27 | call to source |
|
||||
| taint.cpp:109:7:109:13 | access to array | taint.cpp:105:12:105:17 | call to source |
|
||||
| taint.cpp:110:7:110:13 | access to array | taint.cpp:105:12:105:17 | call to source |
|
||||
| taint.cpp:111:7:111:13 | access to array | taint.cpp:106:12:106:17 | call to source |
|
||||
| taint.cpp:112:7:112:13 | access to array | taint.cpp:106:12:106:17 | call to source |
|
||||
| taint.cpp:129:7:129:9 | * ... | taint.cpp:120:11:120:16 | call to source |
|
||||
| taint.cpp:130:7:130:9 | * ... | taint.cpp:127:8:127:13 | call to source |
|
||||
| taint.cpp:134:7:134:9 | * ... | taint.cpp:120:11:120:16 | call to source |
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
| tests1.cpp:26:21:26:26 | call to malloc | This allocation does not include space to null-terminate the string. |
|
||||
| tests1.cpp:36:21:36:26 | call to malloc | This allocation does not include space to null-terminate the string. |
|
||||
| tests1.cpp:56:21:56:27 | call to realloc | This allocation does not include space to null-terminate the string. |
|
||||
| tests1.cpp:67:21:67:26 | call to malloc | This allocation does not include space to null-terminate the string. |
|
||||
| tests1.cpp:89:25:89:30 | call to malloc | This allocation does not include space to null-terminate the string. |
|
||||
| tests1.cpp:109:25:109:30 | call to malloc | This allocation does not include space to null-terminate the string. |
|
||||
| tests3.cpp:25:21:25:31 | call to malloc | This allocation does not include space to null-terminate the string. |
|
||||
| tests3.cpp:30:21:30:31 | call to malloc | This allocation does not include space to null-terminate the string. |
|
||||
| tests3.cpp:53:17:53:44 | new[] | This allocation does not include space to null-terminate the string. |
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
| tests2.cpp:34:4:34:9 | call to strcat | This buffer only contains enough room for 'str1' (copied on line 33) |
|
||||
| tests2.cpp:52:4:52:9 | call to strcat | This buffer only contains enough room for 'str1' (copied on line 51) |
|
||||
|
||||
@@ -33,7 +33,7 @@ void tests1(int case_num)
|
||||
break;
|
||||
|
||||
case 3:
|
||||
buffer = (char *)malloc(strlen(str) * sizeof(char)); // BAD [NOT DETECTED]
|
||||
buffer = (char *)malloc(strlen(str) * sizeof(char)); // BAD
|
||||
strcpy(buffer, str);
|
||||
break;
|
||||
|
||||
@@ -106,7 +106,7 @@ void tests1(int case_num)
|
||||
break;
|
||||
|
||||
case 105:
|
||||
wbuffer = (wchar_t *)malloc(wcslen(wstr) * sizeof(wchar_t)); // BAD [NOT DETECTED]
|
||||
wbuffer = (wchar_t *)malloc(wcslen(wstr) * sizeof(wchar_t)); // BAD
|
||||
wcscpy(wbuffer, wstr);
|
||||
break;
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ void tests2(int case_num)
|
||||
break;
|
||||
|
||||
case 4:
|
||||
buffer = (char *)malloc((strlen(str1) + 1) * sizeof(char)); // BAD [NOT DETECTED]
|
||||
buffer = (char *)malloc((strlen(str1) + 1) * sizeof(char)); // BAD
|
||||
strcpy(buffer, str1);
|
||||
strcat(buffer, str2);
|
||||
break;
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
| test.c:16:20:16:25 | call to malloc | This allocation does not include space to null-terminate the string. |
|
||||
| test.c:32:20:32:25 | call to malloc | This allocation does not include space to null-terminate the string. |
|
||||
| test.c:49:20:49:25 | call to malloc | This allocation does not include space to null-terminate the string. |
|
||||
| test.c:64:20:64:25 | call to malloc | This allocation does not include space to null-terminate the string. |
|
||||
| test.cpp:24:35:24:40 | call to malloc | This allocation does not include space to null-terminate the string. |
|
||||
| test.cpp:31:35:31:40 | call to malloc | This allocation does not include space to null-terminate the string. |
|
||||
| test.cpp:45:28:45:33 | call to malloc | This allocation does not include space to null-terminate the string. |
|
||||
| test.cpp:55:28:55:33 | call to malloc | This allocation does not include space to null-terminate the string. |
|
||||
| test.cpp:63:28:63:33 | call to malloc | This allocation does not include space to null-terminate the string. |
|
||||
|
||||
@@ -60,7 +60,7 @@ void good2(char *str) {
|
||||
}
|
||||
|
||||
void bad3(char *str) {
|
||||
// BAD -- Not allocating space for '\0' terminator [NOT DETECTED]
|
||||
// BAD -- Not allocating space for '\0' terminator
|
||||
char *buffer = malloc(strlen(str) * sizeof(char));
|
||||
strcpy(buffer, str);
|
||||
free(buffer);
|
||||
|
||||
@@ -27,7 +27,7 @@ void bad1(wchar_t *wstr) {
|
||||
}
|
||||
|
||||
void bad2(wchar_t *wstr) {
|
||||
// BAD -- Not allocating space for '\0' terminator [NOT DETECTED]
|
||||
// BAD -- Not allocating space for '\0' terminator
|
||||
wchar_t *wbuffer = (wchar_t *)malloc(wcslen(wstr) * sizeof(wchar_t));
|
||||
wcscpy(wbuffer, wstr);
|
||||
free(wbuffer);
|
||||
|
||||
@@ -2293,12 +2293,13 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
|
||||
exists(
|
||||
AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing,
|
||||
LocalCallContext localCC
|
||||
|
|
||||
pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and
|
||||
localCC = getLocalCallContext(cc, enclosing)
|
||||
exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
||||
ap = ap0
|
||||
@@ -2331,20 +2332,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoLocalStep(
|
||||
PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc,
|
||||
AccessPath ap0, Configuration conf
|
||||
) {
|
||||
midnode = mid.getNode() and
|
||||
cc = mid.getCallContext() and
|
||||
conf = mid.getConfiguration() and
|
||||
localFlowBigStep(midnode, _, _, _, conf, _) and
|
||||
enclosing = midnode.getEnclosingCallable() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
ap0 = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
readDirect(node1, f, node2) and
|
||||
|
||||
@@ -2293,12 +2293,13 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
|
||||
exists(
|
||||
AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing,
|
||||
LocalCallContext localCC
|
||||
|
|
||||
pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and
|
||||
localCC = getLocalCallContext(cc, enclosing)
|
||||
exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
||||
ap = ap0
|
||||
@@ -2331,20 +2332,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoLocalStep(
|
||||
PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc,
|
||||
AccessPath ap0, Configuration conf
|
||||
) {
|
||||
midnode = mid.getNode() and
|
||||
cc = mid.getCallContext() and
|
||||
conf = mid.getConfiguration() and
|
||||
localFlowBigStep(midnode, _, _, _, conf, _) and
|
||||
enclosing = midnode.getEnclosingCallable() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
ap0 = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
readDirect(node1, f, node2) and
|
||||
|
||||
@@ -2293,12 +2293,13 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
|
||||
exists(
|
||||
AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing,
|
||||
LocalCallContext localCC
|
||||
|
|
||||
pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and
|
||||
localCC = getLocalCallContext(cc, enclosing)
|
||||
exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
||||
ap = ap0
|
||||
@@ -2331,20 +2332,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoLocalStep(
|
||||
PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc,
|
||||
AccessPath ap0, Configuration conf
|
||||
) {
|
||||
midnode = mid.getNode() and
|
||||
cc = mid.getCallContext() and
|
||||
conf = mid.getConfiguration() and
|
||||
localFlowBigStep(midnode, _, _, _, conf, _) and
|
||||
enclosing = midnode.getEnclosingCallable() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
ap0 = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
readDirect(node1, f, node2) and
|
||||
|
||||
@@ -2293,12 +2293,13 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
|
||||
exists(
|
||||
AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing,
|
||||
LocalCallContext localCC
|
||||
|
|
||||
pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and
|
||||
localCC = getLocalCallContext(cc, enclosing)
|
||||
exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
||||
ap = ap0
|
||||
@@ -2331,20 +2332,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoLocalStep(
|
||||
PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc,
|
||||
AccessPath ap0, Configuration conf
|
||||
) {
|
||||
midnode = mid.getNode() and
|
||||
cc = mid.getCallContext() and
|
||||
conf = mid.getConfiguration() and
|
||||
localFlowBigStep(midnode, _, _, _, conf, _) and
|
||||
enclosing = midnode.getEnclosingCallable() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
ap0 = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
readDirect(node1, f, node2) and
|
||||
|
||||
@@ -2293,12 +2293,13 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
|
||||
exists(
|
||||
AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing,
|
||||
LocalCallContext localCC
|
||||
|
|
||||
pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and
|
||||
localCC = getLocalCallContext(cc, enclosing)
|
||||
exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
||||
ap = ap0
|
||||
@@ -2331,20 +2332,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoLocalStep(
|
||||
PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc,
|
||||
AccessPath ap0, Configuration conf
|
||||
) {
|
||||
midnode = mid.getNode() and
|
||||
cc = mid.getCallContext() and
|
||||
conf = mid.getConfiguration() and
|
||||
localFlowBigStep(midnode, _, _, _, conf, _) and
|
||||
enclosing = midnode.getEnclosingCallable() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
ap0 = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
readDirect(node1, f, node2) and
|
||||
|
||||
@@ -2293,12 +2293,13 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
|
||||
exists(
|
||||
AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing,
|
||||
LocalCallContext localCC
|
||||
|
|
||||
pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and
|
||||
localCC = getLocalCallContext(cc, enclosing)
|
||||
exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
||||
ap = ap0
|
||||
@@ -2331,20 +2332,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoLocalStep(
|
||||
PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc,
|
||||
AccessPath ap0, Configuration conf
|
||||
) {
|
||||
midnode = mid.getNode() and
|
||||
cc = mid.getCallContext() and
|
||||
conf = mid.getConfiguration() and
|
||||
localFlowBigStep(midnode, _, _, _, conf, _) and
|
||||
enclosing = midnode.getEnclosingCallable() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
ap0 = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
readDirect(node1, f, node2) and
|
||||
|
||||
@@ -2293,12 +2293,13 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
|
||||
exists(
|
||||
AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing,
|
||||
LocalCallContext localCC
|
||||
|
|
||||
pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and
|
||||
localCC = getLocalCallContext(cc, enclosing)
|
||||
exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
||||
ap = ap0
|
||||
@@ -2331,20 +2332,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoLocalStep(
|
||||
PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc,
|
||||
AccessPath ap0, Configuration conf
|
||||
) {
|
||||
midnode = mid.getNode() and
|
||||
cc = mid.getCallContext() and
|
||||
conf = mid.getConfiguration() and
|
||||
localFlowBigStep(midnode, _, _, _, conf, _) and
|
||||
enclosing = midnode.getEnclosingCallable() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
ap0 = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
readDirect(node1, f, node2) and
|
||||
|
||||
@@ -2293,12 +2293,13 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
|
||||
exists(
|
||||
AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing,
|
||||
LocalCallContext localCC
|
||||
|
|
||||
pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and
|
||||
localCC = getLocalCallContext(cc, enclosing)
|
||||
exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
||||
ap = ap0
|
||||
@@ -2331,20 +2332,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoLocalStep(
|
||||
PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc,
|
||||
AccessPath ap0, Configuration conf
|
||||
) {
|
||||
midnode = mid.getNode() and
|
||||
cc = mid.getCallContext() and
|
||||
conf = mid.getConfiguration() and
|
||||
localFlowBigStep(midnode, _, _, _, conf, _) and
|
||||
enclosing = midnode.getEnclosingCallable() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
ap0 = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
readDirect(node1, f, node2) and
|
||||
|
||||
@@ -2293,12 +2293,13 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
|
||||
exists(
|
||||
AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing,
|
||||
LocalCallContext localCC
|
||||
|
|
||||
pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and
|
||||
localCC = getLocalCallContext(cc, enclosing)
|
||||
exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
||||
ap = ap0
|
||||
@@ -2331,20 +2332,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoLocalStep(
|
||||
PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc,
|
||||
AccessPath ap0, Configuration conf
|
||||
) {
|
||||
midnode = mid.getNode() and
|
||||
cc = mid.getCallContext() and
|
||||
conf = mid.getConfiguration() and
|
||||
localFlowBigStep(midnode, _, _, _, conf, _) and
|
||||
enclosing = midnode.getEnclosingCallable() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
ap0 = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
readDirect(node1, f, node2) and
|
||||
|
||||
@@ -2293,12 +2293,13 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
|
||||
exists(
|
||||
AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing,
|
||||
LocalCallContext localCC
|
||||
|
|
||||
pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and
|
||||
localCC = getLocalCallContext(cc, enclosing)
|
||||
exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, _, conf, localCC) and
|
||||
ap = ap0
|
||||
@@ -2331,20 +2332,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
|
||||
pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoLocalStep(
|
||||
PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc,
|
||||
AccessPath ap0, Configuration conf
|
||||
) {
|
||||
midnode = mid.getNode() and
|
||||
cc = mid.getCallContext() and
|
||||
conf = mid.getConfiguration() and
|
||||
localFlowBigStep(midnode, _, _, _, conf, _) and
|
||||
enclosing = midnode.getEnclosingCallable() and
|
||||
sc = mid.getSummaryCtx() and
|
||||
ap0 = mid.getAp()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
|
||||
readDirect(node1, f, node2) and
|
||||
|
||||
@@ -16,7 +16,8 @@ import semmle.javascript.security.dataflow.UnsafeJQueryPlugin::UnsafeJQueryPlugi
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from
|
||||
Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, JQueryPluginMethod plugin
|
||||
Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink,
|
||||
JQuery::JQueryPluginMethod plugin
|
||||
where
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
source.getNode().(Source).getPlugin() = plugin and
|
||||
|
||||
@@ -305,6 +305,13 @@ module DOM {
|
||||
call.getNumArgument() = 1 and
|
||||
forex(InferredType t | t = call.getArgument(0).analyze().getAType() | t = TTNumber())
|
||||
)
|
||||
or
|
||||
// A `this` node from a callback given to a `$().each(callback)` call.
|
||||
// purposely not using JQuery::MethodCall to avoid `jquery.each()`.
|
||||
exists(DataFlow::CallNode eachCall | eachCall = JQuery::objectRef().getAMethodCall("each") |
|
||||
this = DataFlow::thisNode(eachCall.getCallback(0).getFunction()) or
|
||||
this = eachCall.getABoundCallbackParameter(0, 1)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -496,6 +496,15 @@ module JQuery {
|
||||
hasUnderlyingType("jQuery")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `this` node in a JQuery plugin function, which is a JQuery object.
|
||||
*/
|
||||
private class JQueryPluginThisObject extends Range {
|
||||
JQueryPluginThisObject() {
|
||||
this = DataFlow::thisNode(any(JQueryPluginMethod method).getFunction())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** A source of jQuery objects from the AST-based `JQueryObject` class. */
|
||||
@@ -590,4 +599,64 @@ module JQuery {
|
||||
node = getArgument(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds for jQuery plugin definitions of the form `$.fn.<pluginName> = <plugin>` or `$.extend($.fn, {<pluginName>, <plugin>})`.
|
||||
*/
|
||||
private predicate jQueryPluginDefinition(string pluginName, DataFlow::Node plugin) {
|
||||
exists(DataFlow::PropRead fn, DataFlow::PropWrite write |
|
||||
fn = jquery().getAPropertyRead("fn") and
|
||||
(
|
||||
write = fn.getAPropertyWrite()
|
||||
or
|
||||
exists(ExtendCall extend, DataFlow::SourceNode source |
|
||||
fn.flowsTo(extend.getDestinationOperand()) and
|
||||
source = extend.getASourceOperand() and
|
||||
write = source.getAPropertyWrite()
|
||||
)
|
||||
) and
|
||||
plugin = write.getRhs() and
|
||||
(
|
||||
pluginName = write.getPropertyName() or
|
||||
write.getPropertyNameExpr().flow().mayHaveStringValue(pluginName)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that is registered as a jQuery plugin method at `def`.
|
||||
*/
|
||||
private DataFlow::SourceNode getAJQueryPluginMethod(
|
||||
DataFlow::TypeBackTracker t, DataFlow::Node def
|
||||
) {
|
||||
t.start() and jQueryPluginDefinition(_, def) and result.flowsTo(def)
|
||||
or
|
||||
exists(DataFlow::TypeBackTracker t2 | result = getAJQueryPluginMethod(t2, def).backtrack(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a function that is registered as a jQuery plugin method at `def`.
|
||||
*/
|
||||
private DataFlow::FunctionNode getAJQueryPluginMethod(DataFlow::Node def) {
|
||||
result = getAJQueryPluginMethod(DataFlow::TypeBackTracker::end(), def)
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that is registered as a jQuery plugin method.
|
||||
*/
|
||||
class JQueryPluginMethod extends DataFlow::FunctionNode {
|
||||
string pluginName;
|
||||
|
||||
JQueryPluginMethod() {
|
||||
exists(DataFlow::Node def |
|
||||
jQueryPluginDefinition(pluginName, def) and
|
||||
this = getAJQueryPluginMethod(def)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of this plugin.
|
||||
*/
|
||||
string getPluginName() { result = pluginName }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ module UnsafeJQueryPlugin {
|
||||
/**
|
||||
* Gets the plugin that this source is used in.
|
||||
*/
|
||||
abstract JQueryPluginMethod getPlugin();
|
||||
abstract JQuery::JQueryPluginMethod getPlugin();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,49 +49,6 @@ module UnsafeJQueryPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds for jQuery plugin definitions of the form `$.fn.<pluginName> = <plugin>`.
|
||||
*/
|
||||
private predicate jQueryPluginDefinition(string pluginName, DataFlow::Node plugin) {
|
||||
exists(DataFlow::PropRead fn, DataFlow::PropWrite write |
|
||||
fn = jquery().getAPropertyRead("fn") and
|
||||
(
|
||||
write = fn.getAPropertyWrite()
|
||||
or
|
||||
exists(ExtendCall extend, DataFlow::SourceNode source |
|
||||
fn.flowsTo(extend.getDestinationOperand()) and
|
||||
source = extend.getASourceOperand() and
|
||||
write = source.getAPropertyWrite()
|
||||
)
|
||||
) and
|
||||
plugin = write.getRhs() and
|
||||
(
|
||||
pluginName = write.getPropertyName() or
|
||||
write.getPropertyNameExpr().flow().mayHaveStringValue(pluginName)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that is registered as a jQuery plugin method at `def`.
|
||||
*/
|
||||
private DataFlow::SourceNode getAJQueryPluginMethod(
|
||||
DataFlow::TypeBackTracker t, DataFlow::Node def
|
||||
) {
|
||||
t.start() and
|
||||
jQueryPluginDefinition(_, def) and
|
||||
result.flowsTo(def)
|
||||
or
|
||||
exists(DataFlow::TypeBackTracker t2 | result = getAJQueryPluginMethod(t2, def).backtrack(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a function that is registered as a jQuery plugin method at `def`.
|
||||
*/
|
||||
private DataFlow::FunctionNode getAJQueryPluginMethod(DataFlow::Node def) {
|
||||
result = getAJQueryPluginMethod(DataFlow::TypeBackTracker::end(), def)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an operand to `extend`.
|
||||
*/
|
||||
@@ -109,29 +66,10 @@ module UnsafeJQueryPlugin {
|
||||
result = getAnExtendOperand(DataFlow::TypeBackTracker::end(), extend)
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that is registered as a jQuery plugin method.
|
||||
*/
|
||||
class JQueryPluginMethod extends DataFlow::FunctionNode {
|
||||
string pluginName;
|
||||
|
||||
JQueryPluginMethod() {
|
||||
exists(DataFlow::Node def |
|
||||
jQueryPluginDefinition(pluginName, def) and
|
||||
this = getAJQueryPluginMethod(def)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of this plugin.
|
||||
*/
|
||||
string getPluginName() { result = pluginName }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `plugin` has a default option defined at `def`.
|
||||
*/
|
||||
private predicate hasDefaultOption(JQueryPluginMethod plugin, DataFlow::PropWrite def) {
|
||||
private predicate hasDefaultOption(JQuery::JQueryPluginMethod plugin, DataFlow::PropWrite def) {
|
||||
exists(ExtendCall extend, JQueryPluginOptions options, DataFlow::SourceNode default |
|
||||
options.getPlugin() = plugin and
|
||||
options = getAnExtendOperand(extend) and
|
||||
@@ -144,7 +82,7 @@ module UnsafeJQueryPlugin {
|
||||
* The client-provided options object for a jQuery plugin.
|
||||
*/
|
||||
class JQueryPluginOptions extends DataFlow::ParameterNode {
|
||||
JQueryPluginMethod method;
|
||||
JQuery::JQueryPluginMethod method;
|
||||
|
||||
JQueryPluginOptions() {
|
||||
exists(string optionsPattern |
|
||||
@@ -165,7 +103,7 @@ module UnsafeJQueryPlugin {
|
||||
/**
|
||||
* Gets the plugin method that these options are used in.
|
||||
*/
|
||||
JQueryPluginMethod getPlugin() { result = method }
|
||||
JQuery::JQueryPluginMethod getPlugin() { result = method }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -224,7 +162,9 @@ module UnsafeJQueryPlugin {
|
||||
* The client-provided options object for a jQuery plugin, considered as a source for unsafe jQuery plugins.
|
||||
*/
|
||||
class JQueryPluginOptionsAsSource extends Source, JQueryPluginOptions {
|
||||
override JQueryPluginMethod getPlugin() { result = JQueryPluginOptions.super.getPlugin() }
|
||||
override JQuery::JQueryPluginMethod getPlugin() {
|
||||
result = JQueryPluginOptions.super.getPlugin()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -246,7 +186,7 @@ module UnsafeJQueryPlugin {
|
||||
/**
|
||||
* Holds if `plugin` likely expects `sink` to be treated as a HTML fragment.
|
||||
*/
|
||||
predicate isLikelyIntentionalHtmlSink(JQueryPluginMethod plugin, Sink sink) {
|
||||
predicate isLikelyIntentionalHtmlSink(JQuery::JQueryPluginMethod plugin, Sink sink) {
|
||||
exists(DataFlow::PropWrite defaultDef, string default, DataFlow::PropRead finalRead |
|
||||
hasDefaultOption(plugin, defaultDef) and
|
||||
defaultDef.getPropertyName() = finalRead.getPropertyName() and
|
||||
|
||||
@@ -80,6 +80,7 @@ module DomBasedXss {
|
||||
not exists(DataFlow::Node prefix, string strval |
|
||||
isPrefixOfJQueryHtmlString(this, prefix) and
|
||||
strval = prefix.getStringValue() and
|
||||
not strval = "" and
|
||||
not strval.regexpMatch("\\s*<.*")
|
||||
) and
|
||||
not DOM::locationRef().flowsTo(this)
|
||||
|
||||
@@ -347,6 +347,16 @@ nodes
|
||||
| tst.js:354:16:354:39 | documen ... .search |
|
||||
| tst.js:355:12:355:17 | target |
|
||||
| tst.js:355:12:355:17 | target |
|
||||
| tst.js:361:10:361:42 | target |
|
||||
| tst.js:361:19:361:35 | document.location |
|
||||
| tst.js:361:19:361:35 | document.location |
|
||||
| tst.js:361:19:361:42 | documen ... .search |
|
||||
| tst.js:362:16:362:21 | target |
|
||||
| tst.js:362:16:362:21 | target |
|
||||
| tst.js:366:21:366:26 | target |
|
||||
| tst.js:366:21:366:26 | target |
|
||||
| tst.js:369:18:369:23 | target |
|
||||
| tst.js:369:18:369:23 | target |
|
||||
| typeahead.js:20:13:20:45 | target |
|
||||
| typeahead.js:20:22:20:38 | document.location |
|
||||
| typeahead.js:20:22:20:38 | document.location |
|
||||
@@ -670,6 +680,15 @@ edges
|
||||
| tst.js:354:16:354:32 | document.location | tst.js:354:16:354:39 | documen ... .search |
|
||||
| tst.js:354:16:354:32 | document.location | tst.js:354:16:354:39 | documen ... .search |
|
||||
| tst.js:354:16:354:39 | documen ... .search | tst.js:354:7:354:39 | target |
|
||||
| tst.js:361:10:361:42 | target | tst.js:362:16:362:21 | target |
|
||||
| tst.js:361:10:361:42 | target | tst.js:362:16:362:21 | target |
|
||||
| tst.js:361:10:361:42 | target | tst.js:366:21:366:26 | target |
|
||||
| tst.js:361:10:361:42 | target | tst.js:366:21:366:26 | target |
|
||||
| tst.js:361:10:361:42 | target | tst.js:369:18:369:23 | target |
|
||||
| tst.js:361:10:361:42 | target | tst.js:369:18:369:23 | target |
|
||||
| tst.js:361:19:361:35 | document.location | tst.js:361:19:361:42 | documen ... .search |
|
||||
| tst.js:361:19:361:35 | document.location | tst.js:361:19:361:42 | documen ... .search |
|
||||
| tst.js:361:19:361:42 | documen ... .search | tst.js:361:10:361:42 | target |
|
||||
| typeahead.js:20:13:20:45 | target | typeahead.js:21:12:21:17 | target |
|
||||
| typeahead.js:20:22:20:38 | document.location | typeahead.js:20:22:20:45 | documen ... .search |
|
||||
| typeahead.js:20:22:20:38 | document.location | typeahead.js:20:22:20:45 | documen ... .search |
|
||||
@@ -772,6 +791,9 @@ edges
|
||||
| tst.js:336:18:336:35 | params.get('name') | tst.js:330:18:330:34 | document.location | tst.js:336:18:336:35 | params.get('name') | Cross-site scripting vulnerability due to $@. | tst.js:330:18:330:34 | document.location | user-provided value |
|
||||
| tst.js:349:5:349:30 | getUrl( ... ring(1) | tst.js:347:20:347:36 | document.location | tst.js:349:5:349:30 | getUrl( ... ring(1) | Cross-site scripting vulnerability due to $@. | tst.js:347:20:347:36 | document.location | user-provided value |
|
||||
| tst.js:355:12:355:17 | target | tst.js:354:16:354:32 | document.location | tst.js:355:12:355:17 | target | Cross-site scripting vulnerability due to $@. | tst.js:354:16:354:32 | document.location | user-provided value |
|
||||
| tst.js:362:16:362:21 | target | tst.js:361:19:361:35 | document.location | tst.js:362:16:362:21 | target | Cross-site scripting vulnerability due to $@. | tst.js:361:19:361:35 | document.location | user-provided value |
|
||||
| tst.js:366:21:366:26 | target | tst.js:361:19:361:35 | document.location | tst.js:366:21:366:26 | target | Cross-site scripting vulnerability due to $@. | tst.js:361:19:361:35 | document.location | user-provided value |
|
||||
| tst.js:369:18:369:23 | target | tst.js:361:19:361:35 | document.location | tst.js:369:18:369:23 | target | Cross-site scripting vulnerability due to $@. | tst.js:361:19:361:35 | document.location | user-provided value |
|
||||
| typeahead.js:25:18:25:20 | val | typeahead.js:20:22:20:38 | document.location | typeahead.js:25:18:25:20 | val | Cross-site scripting vulnerability due to $@. | typeahead.js:20:22:20:38 | document.location | user-provided value |
|
||||
| v-html.vue:2:8:2:23 | v-html=tainted | v-html.vue:6:42:6:58 | document.location | v-html.vue:2:8:2:23 | v-html=tainted | Cross-site scripting vulnerability due to $@. | v-html.vue:6:42:6:58 | document.location | user-provided value |
|
||||
| winjs.js:3:43:3:49 | tainted | winjs.js:2:17:2:33 | document.location | winjs.js:3:43:3:49 | tainted | Cross-site scripting vulnerability due to $@. | winjs.js:2:17:2:33 | document.location | user-provided value |
|
||||
|
||||
@@ -41,6 +41,11 @@ nodes
|
||||
| xss-through-dom.js:71:11:71:32 | $("inpu ... 0).name |
|
||||
| xss-through-dom.js:71:11:71:32 | $("inpu ... 0).name |
|
||||
| xss-through-dom.js:71:11:71:32 | $("inpu ... 0).name |
|
||||
| xss-through-dom.js:73:9:73:41 | selector |
|
||||
| xss-through-dom.js:73:20:73:41 | $("inpu ... 0).name |
|
||||
| xss-through-dom.js:73:20:73:41 | $("inpu ... 0).name |
|
||||
| xss-through-dom.js:77:7:77:14 | selector |
|
||||
| xss-through-dom.js:77:7:77:14 | selector |
|
||||
edges
|
||||
| xss-through-dom.js:2:16:2:34 | $("textarea").val() | xss-through-dom.js:2:16:2:34 | $("textarea").val() |
|
||||
| xss-through-dom.js:4:16:4:40 | $(".som ... .text() | xss-through-dom.js:4:16:4:40 | $(".som ... .text() |
|
||||
@@ -56,6 +61,10 @@ edges
|
||||
| xss-through-dom.js:61:30:61:69 | $(docum ... value") | xss-through-dom.js:61:30:61:69 | $(docum ... value") |
|
||||
| xss-through-dom.js:64:30:64:40 | valMethod() | xss-through-dom.js:64:30:64:40 | valMethod() |
|
||||
| xss-through-dom.js:71:11:71:32 | $("inpu ... 0).name | xss-through-dom.js:71:11:71:32 | $("inpu ... 0).name |
|
||||
| xss-through-dom.js:73:9:73:41 | selector | xss-through-dom.js:77:7:77:14 | selector |
|
||||
| xss-through-dom.js:73:9:73:41 | selector | xss-through-dom.js:77:7:77:14 | selector |
|
||||
| xss-through-dom.js:73:20:73:41 | $("inpu ... 0).name | xss-through-dom.js:73:9:73:41 | selector |
|
||||
| xss-through-dom.js:73:20:73:41 | $("inpu ... 0).name | xss-through-dom.js:73:9:73:41 | selector |
|
||||
#select
|
||||
| xss-through-dom.js:2:16:2:34 | $("textarea").val() | xss-through-dom.js:2:16:2:34 | $("textarea").val() | xss-through-dom.js:2:16:2:34 | $("textarea").val() | Cross-site scripting vulnerability due to $@. | xss-through-dom.js:2:16:2:34 | $("textarea").val() | DOM text |
|
||||
| xss-through-dom.js:4:16:4:40 | $(".som ... .text() | xss-through-dom.js:4:16:4:40 | $(".som ... .text() | xss-through-dom.js:4:16:4:40 | $(".som ... .text() | Cross-site scripting vulnerability due to $@. | xss-through-dom.js:4:16:4:40 | $(".som ... .text() | DOM text |
|
||||
@@ -71,3 +80,4 @@ edges
|
||||
| xss-through-dom.js:61:30:61:69 | $(docum ... value") | xss-through-dom.js:61:30:61:69 | $(docum ... value") | xss-through-dom.js:61:30:61:69 | $(docum ... value") | Cross-site scripting vulnerability due to $@. | xss-through-dom.js:61:30:61:69 | $(docum ... value") | DOM text |
|
||||
| xss-through-dom.js:64:30:64:40 | valMethod() | xss-through-dom.js:64:30:64:40 | valMethod() | xss-through-dom.js:64:30:64:40 | valMethod() | Cross-site scripting vulnerability due to $@. | xss-through-dom.js:64:30:64:40 | valMethod() | DOM text |
|
||||
| xss-through-dom.js:71:11:71:32 | $("inpu ... 0).name | xss-through-dom.js:71:11:71:32 | $("inpu ... 0).name | xss-through-dom.js:71:11:71:32 | $("inpu ... 0).name | Cross-site scripting vulnerability due to $@. | xss-through-dom.js:71:11:71:32 | $("inpu ... 0).name | DOM text |
|
||||
| xss-through-dom.js:77:7:77:14 | selector | xss-through-dom.js:73:20:73:41 | $("inpu ... 0).name | xss-through-dom.js:77:7:77:14 | selector | Cross-site scripting vulnerability due to $@. | xss-through-dom.js:73:20:73:41 | $("inpu ... 0).name | DOM text |
|
||||
|
||||
@@ -347,6 +347,16 @@ nodes
|
||||
| tst.js:354:16:354:39 | documen ... .search |
|
||||
| tst.js:355:12:355:17 | target |
|
||||
| tst.js:355:12:355:17 | target |
|
||||
| tst.js:361:10:361:42 | target |
|
||||
| tst.js:361:19:361:35 | document.location |
|
||||
| tst.js:361:19:361:35 | document.location |
|
||||
| tst.js:361:19:361:42 | documen ... .search |
|
||||
| tst.js:362:16:362:21 | target |
|
||||
| tst.js:362:16:362:21 | target |
|
||||
| tst.js:366:21:366:26 | target |
|
||||
| tst.js:366:21:366:26 | target |
|
||||
| tst.js:369:18:369:23 | target |
|
||||
| tst.js:369:18:369:23 | target |
|
||||
| typeahead.js:9:28:9:30 | loc |
|
||||
| typeahead.js:9:28:9:30 | loc |
|
||||
| typeahead.js:10:16:10:18 | loc |
|
||||
@@ -674,6 +684,15 @@ edges
|
||||
| tst.js:354:16:354:32 | document.location | tst.js:354:16:354:39 | documen ... .search |
|
||||
| tst.js:354:16:354:32 | document.location | tst.js:354:16:354:39 | documen ... .search |
|
||||
| tst.js:354:16:354:39 | documen ... .search | tst.js:354:7:354:39 | target |
|
||||
| tst.js:361:10:361:42 | target | tst.js:362:16:362:21 | target |
|
||||
| tst.js:361:10:361:42 | target | tst.js:362:16:362:21 | target |
|
||||
| tst.js:361:10:361:42 | target | tst.js:366:21:366:26 | target |
|
||||
| tst.js:361:10:361:42 | target | tst.js:366:21:366:26 | target |
|
||||
| tst.js:361:10:361:42 | target | tst.js:369:18:369:23 | target |
|
||||
| tst.js:361:10:361:42 | target | tst.js:369:18:369:23 | target |
|
||||
| tst.js:361:19:361:35 | document.location | tst.js:361:19:361:42 | documen ... .search |
|
||||
| tst.js:361:19:361:35 | document.location | tst.js:361:19:361:42 | documen ... .search |
|
||||
| tst.js:361:19:361:42 | documen ... .search | tst.js:361:10:361:42 | target |
|
||||
| typeahead.js:9:28:9:30 | loc | typeahead.js:10:16:10:18 | loc |
|
||||
| typeahead.js:9:28:9:30 | loc | typeahead.js:10:16:10:18 | loc |
|
||||
| typeahead.js:9:28:9:30 | loc | typeahead.js:10:16:10:18 | loc |
|
||||
|
||||
@@ -355,3 +355,20 @@ function growl() {
|
||||
$.jGrowl(target); // NOT OK
|
||||
}
|
||||
|
||||
function thisNodes() {
|
||||
var pluginName = "myFancyJQueryPlugin";
|
||||
var myPlugin = function () {
|
||||
var target = document.location.search
|
||||
this.html(target); // NOT OK. (this is a jQuery object)
|
||||
this.innerHTML = target // OK. (this is a jQuery object)
|
||||
|
||||
this.each(function (i, e) {
|
||||
this.innerHTML = target; // NOT OK. (this is a DOM-node);
|
||||
this.html(target); // OK. (this is a DOM-node);
|
||||
|
||||
e.innerHTML = target; // NOT OK.
|
||||
});
|
||||
}
|
||||
$.fn[pluginName] = myPlugin;
|
||||
|
||||
}
|
||||
|
||||
@@ -69,4 +69,10 @@
|
||||
}
|
||||
|
||||
$.jGrowl($("input").get(0).name); // NOT OK.
|
||||
|
||||
let selector = $("input").get(0).name;
|
||||
if (something()) {
|
||||
selector = $("textarea").val || ''
|
||||
}
|
||||
$(selector); // NOT OK
|
||||
})();
|
||||
@@ -12,17 +12,17 @@
|
||||
import python
|
||||
import Expressions.CallArgs
|
||||
|
||||
from Call call, FunctionObject func, FunctionObject overridden, string problem
|
||||
from Call call, FunctionValue func, FunctionValue overridden, string problem
|
||||
where
|
||||
func.overrides(overridden) and
|
||||
(
|
||||
wrong_args_objectapi(call, func, _, problem) and
|
||||
correct_args_if_called_as_method_objectapi(call, overridden)
|
||||
wrong_args(call, func, _, problem) and
|
||||
correct_args_if_called_as_method(call, overridden)
|
||||
or
|
||||
exists(string name |
|
||||
illegally_named_parameter_objectapi(call, func, name) and
|
||||
illegally_named_parameter(call, func, name) and
|
||||
problem = "an argument named '" + name + "'" and
|
||||
overridden.getFunction().getAnArg().(Name).getId() = name
|
||||
overridden.getScope().getAnArg().(Name).getId() = name
|
||||
)
|
||||
)
|
||||
select func,
|
||||
|
||||
@@ -17,7 +17,7 @@ import semmle.python.filters.Tests
|
||||
from Assert a, string value
|
||||
where
|
||||
/* Exclude asserts inside test cases */
|
||||
not a.getScope() instanceof Test and
|
||||
not a.getScope().getScope*() instanceof TestScope and
|
||||
exists(Expr test | test = a.getTest() |
|
||||
value = test.(IntegerLiteral).getN()
|
||||
or
|
||||
|
||||
@@ -5,18 +5,17 @@ abstract class TestScope extends Scope { }
|
||||
// don't extend Class directly to avoid ambiguous method warnings
|
||||
class UnitTestClass extends TestScope {
|
||||
UnitTestClass() {
|
||||
exists(ClassObject c | this = c.getPyClass() |
|
||||
c.getASuperType() = theUnitTestPackage().attr(_)
|
||||
exists(ClassValue cls | this = cls.getScope() |
|
||||
cls.getABaseType+() = Module::named("unittest").attr(_)
|
||||
or
|
||||
c.getASuperType().getName().toLowerCase() = "testcase"
|
||||
cls.getABaseType+().getName().toLowerCase() = "testcase"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
PackageObject theUnitTestPackage() { result.getName() = "unittest" }
|
||||
|
||||
abstract class Test extends TestScope { }
|
||||
|
||||
/** Class of test function that uses the `unittest` framework */
|
||||
class UnitTestFunction extends Test {
|
||||
UnitTestFunction() {
|
||||
this.getScope+() instanceof UnitTestClass and
|
||||
@@ -37,3 +36,11 @@ class NoseTestFunction extends Test {
|
||||
this.(Function).getName().matches("test%")
|
||||
}
|
||||
}
|
||||
|
||||
/** Class of functions that are clearly tests, but don't belong to a specific framework */
|
||||
class UnknownTestFunction extends Test {
|
||||
UnknownTestFunction() {
|
||||
this.(Function).getName().matches("test%") and
|
||||
this.getEnclosingModule().getFile().getShortName().matches("test_%.py")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,21 @@ abstract class StringKind extends TaintKind {
|
||||
bindingset[this]
|
||||
StringKind() { this = this }
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
name in ["capitalize", "casefold", "center", "expandtabs", "format", "format_map", "ljust",
|
||||
"lstrip", "lower", "replace", "rjust", "rstrip", "strip", "swapcase", "title", "upper",
|
||||
"zfill",
|
||||
/* encode/decode is technically not correct, but close enough */
|
||||
"encode", "decode"] and
|
||||
result = this
|
||||
or
|
||||
name in ["partition", "rpartition", "rsplit", "split", "splitlines"] and
|
||||
result.(SequenceKind).getItem() = this
|
||||
}
|
||||
|
||||
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
result = this and
|
||||
(
|
||||
str_method_call(fromnode, tonode) or
|
||||
slice(fromnode, tonode) or
|
||||
tonode.(BinaryExprNode).getAnOperand() = fromnode or
|
||||
os_path_join(fromnode, tonode) or
|
||||
@@ -50,20 +61,6 @@ private class StringEqualitySanitizer extends Sanitizer {
|
||||
}
|
||||
}
|
||||
|
||||
/* tonode = fromnode.xxx() where the call to xxx returns an identical or similar string */
|
||||
private predicate str_method_call(ControlFlowNode fromnode, CallNode tonode) {
|
||||
exists(string method_name | tonode.getFunction().(AttrNode).getObject(method_name) = fromnode |
|
||||
method_name = "strip" or
|
||||
method_name = "format" or
|
||||
method_name = "lstrip" or
|
||||
method_name = "rstrip" or
|
||||
method_name = "ljust" or
|
||||
method_name = "rjust" or
|
||||
method_name = "title" or
|
||||
method_name = "capitalize"
|
||||
)
|
||||
}
|
||||
|
||||
/* tonode = ....format(fromnode) */
|
||||
private predicate str_format(ControlFlowNode fromnode, CallNode tonode) {
|
||||
tonode.getFunction().(AttrNode).getName() = "format" and
|
||||
|
||||
@@ -7,3 +7,4 @@ import semmle.python.web.bottle.Request
|
||||
import semmle.python.web.turbogears.Request
|
||||
import semmle.python.web.falcon.Request
|
||||
import semmle.python.web.cherrypy.Request
|
||||
import semmle.python.web.stdlib.Request
|
||||
|
||||
@@ -7,3 +7,4 @@ import semmle.python.web.bottle.Response
|
||||
import semmle.python.web.turbogears.Response
|
||||
import semmle.python.web.falcon.Response
|
||||
import semmle.python.web.cherrypy.Response
|
||||
import semmle.python.web.stdlib.Response
|
||||
|
||||
124
python/ql/src/semmle/python/web/stdlib/Request.qll
Normal file
124
python/ql/src/semmle/python/web/stdlib/Request.qll
Normal file
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* Provides the sources and taint-flow for HTTP servers defined using the standard library (stdlib).
|
||||
* Specifically, we model `HttpRequestTaintSource`s from instances of `BaseHTTPRequestHandler`
|
||||
* (or subclasses) and form parsing using `cgi.FieldStorage`.
|
||||
*/
|
||||
import python
|
||||
import semmle.python.security.TaintTracking
|
||||
import semmle.python.web.Http
|
||||
|
||||
/** Source of BaseHTTPRequestHandler instances. */
|
||||
class StdLibRequestSource extends HttpRequestTaintSource {
|
||||
StdLibRequestSource() {
|
||||
exists(ClassValue cls |
|
||||
cls.getABaseType+() = Value::named("BaseHTTPServer.BaseHTTPRequestHandler")
|
||||
or
|
||||
cls.getABaseType+() = Value::named("http.server.BaseHTTPRequestHandler")
|
||||
|
|
||||
this.(ControlFlowNode).pointsTo().getClass() = cls
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof BaseHTTPRequestHandlerKind }
|
||||
}
|
||||
|
||||
/** TaintKind for an instance of BaseHTTPRequestHandler. */
|
||||
class BaseHTTPRequestHandlerKind extends TaintKind {
|
||||
BaseHTTPRequestHandlerKind() { this = "BaseHTTPRequestHandlerKind" }
|
||||
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
name in ["requestline", "path"] and
|
||||
result instanceof ExternalStringKind
|
||||
or
|
||||
name = "headers" and
|
||||
result instanceof HTTPMessageKind
|
||||
or
|
||||
name = "rfile" and
|
||||
result instanceof ExternalFileObject
|
||||
}
|
||||
}
|
||||
|
||||
/** TaintKind for headers (instance of HTTPMessage). */
|
||||
class HTTPMessageKind extends ExternalStringDictKind {
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
result = super.getTaintOfMethodResult(name)
|
||||
or
|
||||
name = "get_all" and
|
||||
result.(SequenceKind).getItem() = this.getValue()
|
||||
or
|
||||
name in ["as_bytes", "as_string"] and
|
||||
result instanceof ExternalStringKind
|
||||
}
|
||||
|
||||
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
result = super.getTaintForFlowStep(fromnode, tonode)
|
||||
or
|
||||
exists(ClassValue cls | cls = ClassValue::unicode() or cls = ClassValue::bytes() |
|
||||
tonode = cls.getACall() and
|
||||
tonode.(CallNode).getArg(0) = fromnode and
|
||||
result instanceof ExternalStringKind
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Source of parsed HTTP forms (by using the `cgi` module). */
|
||||
class CgiFieldStorageSource extends HttpRequestTaintSource {
|
||||
CgiFieldStorageSource() { this = Value::named("cgi.FieldStorage").getACall() }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof CgiFieldStorageFormKind }
|
||||
}
|
||||
|
||||
/** TaintKind for a parsed HTTP form. */
|
||||
class CgiFieldStorageFormKind extends TaintKind {
|
||||
/*
|
||||
* There is a slight difference between how we model form/fields and how it is handled by the code.
|
||||
* In the code
|
||||
* ```
|
||||
* form = cgi.FieldStorage()
|
||||
* field = form['myfield']
|
||||
* ```
|
||||
* both `form` and `field` have the type `cgi.FieldStorage`. This allows the code to represent
|
||||
* nested forms as `form['nested_form']['myfield']`. However, since HTML forms can't be nested
|
||||
* we ignore that detail since it allows for a more clean modeling.
|
||||
*/
|
||||
CgiFieldStorageFormKind() { this = "CgiFieldStorageFormKind" }
|
||||
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
name = "value" and result.(SequenceKind).getItem() instanceof CgiFieldStorageFieldKind
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
name = "getvalue" and
|
||||
(
|
||||
result instanceof ExternalStringKind
|
||||
or
|
||||
result.(SequenceKind).getItem() instanceof ExternalStringKind
|
||||
)
|
||||
or
|
||||
name = "getfirst" and
|
||||
result instanceof ExternalStringKind
|
||||
or
|
||||
name = "getlist" and
|
||||
result.(SequenceKind).getItem() instanceof ExternalStringKind
|
||||
}
|
||||
|
||||
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
tonode.(SubscriptNode).getObject() = fromnode and
|
||||
(
|
||||
result instanceof CgiFieldStorageFieldKind
|
||||
or
|
||||
result.(SequenceKind).getItem() instanceof CgiFieldStorageFieldKind
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** TaintKind for the field of a parsed HTTP form. */
|
||||
class CgiFieldStorageFieldKind extends TaintKind {
|
||||
CgiFieldStorageFieldKind() { this = "CgiFieldStorageFieldKind" }
|
||||
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
name in ["filename", "value"] and result instanceof ExternalStringKind
|
||||
or
|
||||
name = "file" and result instanceof ExternalFileObject
|
||||
}
|
||||
}
|
||||
43
python/ql/src/semmle/python/web/stdlib/Response.qll
Normal file
43
python/ql/src/semmle/python/web/stdlib/Response.qll
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Provides the sinks for HTTP servers defined with standard library (stdlib).
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.security.TaintTracking
|
||||
import semmle.python.web.Http
|
||||
|
||||
private predicate is_wfile(AttrNode wfile) {
|
||||
exists(ClassValue cls |
|
||||
// Python 2
|
||||
cls.getABaseType+() = Value::named("BaseHTTPServer.BaseHTTPRequestHandler")
|
||||
or
|
||||
// Python 3
|
||||
cls.getABaseType+() = Value::named("http.server.BaseHTTPRequestHandler")
|
||||
|
|
||||
wfile.getObject("wfile").pointsTo().getClass() = cls
|
||||
)
|
||||
}
|
||||
|
||||
/** Sink for `h.wfile.write` where `h` is an instance of BaseHTTPRequestHandler. */
|
||||
class StdLibWFileWriteSink extends HttpResponseTaintSink {
|
||||
StdLibWFileWriteSink() {
|
||||
exists(CallNode call |
|
||||
is_wfile(call.getFunction().(AttrNode).getObject("write")) and
|
||||
call.getArg(0) = this
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
|
||||
}
|
||||
|
||||
/** Sink for `h.wfile.writelines` where `h` is an instance of BaseHTTPRequestHandler. */
|
||||
class StdLibWFileWritelinesSink extends HttpResponseTaintSink {
|
||||
StdLibWFileWritelinesSink() {
|
||||
exists(CallNode call |
|
||||
is_wfile(call.getFunction().(AttrNode).getObject("writelines")) and
|
||||
call.getArg(0) = this
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringSequenceKind }
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
| Class MyTest |
|
||||
| Function test_1 |
|
||||
| Function test_2 |
|
||||
| test.py:4:1:4:23 | Class MyTest |
|
||||
| test.py:6:5:6:21 | Function test_1 |
|
||||
| test.py:9:5:9:21 | Function test_2 |
|
||||
| test_foo.py:3:1:3:15 | Function test_foo |
|
||||
| unittest_test.py:3:1:3:33 | Class FooTest |
|
||||
| unittest_test.py:4:5:4:25 | Function test_valid |
|
||||
|
||||
@@ -2,4 +2,4 @@ import python
|
||||
import semmle.python.filters.Tests
|
||||
|
||||
from TestScope t
|
||||
select t.toString()
|
||||
select t
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
class TestCase:
|
||||
pass
|
||||
|
||||
|
||||
4
python/ql/test/library-tests/filters/tests/test_foo.py
Normal file
4
python/ql/test/library-tests/filters/tests/test_foo.py
Normal file
@@ -0,0 +1,4 @@
|
||||
# This is running under some unknown framework, but is clearly a test!
|
||||
|
||||
def test_foo():
|
||||
assert True
|
||||
@@ -0,0 +1,5 @@
|
||||
import unittest
|
||||
|
||||
class FooTest(unittest.TestCase):
|
||||
def test_valid(self):
|
||||
pass
|
||||
@@ -1,5 +1,10 @@
|
||||
| Taint [externally controlled string] | test.py:67 | test.py:67:20:67:43 | urlsplit() | | --> | Taint [externally controlled string] | test.py:69 | test.py:69:10:69:21 | urlsplit_res | |
|
||||
| Taint [externally controlled string] | test.py:68 | test.py:68:20:68:43 | urlparse() | | --> | Taint [externally controlled string] | test.py:69 | test.py:69:24:69:35 | urlparse_res | |
|
||||
| Taint [externally controlled string] | test.py:98 | test.py:98:9:98:37 | Attribute() | | --> | Taint externally controlled string | test.py:98 | test.py:98:9:98:40 | Subscript | |
|
||||
| Taint [externally controlled string] | test.py:102 | test.py:102:9:102:38 | Attribute() | | --> | Taint externally controlled string | test.py:102 | test.py:102:9:102:41 | Subscript | |
|
||||
| Taint [externally controlled string] | test.py:104 | test.py:104:9:104:37 | Attribute() | | --> | Taint externally controlled string | test.py:104 | test.py:104:9:104:41 | Subscript | |
|
||||
| Taint [externally controlled string] | test.py:107 | test.py:107:9:107:30 | Attribute() | | --> | Taint externally controlled string | test.py:107 | test.py:107:9:107:33 | Subscript | |
|
||||
| Taint [externally controlled string] | test.py:109 | test.py:109:9:109:35 | Attribute() | | --> | Taint externally controlled string | test.py:109 | test.py:109:9:109:38 | Subscript | |
|
||||
| Taint exception.info | test.py:44 | test.py:44:22:44:26 | taint | p1 = exception.info | --> | Taint exception.info | test.py:45 | test.py:45:17:45:21 | taint | p1 = exception.info |
|
||||
| Taint exception.info | test.py:45 | test.py:45:17:45:21 | taint | p1 = exception.info | --> | Taint exception.info | test.py:45 | test.py:45:12:45:22 | func() | p1 = exception.info |
|
||||
| Taint exception.info | test.py:45 | test.py:45:17:45:21 | taint | p1 = exception.info | --> | Taint exception.info | test.py:52 | test.py:52:19:52:21 | arg | p0 = exception.info |
|
||||
@@ -62,6 +67,71 @@
|
||||
| Taint externally controlled string | test.py:66 | test.py:66:22:66:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:68 | test.py:68:29:68:42 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:67 | test.py:67:29:67:42 | tainted_string | | --> | Taint [externally controlled string] | test.py:67 | test.py:67:20:67:43 | urlsplit() | |
|
||||
| Taint externally controlled string | test.py:68 | test.py:68:29:68:42 | tainted_string | | --> | Taint [externally controlled string] | test.py:68 | test.py:68:20:68:43 | urlparse() | |
|
||||
| Taint externally controlled string | test.py:72 | test.py:72:22:72:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:74 | test.py:74:9:74:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:72 | test.py:72:22:72:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:76 | test.py:76:12:76:25 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:74 | test.py:74:9:74:22 | tainted_string | | --> | Taint externally controlled string | test.py:74 | test.py:74:9:74:30 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:74 | test.py:74:9:74:30 | Attribute() | | --> | Taint externally controlled string | test.py:79 | test.py:79:10:79:10 | a | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:85 | test.py:85:9:85:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:86 | test.py:86:9:86:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:87 | test.py:87:9:87:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:88 | test.py:88:9:88:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:89 | test.py:89:9:89:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:90 | test.py:90:9:90:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:91 | test.py:91:9:91:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:92 | test.py:92:9:92:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:93 | test.py:93:9:93:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:94 | test.py:94:9:94:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:95 | test.py:95:9:95:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:96 | test.py:96:9:96:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:97 | test.py:97:9:97:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:98 | test.py:98:9:98:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:99 | test.py:99:9:99:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:100 | test.py:100:9:100:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:101 | test.py:101:9:101:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:102 | test.py:102:9:102:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:103 | test.py:103:9:103:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:104 | test.py:104:9:104:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:105 | test.py:105:9:105:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:106 | test.py:106:9:106:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:107 | test.py:107:9:107:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:108 | test.py:108:9:108:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:109 | test.py:109:9:109:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:110 | test.py:110:9:110:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:111 | test.py:111:9:111:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:112 | test.py:112:9:112:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:115 | test.py:115:9:115:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:82 | test.py:82:22:82:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:116 | test.py:116:9:116:22 | tainted_string | |
|
||||
| Taint externally controlled string | test.py:85 | test.py:85:9:85:22 | tainted_string | | --> | Taint externally controlled string | test.py:85 | test.py:85:9:85:35 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:86 | test.py:86:9:86:22 | tainted_string | | --> | Taint externally controlled string | test.py:86 | test.py:86:9:86:33 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:87 | test.py:87:9:87:22 | tainted_string | | --> | Taint externally controlled string | test.py:87 | test.py:87:9:87:31 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:88 | test.py:88:9:88:22 | tainted_string | | --> | Taint externally controlled string | test.py:88 | test.py:88:9:88:38 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:89 | test.py:89:9:89:22 | tainted_string | | --> | Taint externally controlled string | test.py:89 | test.py:89:9:89:38 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:89 | test.py:89:9:89:38 | Attribute() | | --> | Taint externally controlled string | test.py:89 | test.py:89:9:89:54 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:90 | test.py:90:9:90:22 | tainted_string | | --> | Taint externally controlled string | test.py:90 | test.py:90:9:90:35 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:91 | test.py:91:9:91:22 | tainted_string | | --> | Taint externally controlled string | test.py:91 | test.py:91:9:91:37 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:92 | test.py:92:9:92:22 | tainted_string | | --> | Taint externally controlled string | test.py:92 | test.py:92:9:92:46 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:93 | test.py:93:9:93:22 | tainted_string | | --> | Taint externally controlled string | test.py:93 | test.py:93:9:93:33 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:94 | test.py:94:9:94:22 | tainted_string | | --> | Taint externally controlled string | test.py:94 | test.py:94:9:94:30 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:95 | test.py:95:9:95:22 | tainted_string | | --> | Taint externally controlled string | test.py:95 | test.py:95:9:95:31 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:96 | test.py:96:9:96:22 | tainted_string | | --> | Taint externally controlled string | test.py:96 | test.py:96:9:96:35 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:97 | test.py:97:9:97:22 | tainted_string | | --> | Taint [externally controlled string] | test.py:97 | test.py:97:9:97:37 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:98 | test.py:98:9:98:22 | tainted_string | | --> | Taint [externally controlled string] | test.py:98 | test.py:98:9:98:37 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:99 | test.py:99:9:99:22 | tainted_string | | --> | Taint externally controlled string | test.py:99 | test.py:99:9:99:42 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:100 | test.py:100:9:100:22 | tainted_string | | --> | Taint externally controlled string | test.py:100 | test.py:100:9:100:33 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:101 | test.py:101:9:101:22 | tainted_string | | --> | Taint [externally controlled string] | test.py:101 | test.py:101:9:101:38 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:102 | test.py:102:9:102:22 | tainted_string | | --> | Taint [externally controlled string] | test.py:102 | test.py:102:9:102:38 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:103 | test.py:103:9:103:22 | tainted_string | | --> | Taint [externally controlled string] | test.py:103 | test.py:103:9:103:37 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:104 | test.py:104:9:104:22 | tainted_string | | --> | Taint [externally controlled string] | test.py:104 | test.py:104:9:104:37 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:105 | test.py:105:9:105:22 | tainted_string | | --> | Taint externally controlled string | test.py:105 | test.py:105:9:105:31 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:106 | test.py:106:9:106:22 | tainted_string | | --> | Taint [externally controlled string] | test.py:106 | test.py:106:9:106:30 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:107 | test.py:107:9:107:22 | tainted_string | | --> | Taint [externally controlled string] | test.py:107 | test.py:107:9:107:30 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:108 | test.py:108:9:108:22 | tainted_string | | --> | Taint [externally controlled string] | test.py:108 | test.py:108:9:108:35 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:109 | test.py:109:9:109:22 | tainted_string | | --> | Taint [externally controlled string] | test.py:109 | test.py:109:9:109:35 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:110 | test.py:110:9:110:22 | tainted_string | | --> | Taint externally controlled string | test.py:110 | test.py:110:9:110:30 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:111 | test.py:111:9:111:22 | tainted_string | | --> | Taint externally controlled string | test.py:111 | test.py:111:9:111:33 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:112 | test.py:112:9:112:22 | tainted_string | | --> | Taint externally controlled string | test.py:112 | test.py:112:9:112:30 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:115 | test.py:115:9:115:22 | tainted_string | | --> | Taint externally controlled string | test.py:115 | test.py:115:9:115:30 | Attribute() | |
|
||||
| Taint externally controlled string | test.py:116 | test.py:116:9:116:22 | tainted_string | | --> | Taint externally controlled string | test.py:116 | test.py:116:9:116:33 | Attribute() | |
|
||||
| Taint json[externally controlled string] | test.py:6 | test.py:6:20:6:45 | Attribute() | | --> | Taint json[externally controlled string] | test.py:7 | test.py:7:9:7:20 | tainted_json | |
|
||||
| Taint json[externally controlled string] | test.py:7 | test.py:7:9:7:20 | tainted_json | | --> | Taint externally controlled string | test.py:7 | test.py:7:9:7:25 | Subscript | |
|
||||
| Taint json[externally controlled string] | test.py:7 | test.py:7:9:7:20 | tainted_json | | --> | Taint json[externally controlled string] | test.py:7 | test.py:7:9:7:25 | Subscript | |
|
||||
|
||||
@@ -22,3 +22,35 @@
|
||||
| test.py:58 | test_untrusted | res | externally controlled string |
|
||||
| test.py:69 | test_urlsplit_urlparse | urlparse_res | [externally controlled string] |
|
||||
| test.py:69 | test_urlsplit_urlparse | urlsplit_res | [externally controlled string] |
|
||||
| test.py:79 | test_method_reference | a | externally controlled string |
|
||||
| test.py:79 | test_method_reference | b | NO TAINT |
|
||||
| test.py:85 | test_str_methods | Attribute() | externally controlled string |
|
||||
| test.py:86 | test_str_methods | Attribute() | externally controlled string |
|
||||
| test.py:87 | test_str_methods | Attribute() | externally controlled string |
|
||||
| test.py:88 | test_str_methods | Attribute() | externally controlled string |
|
||||
| test.py:89 | test_str_methods | Attribute() | externally controlled string |
|
||||
| test.py:90 | test_str_methods | Attribute() | externally controlled string |
|
||||
| test.py:91 | test_str_methods | Attribute() | externally controlled string |
|
||||
| test.py:92 | test_str_methods | Attribute() | externally controlled string |
|
||||
| test.py:93 | test_str_methods | Attribute() | externally controlled string |
|
||||
| test.py:94 | test_str_methods | Attribute() | externally controlled string |
|
||||
| test.py:95 | test_str_methods | Attribute() | externally controlled string |
|
||||
| test.py:96 | test_str_methods | Attribute() | externally controlled string |
|
||||
| test.py:97 | test_str_methods | Attribute() | [externally controlled string] |
|
||||
| test.py:98 | test_str_methods | Subscript | externally controlled string |
|
||||
| test.py:99 | test_str_methods | Attribute() | externally controlled string |
|
||||
| test.py:100 | test_str_methods | Attribute() | externally controlled string |
|
||||
| test.py:101 | test_str_methods | Attribute() | [externally controlled string] |
|
||||
| test.py:102 | test_str_methods | Subscript | externally controlled string |
|
||||
| test.py:103 | test_str_methods | Attribute() | [externally controlled string] |
|
||||
| test.py:104 | test_str_methods | Subscript | externally controlled string |
|
||||
| test.py:105 | test_str_methods | Attribute() | externally controlled string |
|
||||
| test.py:106 | test_str_methods | Attribute() | [externally controlled string] |
|
||||
| test.py:107 | test_str_methods | Subscript | externally controlled string |
|
||||
| test.py:108 | test_str_methods | Attribute() | [externally controlled string] |
|
||||
| test.py:109 | test_str_methods | Subscript | externally controlled string |
|
||||
| test.py:110 | test_str_methods | Attribute() | externally controlled string |
|
||||
| test.py:111 | test_str_methods | Attribute() | externally controlled string |
|
||||
| test.py:112 | test_str_methods | Attribute() | externally controlled string |
|
||||
| test.py:115 | test_str_methods | Attribute() | externally controlled string |
|
||||
| test.py:116 | test_str_methods | Attribute() | externally controlled string |
|
||||
|
||||
@@ -67,3 +67,51 @@ def test_urlsplit_urlparse():
|
||||
urlsplit_res = urlsplit(tainted_string)
|
||||
urlparse_res = urlparse(tainted_string)
|
||||
test(urlsplit_res, urlparse_res)
|
||||
|
||||
def test_method_reference():
|
||||
tainted_string = TAINTED_STRING
|
||||
|
||||
a = tainted_string.title()
|
||||
|
||||
func = tainted_string.title
|
||||
b = func()
|
||||
|
||||
test(a, b) # TODO: `b` not tainted
|
||||
|
||||
def test_str_methods():
|
||||
tainted_string = TAINTED_STRING
|
||||
|
||||
test(
|
||||
tainted_string.capitalize(),
|
||||
tainted_string.casefold(),
|
||||
tainted_string.center(),
|
||||
tainted_string.encode('utf-8'),
|
||||
tainted_string.encode('utf-8').decode('utf-8'),
|
||||
tainted_string.expandtabs(),
|
||||
tainted_string.format(foo=42),
|
||||
tainted_string.format_map({'foo': 42}),
|
||||
tainted_string.ljust(100),
|
||||
tainted_string.lower(),
|
||||
tainted_string.lstrip(),
|
||||
tainted_string.lstrip('w.'),
|
||||
tainted_string.partition(';'),
|
||||
tainted_string.partition(';')[0],
|
||||
tainted_string.replace('/', '', 1),
|
||||
tainted_string.rjust(100),
|
||||
tainted_string.rpartition(';'),
|
||||
tainted_string.rpartition(';')[2],
|
||||
tainted_string.rsplit(';', 4),
|
||||
tainted_string.rsplit(';', 4)[-1],
|
||||
tainted_string.rstrip(),
|
||||
tainted_string.split(),
|
||||
tainted_string.split()[0],
|
||||
tainted_string.splitlines(),
|
||||
tainted_string.splitlines()[0],
|
||||
tainted_string.strip(),
|
||||
tainted_string.swapcase(),
|
||||
tainted_string.title(),
|
||||
# ignoring, as I have never seen this in practice
|
||||
# tainted_string.translate(translation_table),
|
||||
tainted_string.upper(),
|
||||
tainted_string.zfill(100),
|
||||
)
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
| test.py:72:26:72:58 | Taint sink | externally controlled string |
|
||||
| test.py:73:31:73:54 | Taint sink | [externally controlled string] |
|
||||
@@ -0,0 +1,7 @@
|
||||
import python
|
||||
import semmle.python.web.HttpResponse
|
||||
import semmle.python.security.strings.Untrusted
|
||||
|
||||
from HttpResponseTaintSink sink, TaintKind kind
|
||||
where sink.sinks(kind)
|
||||
select sink, kind
|
||||
34
python/ql/test/library-tests/web/stdlib/HttpSources.expected
Normal file
34
python/ql/test/library-tests/web/stdlib/HttpSources.expected
Normal file
@@ -0,0 +1,34 @@
|
||||
| test.py:18:13:18:16 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:20:13:20:16 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:22:13:22:16 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:24:13:24:16 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:25:13:25:16 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:26:13:26:16 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:27:13:27:16 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:28:13:28:16 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:29:13:29:16 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:30:13:30:16 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:31:13:31:16 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:32:13:32:16 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:33:17:33:20 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:34:19:34:22 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:36:13:36:16 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:37:13:37:16 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:40:16:44:9 | Attribute() | CgiFieldStorageFormKind |
|
||||
| test.py:41:13:41:16 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:42:13:42:16 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:43:64:43:67 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:69:9:69:12 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:70:9:70:12 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:71:9:71:12 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:72:9:72:12 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:73:9:73:12 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:74:15:74:18 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:78:16:82:9 | Attribute() | CgiFieldStorageFormKind |
|
||||
| test.py:79:13:79:16 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:80:13:80:16 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:81:64:81:67 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:85:13:85:16 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:86:13:86:16 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:96:9:96:12 | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:97:9:97:12 | self | BaseHTTPRequestHandlerKind |
|
||||
9
python/ql/test/library-tests/web/stdlib/HttpSources.ql
Normal file
9
python/ql/test/library-tests/web/stdlib/HttpSources.ql
Normal file
@@ -0,0 +1,9 @@
|
||||
import python
|
||||
import semmle.python.web.HttpRequest
|
||||
import semmle.python.security.strings.Untrusted
|
||||
|
||||
from HttpRequestTaintSource source, TaintKind kind
|
||||
where
|
||||
source.isSourceOf(kind) and
|
||||
source.getLocation().getFile().getShortName() != "cgi.py"
|
||||
select source.(ControlFlowNode).getNode(), kind
|
||||
32
python/ql/test/library-tests/web/stdlib/TestTaint.expected
Normal file
32
python/ql/test/library-tests/web/stdlib/TestTaint.expected
Normal file
@@ -0,0 +1,32 @@
|
||||
| test.py:18 | ok | taint_sources | self | BaseHTTPRequestHandlerKind |
|
||||
| test.py:20 | ok | taint_sources | Attribute | externally controlled string |
|
||||
| test.py:22 | ok | taint_sources | Attribute | externally controlled string |
|
||||
| test.py:24 | ok | taint_sources | Attribute | {externally controlled string} |
|
||||
| test.py:25 | ok | taint_sources | Subscript | externally controlled string |
|
||||
| test.py:26 | ok | taint_sources | Attribute() | externally controlled string |
|
||||
| test.py:27 | ok | taint_sources | Attribute() | [externally controlled string] |
|
||||
| test.py:28 | fail | taint_sources | Attribute() | <NO TAINT> |
|
||||
| test.py:29 | ok | taint_sources | Attribute() | [externally controlled string] |
|
||||
| test.py:30 | fail | taint_sources | Attribute() | <NO TAINT> |
|
||||
| test.py:31 | ok | taint_sources | Attribute() | externally controlled string |
|
||||
| test.py:32 | ok | taint_sources | Attribute() | externally controlled string |
|
||||
| test.py:33 | ok | taint_sources | str() | externally controlled string |
|
||||
| test.py:34 | ok | taint_sources | bytes() | externally controlled string |
|
||||
| test.py:36 | ok | taint_sources | Attribute | file[externally controlled string] |
|
||||
| test.py:37 | ok | taint_sources | Attribute() | externally controlled string |
|
||||
| test.py:47 | ok | taint_sources | form | CgiFieldStorageFormKind |
|
||||
| test.py:49 | ok | taint_sources | Subscript | CgiFieldStorageFieldKind |
|
||||
| test.py:49 | ok | taint_sources | Subscript | [CgiFieldStorageFieldKind] |
|
||||
| test.py:50 | ok | taint_sources | Attribute | externally controlled string |
|
||||
| test.py:51 | ok | taint_sources | Attribute | file[externally controlled string] |
|
||||
| test.py:52 | ok | taint_sources | Attribute | externally controlled string |
|
||||
| test.py:53 | ok | taint_sources | Subscript | CgiFieldStorageFieldKind |
|
||||
| test.py:54 | ok | taint_sources | Attribute | externally controlled string |
|
||||
| test.py:55 | ok | taint_sources | Attribute | file[externally controlled string] |
|
||||
| test.py:56 | ok | taint_sources | Attribute | externally controlled string |
|
||||
| test.py:58 | ok | taint_sources | Attribute() | [externally controlled string] |
|
||||
| test.py:58 | ok | taint_sources | Attribute() | externally controlled string |
|
||||
| test.py:59 | ok | taint_sources | Subscript | externally controlled string |
|
||||
| test.py:61 | ok | taint_sources | Attribute() | externally controlled string |
|
||||
| test.py:63 | ok | taint_sources | Attribute() | [externally controlled string] |
|
||||
| test.py:64 | ok | taint_sources | Subscript | externally controlled string |
|
||||
32
python/ql/test/library-tests/web/stdlib/TestTaint.ql
Normal file
32
python/ql/test/library-tests/web/stdlib/TestTaint.ql
Normal file
@@ -0,0 +1,32 @@
|
||||
import python
|
||||
import semmle.python.security.TaintTracking
|
||||
import semmle.python.web.HttpRequest
|
||||
import semmle.python.security.strings.Untrusted
|
||||
|
||||
from
|
||||
Call call, Expr arg, boolean expected_taint, boolean has_taint, string test_res,
|
||||
string taint_string
|
||||
where
|
||||
call.getLocation().getFile().getShortName() = "test.py" and
|
||||
(
|
||||
call.getFunc().(Name).getId() = "ensure_tainted" and
|
||||
expected_taint = true
|
||||
or
|
||||
call.getFunc().(Name).getId() = "ensure_not_tainted" and
|
||||
expected_taint = false
|
||||
) and
|
||||
arg = call.getAnArg() and
|
||||
(
|
||||
not exists(TaintedNode tainted | tainted.getAstNode() = arg) and
|
||||
taint_string = "<NO TAINT>" and
|
||||
has_taint = false
|
||||
or
|
||||
exists(TaintedNode tainted | tainted.getAstNode() = arg |
|
||||
taint_string = tainted.getTaintKind().toString()
|
||||
) and
|
||||
has_taint = true
|
||||
) and
|
||||
if expected_taint = has_taint then test_res = "ok " else test_res = "fail"
|
||||
// if expected_taint = has_taint then test_res = "✓" else test_res = "✕"
|
||||
select arg.getLocation().toString(), test_res, call.getScope().(Function).getName(), arg.toString(),
|
||||
taint_string
|
||||
108
python/ql/test/library-tests/web/stdlib/test.py
Normal file
108
python/ql/test/library-tests/web/stdlib/test.py
Normal file
@@ -0,0 +1,108 @@
|
||||
import sys
|
||||
import os
|
||||
import cgi
|
||||
|
||||
if sys.version_info[0] == 2:
|
||||
from BaseHTTPServer import BaseHTTPRequestHandler
|
||||
from BaseHTTPServer import HTTPServer
|
||||
|
||||
if sys.version_info[0] == 3:
|
||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||
|
||||
|
||||
class MyHandler(BaseHTTPRequestHandler):
|
||||
|
||||
def taint_sources(self):
|
||||
|
||||
ensure_tainted(
|
||||
self,
|
||||
|
||||
self.requestline,
|
||||
|
||||
self.path,
|
||||
|
||||
self.headers,
|
||||
self.headers['Foo'],
|
||||
self.headers.get('Foo'),
|
||||
self.headers.get_all('Foo'),
|
||||
self.headers.keys(),
|
||||
self.headers.values(),
|
||||
self.headers.items(),
|
||||
self.headers.as_bytes(),
|
||||
self.headers.as_string(),
|
||||
str(self.headers),
|
||||
bytes(self.headers),
|
||||
|
||||
self.rfile,
|
||||
self.rfile.read(),
|
||||
)
|
||||
|
||||
form = cgi.FieldStorage(
|
||||
self.rfile,
|
||||
self.headers,
|
||||
environ={'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': self.headers.get('content-type')},
|
||||
)
|
||||
|
||||
ensure_tainted(
|
||||
form,
|
||||
|
||||
form['key'],
|
||||
form['key'].value,
|
||||
form['key'].file,
|
||||
form['key'].filename,
|
||||
form['key'][0], # will be a list, if multiple fields named "key" are provided
|
||||
form['key'][0].value,
|
||||
form['key'][0].file,
|
||||
form['key'][0].filename,
|
||||
|
||||
form.getvalue('key'),
|
||||
form.getvalue('key')[0], # will be a list, if multiple fields named "key" are provided
|
||||
|
||||
form.getfirst('key'),
|
||||
|
||||
form.getlist('key'),
|
||||
form.getlist('key')[0],
|
||||
)
|
||||
|
||||
def do_GET(self):
|
||||
# send_response will log a line to stderr
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/plain; charset=utf-8")
|
||||
self.end_headers()
|
||||
self.wfile.write(b"Hello BaseHTTPRequestHandler\n")
|
||||
self.wfile.writelines([b"1\n", b"2\n", b"3\n"])
|
||||
print(self.headers)
|
||||
|
||||
|
||||
def do_POST(self):
|
||||
form = cgi.FieldStorage(
|
||||
self.rfile,
|
||||
self.headers,
|
||||
environ={'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': self.headers.get('content-type')},
|
||||
)
|
||||
|
||||
if 'myfile' not in form:
|
||||
self.send_response(422)
|
||||
self.end_headers()
|
||||
return
|
||||
|
||||
field = form['myfile']
|
||||
|
||||
field.file.seek(0, os.SEEK_END)
|
||||
filesize = field.file.tell()
|
||||
|
||||
print("Uploaded {!r} with {} bytes".format(field.filename, filesize))
|
||||
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
server = HTTPServer(("127.0.0.1", 8080), MyHandler)
|
||||
server.serve_forever()
|
||||
|
||||
# Headers works case insensitvely, so self.headers['foo'] == self.headers['FOO']
|
||||
# curl localhost:8080 --header "Foo: 1" --header "foo: 2"
|
||||
|
||||
# To test file submission through forms, use
|
||||
# curl -F myfile=@<yourfile> localhost:8080
|
||||
@@ -1,6 +1,6 @@
|
||||
| test.py:24:5:24:26 | Function meth1 | Overriding method signature does not match $@, where it is passed too few. Overridden method $@ is correctly specified. | test.py:15:9:15:20 | Attribute() | here | test.py:5:5:5:20 | Function meth1 | method Base.meth1 |
|
||||
| test.py:24:5:24:26 | Function meth1 | Overriding method signature does not match $@, where it is passed too few. Overridden method $@ is correctly specified. | test.py:34:9:34:20 | Attribute() | here | test.py:5:5:5:20 | Function meth1 | method Base.meth1 |
|
||||
| test.py:27:5:27:20 | Function meth2 | Overriding method signature does not match $@, where it is passed an argument named 'spam'. Overridden method $@ is correctly specified. | test.py:20:9:20:31 | Attribute() | here | test.py:8:5:8:26 | Function meth2 | method Base.meth2 |
|
||||
| test.py:27:5:27:20 | Function meth2 | Overriding method signature does not match $@, where it is passed an argument named 'spam'. Overridden method $@ is correctly specified. | test.py:39:9:39:31 | Attribute() | here | test.py:8:5:8:26 | Function meth2 | method Base.meth2 |
|
||||
| test.py:27:5:27:20 | Function meth2 | Overriding method signature does not match $@, where it is passed too many. Overridden method $@ is correctly specified. | test.py:18:9:18:21 | Attribute() | here | test.py:8:5:8:26 | Function meth2 | method Base.meth2 |
|
||||
| test.py:27:5:27:20 | Function meth2 | Overriding method signature does not match $@, where it is passed too many. Overridden method $@ is correctly specified. | test.py:37:9:37:21 | Attribute() | here | test.py:8:5:8:26 | Function meth2 | method Base.meth2 |
|
||||
| test.py:24:5:24:26 | Function Derived.meth1 | Overriding method signature does not match $@, where it is passed too few. Overridden method $@ is correctly specified. | test.py:15:9:15:20 | Attribute() | here | test.py:5:5:5:20 | Function Base.meth1 | method Base.meth1 |
|
||||
| test.py:24:5:24:26 | Function Derived.meth1 | Overriding method signature does not match $@, where it is passed too few. Overridden method $@ is correctly specified. | test.py:34:9:34:20 | Attribute() | here | test.py:5:5:5:20 | Function Base.meth1 | method Base.meth1 |
|
||||
| test.py:27:5:27:20 | Function Derived.meth2 | Overriding method signature does not match $@, where it is passed an argument named 'spam'. Overridden method $@ is correctly specified. | test.py:20:9:20:31 | Attribute() | here | test.py:8:5:8:26 | Function Base.meth2 | method Base.meth2 |
|
||||
| test.py:27:5:27:20 | Function Derived.meth2 | Overriding method signature does not match $@, where it is passed an argument named 'spam'. Overridden method $@ is correctly specified. | test.py:39:9:39:31 | Attribute() | here | test.py:8:5:8:26 | Function Base.meth2 | method Base.meth2 |
|
||||
| test.py:27:5:27:20 | Function Derived.meth2 | Overriding method signature does not match $@, where it is passed too many. Overridden method $@ is correctly specified. | test.py:18:9:18:21 | Attribute() | here | test.py:8:5:8:26 | Function Base.meth2 | method Base.meth2 |
|
||||
| test.py:27:5:27:20 | Function Derived.meth2 | Overriding method signature does not match $@, where it is passed too many. Overridden method $@ is correctly specified. | test.py:37:9:37:21 | Attribute() | here | test.py:8:5:8:26 | Function Base.meth2 | method Base.meth2 |
|
||||
|
||||
Reference in New Issue
Block a user