mirror of
https://github.com/github/codeql.git
synced 2025-12-21 11:16:30 +01:00
Merge branch 'main' into python/test-MaD-keyword-argument
This commit is contained in:
@@ -10,12 +10,16 @@ private import semmle.python.dataflow.new.internal.DataFlowDispatch
|
||||
private import semmle.python.dataflow.new.internal.TaintTrackingImplSpecific
|
||||
private import codeql.dataflow.internal.DataFlowImplConsistency
|
||||
|
||||
private module Input implements InputSig<PythonDataFlow> {
|
||||
private module Input implements InputSig<Location, PythonDataFlow> {
|
||||
private import Private
|
||||
private import Public
|
||||
|
||||
predicate postWithInFlowExclude(Node n) { n instanceof FlowSummaryNode }
|
||||
|
||||
predicate uniqueNodeLocationExclude(Node n) { n instanceof FlowSummaryNode }
|
||||
|
||||
predicate missingLocationExclude(Node n) { n instanceof FlowSummaryNode }
|
||||
|
||||
predicate argHasPostUpdateExclude(ArgumentNode n) {
|
||||
// TODO: Implement post-updates for *args, see tests added in https://github.com/github/codeql/pull/14936
|
||||
exists(ArgumentPosition apos | n.argumentOf(_, apos) and apos.isStarArgs(_))
|
||||
@@ -132,4 +136,4 @@ private module Input implements InputSig<PythonDataFlow> {
|
||||
}
|
||||
}
|
||||
|
||||
import MakeConsistency<PythonDataFlow, PythonTaintTracking, Input>
|
||||
import MakeConsistency<Location, PythonDataFlow, PythonTaintTracking, Input>
|
||||
|
||||
42
python/ql/consistency-queries/TypeTrackingConsistency.ql
Normal file
42
python/ql/consistency-queries/TypeTrackingConsistency.ql
Normal file
@@ -0,0 +1,42 @@
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate
|
||||
private import semmle.python.dataflow.new.internal.TypeTrackingImpl
|
||||
|
||||
private module ConsistencyChecksInput implements ConsistencyChecksInputSig {
|
||||
predicate unreachableNodeExclude(DataFlow::Node n) {
|
||||
n instanceof DataFlowPrivate::SyntheticPostUpdateNode
|
||||
or
|
||||
n instanceof DataFlowPrivate::SyntheticPreUpdateNode
|
||||
or
|
||||
// TODO: when adding support for proper content, handle **kwargs passing better!
|
||||
n instanceof DataFlowPrivate::SynthDictSplatArgumentNode
|
||||
or
|
||||
// TODO: when adding support for proper content, handle unpacking tuples in match
|
||||
// cases better, such as
|
||||
//
|
||||
// match (NONSOURCE, SOURCE):
|
||||
// case (x, y): ...
|
||||
exists(DataFlow::Node m |
|
||||
m.asCfgNode().getNode() instanceof MatchCapturePattern
|
||||
or
|
||||
m.asCfgNode().getNode() instanceof MatchAsPattern
|
||||
or
|
||||
m.asCfgNode().getNode() instanceof MatchOrPattern
|
||||
|
|
||||
TypeTrackingInput::simpleLocalSmallStep*(m, n)
|
||||
)
|
||||
or
|
||||
// TODO: when adding support for proper content, handle iterable unpacking better
|
||||
// such as `for k,v in items:`, or `a, (b,c) = ...`
|
||||
n instanceof DataFlow::IterableSequenceNode
|
||||
or
|
||||
// We have missing use-use flow in
|
||||
// https://github.com/python/cpython/blob/0fb18b02c8ad56299d6a2910be0bab8ad601ef24/Lib/socketserver.py#L276-L303
|
||||
// which I couldn't just fix. We ignore the problems here, and instead rely on the
|
||||
// test-case added in https://github.com/github/codeql/pull/15841
|
||||
n.getLocation().getFile().getAbsolutePath().matches("%/socketserver.py")
|
||||
}
|
||||
}
|
||||
|
||||
import ConsistencyChecks<ConsistencyChecksInput>
|
||||
@@ -1,3 +1,14 @@
|
||||
## 0.11.11
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.11.10
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Fixed missing flow for dictionary updates (`d[<key>] = ...`) when `<key>` is a string constant not used in dictionary literals or as name of keyword-argument.
|
||||
* Fixed flow for iterable unpacking (`a,b = my_tuple`) when it occurs on top-level (module) scope.
|
||||
|
||||
## 0.11.9
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Fixed flow for iterable unpacking (`a,b = my_tuple`) when it occurs on top-level (module) scope.
|
||||
@@ -1,4 +1,6 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
## 0.11.10
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Fixed missing flow for dictionary updates (`d[<key>] = ...`) when `<key>` is a string constant not used in dictionary literals or as name of keyword-argument.
|
||||
* Fixed flow for iterable unpacking (`a,b = my_tuple`) when it occurs on top-level (module) scope.
|
||||
3
python/ql/lib/change-notes/released/0.11.11.md
Normal file
3
python/ql/lib/change-notes/released/0.11.11.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.11.11
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.11.9
|
||||
lastReleaseVersion: 0.11.11
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/python-all
|
||||
version: 0.11.10-dev
|
||||
version: 0.11.12-dev
|
||||
groups: python
|
||||
dbscheme: semmlecode.python.dbscheme
|
||||
extractor: python
|
||||
|
||||
@@ -328,6 +328,9 @@ module API {
|
||||
*/
|
||||
DataFlow::Node getInducingNode() { this = Impl::MkUse(result) or this = Impl::MkDef(result) }
|
||||
|
||||
/** Gets the location of this node */
|
||||
PY::Location getLocation() { result = this.getInducingNode().getLocation() }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
@@ -335,7 +338,7 @@ module API {
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
deprecated predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.getInducingNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
|
||||
@@ -24,6 +24,6 @@ private import python
|
||||
module DataFlow {
|
||||
private import internal.DataFlowImplSpecific
|
||||
private import codeql.dataflow.DataFlow
|
||||
import DataFlowMake<PythonDataFlow>
|
||||
import DataFlowMake<Location, PythonDataFlow>
|
||||
import internal.DataFlowImpl1
|
||||
}
|
||||
|
||||
@@ -19,6 +19,6 @@ module TaintTracking {
|
||||
private import semmle.python.dataflow.new.internal.DataFlowImplSpecific
|
||||
private import semmle.python.dataflow.new.internal.TaintTrackingImplSpecific
|
||||
private import codeql.dataflow.TaintTracking
|
||||
import TaintFlowMake<PythonDataFlow, PythonTaintTracking>
|
||||
import TaintFlowMake<Location, PythonDataFlow, PythonTaintTracking>
|
||||
import internal.tainttracking1.TaintTrackingImpl
|
||||
}
|
||||
|
||||
@@ -1611,7 +1611,7 @@ class FlowSummaryNode extends Node, TFlowSummaryNode {
|
||||
override string toString() { result = this.getSummaryNode().toString() }
|
||||
|
||||
// Hack to return "empty location"
|
||||
override predicate hasLocationInfo(
|
||||
deprecated override predicate hasLocationInfo(
|
||||
string file, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
file = "" and
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
private import DataFlowImplSpecific
|
||||
private import codeql.dataflow.internal.DataFlowImpl
|
||||
import MakeImpl<PythonDataFlow>
|
||||
private import semmle.python.Files
|
||||
import MakeImpl<Location, PythonDataFlow>
|
||||
|
||||
@@ -285,6 +285,8 @@ deprecated private module Config implements FullStateConfigSig {
|
||||
|
||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||
|
||||
int accessPathLimit() { result = 5 }
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||
|
||||
@@ -285,6 +285,8 @@ deprecated private module Config implements FullStateConfigSig {
|
||||
|
||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||
|
||||
int accessPathLimit() { result = 5 }
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||
|
||||
@@ -285,6 +285,8 @@ deprecated private module Config implements FullStateConfigSig {
|
||||
|
||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||
|
||||
int accessPathLimit() { result = 5 }
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||
|
||||
@@ -285,6 +285,8 @@ deprecated private module Config implements FullStateConfigSig {
|
||||
|
||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||
|
||||
int accessPathLimit() { result = 5 }
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
private import DataFlowImplSpecific
|
||||
private import codeql.dataflow.internal.DataFlowImplCommon
|
||||
import MakeImplCommon<PythonDataFlow>
|
||||
private import semmle.python.Files
|
||||
import MakeImplCommon<Location, PythonDataFlow>
|
||||
|
||||
@@ -15,11 +15,13 @@ module Public {
|
||||
import DataFlowUtil
|
||||
}
|
||||
|
||||
module PythonDataFlow implements InputSig {
|
||||
module PythonDataFlow implements InputSig<Python::Location> {
|
||||
import Private
|
||||
import Public
|
||||
|
||||
predicate neverSkipInPathGraph = Private::neverSkipInPathGraph/1;
|
||||
|
||||
Node exprNode(DataFlowExpr e) { result = Public::exprNode(e) }
|
||||
|
||||
predicate ignoreFieldFlowBranchLimit(DataFlowCallable c) { exists(c.asLibraryCallable()) }
|
||||
}
|
||||
|
||||
@@ -148,6 +148,7 @@ class Node extends TNode {
|
||||
DataFlowCallable getEnclosingCallable() { result = getCallableScope(this.getScope()) }
|
||||
|
||||
/** Gets the location of this node */
|
||||
cached
|
||||
Location getLocation() { none() }
|
||||
|
||||
/**
|
||||
@@ -157,8 +158,7 @@ class Node extends TNode {
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
cached
|
||||
predicate hasLocationInfo(
|
||||
deprecated predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
Stages::DataFlow::ref() and
|
||||
|
||||
@@ -9,7 +9,7 @@ private import DataFlowImplSpecific as DataFlowImplSpecific
|
||||
private import DataFlowImplSpecific::Private
|
||||
private import DataFlowImplSpecific::Public
|
||||
|
||||
module Input implements InputSig<DataFlowImplSpecific::PythonDataFlow> {
|
||||
module Input implements InputSig<Location, DataFlowImplSpecific::PythonDataFlow> {
|
||||
class SummarizedCallableBase = string;
|
||||
|
||||
ArgumentPosition callbackSelfParameterPosition() { result.isLambdaSelf() }
|
||||
@@ -88,7 +88,7 @@ module Input implements InputSig<DataFlowImplSpecific::PythonDataFlow> {
|
||||
}
|
||||
}
|
||||
|
||||
private import Make<DataFlowImplSpecific::PythonDataFlow, Input> as Impl
|
||||
private import Make<Location, DataFlowImplSpecific::PythonDataFlow, Input> as Impl
|
||||
|
||||
private module StepsInput implements Impl::Private::StepsInputSig {
|
||||
DataFlowCall getACall(Public::SummarizedCallable sc) {
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
|
||||
private import codeql.dataflow.TaintTracking
|
||||
private import DataFlowImplSpecific
|
||||
private import semmle.python.Files
|
||||
|
||||
module PythonTaintTracking implements InputSig<PythonDataFlow> {
|
||||
module PythonTaintTracking implements InputSig<Location, PythonDataFlow> {
|
||||
import TaintTrackingPrivate
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ module Stages {
|
||||
or
|
||||
exists(any(DataFlowPublic::Node node).toString())
|
||||
or
|
||||
any(DataFlowPublic::Node node).hasLocationInfo(_, _, _, _, _)
|
||||
exists(any(DataFlowPublic::Node node).getLocation())
|
||||
or
|
||||
DataFlowDispatch::resolveCall(_, _, _)
|
||||
or
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
## 0.9.11
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.9.10
|
||||
|
||||
### New Queries
|
||||
|
||||
* The query `py/nosql-injection` for finding NoSQL injection vulnerabilities is now part of the default security suite.
|
||||
|
||||
## 0.9.9
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -16,6 +16,8 @@ import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
import semmle.python.filters.Tests
|
||||
private import semmle.python.dataflow.new.internal.DataFlowDispatch as DataFlowDispatch
|
||||
private import semmle.python.dataflow.new.internal.Builtins::Builtins as Builtins
|
||||
|
||||
bindingset[char, fraction]
|
||||
predicate fewer_characters_than(StrConst str, string char, float fraction) {
|
||||
@@ -30,15 +32,13 @@ predicate fewer_characters_than(StrConst str, string char, float fraction) {
|
||||
}
|
||||
|
||||
predicate possible_reflective_name(string name) {
|
||||
exists(any(ModuleValue m).attr(name))
|
||||
any(Function f).getName() = name
|
||||
or
|
||||
exists(any(ClassValue c).lookup(name))
|
||||
any(Class c).getName() = name
|
||||
or
|
||||
any(ClassValue c).getName() = name
|
||||
any(Module m).getName() = name
|
||||
or
|
||||
exists(Module::named(name))
|
||||
or
|
||||
exists(Value::named(name))
|
||||
exists(Builtins::likelyBuiltin(name))
|
||||
}
|
||||
|
||||
int char_count(StrConst str) { result = count(string c | c = str.getText().charAt(_)) }
|
||||
@@ -84,7 +84,9 @@ class CredentialSink extends DataFlow::Node {
|
||||
name.regexpMatch(getACredentialRegex()) and
|
||||
not name.matches("%file")
|
||||
|
|
||||
any(FunctionValue func).getNamedArgumentForCall(_, name) = this.asCfgNode()
|
||||
exists(DataFlowDispatch::ArgumentPosition pos | pos.isKeyword(name) |
|
||||
this.(DataFlow::ArgumentNode).argumentOf(_, pos)
|
||||
)
|
||||
or
|
||||
exists(Keyword k | k.getArg() = name and k.getValue().getAFlowNode() = this.asCfgNode())
|
||||
or
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
* @kind metric
|
||||
* @tags summary
|
||||
* lines-of-code
|
||||
* debug
|
||||
* @id py/summary/lines-of-user-code
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
category: newQuery
|
||||
---
|
||||
## 0.9.10
|
||||
|
||||
### New Queries
|
||||
|
||||
* The query `py/nosql-injection` for finding NoSQL injection vulnerabilities is now part of the default security suite.
|
||||
3
python/ql/src/change-notes/released/0.9.11.md
Normal file
3
python/ql/src/change-notes/released/0.9.11.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.9.11
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.9.9
|
||||
lastReleaseVersion: 0.9.11
|
||||
|
||||
38
python/ql/src/experimental/Security/CWE-770/UnicodeDoS.qhelp
Normal file
38
python/ql/src/experimental/Security/CWE-770/UnicodeDoS.qhelp
Normal file
@@ -0,0 +1,38 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>When a remote user-controlled data can reach a costly Unicode normalization with either form, NFKC or NFKD, an attack such as the One Million Unicode Characters, could lead to a denial of service on Windows OS.</p>
|
||||
|
||||
<p>And, with the use of special Unicode characters, like U+2100 (℀) or U+2105 (℅), the payload size could be tripled after the compatibility normalization.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>Ensure limiting the size of any incoming data that would go through a costly operations, including a Windows Unicode normalization with NFKC or NFKD. Such a recommandation would avoid a potential denial of service.</p>
|
||||
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
In this example a simple user-controlled data reaches a Unicode normalization with the form "NFKC".
|
||||
</p>
|
||||
|
||||
<sample src="bad.py" />
|
||||
|
||||
<p>To fix this vulnerability, we need restrain the size of the user input.</p>
|
||||
|
||||
<p>For example, we can use the <code>len()</code> builtin function to limit the size of the user input.</p>
|
||||
|
||||
<sample src="good.py" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
<a href="https://hackerone.com/reports/2258758">CVE-2023-46695: Potential denial of service vulnerability in Django UsernameField on Windows.</a>
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
114
python/ql/src/experimental/Security/CWE-770/UnicodeDoS.ql
Normal file
114
python/ql/src/experimental/Security/CWE-770/UnicodeDoS.ql
Normal file
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* @name Denial of Service using Unicode Characters
|
||||
* @description A remote user-controlled data can reach a costly Unicode normalization with either form NFKC or NFKD. On Windows OS, with an attack such as the One Million Unicode Characters, this could lead to a denial of service. And, with the use of special Unicode characters, like U+2100 (℀) or U+2105 (℅), the payload size could be tripled.
|
||||
* @kind path-problem
|
||||
* @id py/unicode-dos
|
||||
* @precision high
|
||||
* @problem.severity error
|
||||
* @tags security
|
||||
* experimental
|
||||
* external/cwe/cwe-770
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.ApiGraphs
|
||||
import semmle.python.Concepts
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
import semmle.python.dataflow.new.internal.DataFlowPublic
|
||||
import semmle.python.dataflow.new.RemoteFlowSources
|
||||
|
||||
// The Unicode compatibility normalization calls from unicodedata, unidecode, pyunormalize
|
||||
// and textnorm modules. The use of argIdx is to constraint the argument being normalized.
|
||||
class UnicodeCompatibilityNormalize extends API::CallNode {
|
||||
int argIdx;
|
||||
|
||||
UnicodeCompatibilityNormalize() {
|
||||
(
|
||||
this = API::moduleImport("unicodedata").getMember("normalize").getACall() and
|
||||
this.getParameter(0).getAValueReachingSink().asExpr().(StrConst).getText() in ["NFKC", "NFKD"]
|
||||
or
|
||||
this = API::moduleImport("pyunormalize").getMember("normalize").getACall() and
|
||||
this.getParameter(0).getAValueReachingSink().asExpr().(StrConst).getText() in ["NFKC", "NFKD"]
|
||||
) and
|
||||
argIdx = 1
|
||||
or
|
||||
(
|
||||
this = API::moduleImport("textnorm").getMember("normalize_unicode").getACall() and
|
||||
this.getParameter(1).getAValueReachingSink().asExpr().(StrConst).getText() in ["NFKC", "NFKD"]
|
||||
or
|
||||
this = API::moduleImport("unidecode").getMember("unidecode").getACall()
|
||||
or
|
||||
this = API::moduleImport("pyunormalize").getMember(["NFKC", "NFKD"]).getACall()
|
||||
) and
|
||||
argIdx = 0
|
||||
}
|
||||
|
||||
DataFlow::Node getPathArg() { result = this.getArg(argIdx) }
|
||||
}
|
||||
|
||||
predicate underAValue(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
|
||||
exists(CompareNode cn | cn = g |
|
||||
exists(API::CallNode lenCall, Cmpop op, Node n |
|
||||
lenCall = n.getALocalSource() and
|
||||
(
|
||||
// arg <= LIMIT OR arg < LIMIT
|
||||
(op instanceof LtE or op instanceof Lt) and
|
||||
branch = true and
|
||||
cn.operands(n.asCfgNode(), op, _)
|
||||
or
|
||||
// LIMIT >= arg OR LIMIT > arg
|
||||
(op instanceof GtE or op instanceof Gt) and
|
||||
branch = true and
|
||||
cn.operands(_, op, n.asCfgNode())
|
||||
or
|
||||
// not arg >= LIMIT OR not arg > LIMIT
|
||||
(op instanceof GtE or op instanceof Gt) and
|
||||
branch = false and
|
||||
cn.operands(n.asCfgNode(), op, _)
|
||||
or
|
||||
// not LIMIT <= arg OR not LIMIT < arg
|
||||
(op instanceof LtE or op instanceof Lt) and
|
||||
branch = false and
|
||||
cn.operands(_, op, n.asCfgNode())
|
||||
)
|
||||
|
|
||||
lenCall = API::builtin("len").getACall() and
|
||||
node = lenCall.getArg(0).asCfgNode()
|
||||
) //and
|
||||
//not cn.getLocation().getFile().inStdlib()
|
||||
)
|
||||
}
|
||||
|
||||
private module UnicodeDoSConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
predicate isBarrier(DataFlow::Node sanitizer) {
|
||||
// underAValue is a check to ensure that the length of the user-provided value is limited to a certain amount
|
||||
sanitizer = DataFlow::BarrierGuard<underAValue/3>::getABarrierNode()
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
// Any call to the Unicode compatibility normalization is a costly operation
|
||||
sink = any(UnicodeCompatibilityNormalize ucn).getPathArg()
|
||||
or
|
||||
// The call to secure_filename() from pallets/werkzeug uses the Unicode compatibility normalization
|
||||
// under the hood, https://github.com/pallets/werkzeug/blob/d3dd65a27388fbd39d146caacf2563639ba622f0/src/werkzeug/utils.py#L218
|
||||
sink = API::moduleImport("werkzeug").getMember("secure_filename").getACall().getArg(_)
|
||||
or
|
||||
sink =
|
||||
API::moduleImport("werkzeug")
|
||||
.getMember("utils")
|
||||
.getMember("secure_filename")
|
||||
.getACall()
|
||||
.getArg(_)
|
||||
}
|
||||
}
|
||||
|
||||
module UnicodeDoSFlow = TaintTracking::Global<UnicodeDoSConfig>;
|
||||
|
||||
import UnicodeDoSFlow::PathGraph
|
||||
|
||||
from UnicodeDoSFlow::PathNode source, UnicodeDoSFlow::PathNode sink
|
||||
where UnicodeDoSFlow::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "This $@ can reach a $@.", source.getNode(),
|
||||
"user-provided value", sink.getNode(), "costly Unicode normalization operation"
|
||||
17
python/ql/src/experimental/Security/CWE-770/bad.py
Normal file
17
python/ql/src/experimental/Security/CWE-770/bad.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from flask import Flask, jsonify, request
|
||||
import unicodedata
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@app.route("/bad_1")
|
||||
def bad_1():
|
||||
# User controlled data
|
||||
file_path = request.args.get("file_path", "")
|
||||
|
||||
# Normalize the file path using NFKC Unicode normalization
|
||||
return (
|
||||
unicodedata.normalize("NFKC", file_path),
|
||||
200,
|
||||
{"Content-Type": "application/octet-stream"},
|
||||
)
|
||||
16
python/ql/src/experimental/Security/CWE-770/good.py
Normal file
16
python/ql/src/experimental/Security/CWE-770/good.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from flask import Flask, jsonify, request
|
||||
import unicodedata
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@app.route("/good_1")
|
||||
def good_1():
|
||||
r = request.args.get("file_path", "")
|
||||
|
||||
if len(r) <= 1_000:
|
||||
# Normalize the r using NFKD Unicode normalization
|
||||
r = unicodedata.normalize("NFKD", r)
|
||||
return r, 200, {"Content-Type": "application/octet-stream"}
|
||||
else:
|
||||
return jsonify({"error": "File not found"}), 404
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/python-queries
|
||||
version: 0.9.10-dev
|
||||
version: 0.9.12-dev
|
||||
groups:
|
||||
- python
|
||||
- queries
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
argumentToEnsureNotTaintedNotMarkedAsSpurious
|
||||
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
|
||||
testFailures
|
||||
failures
|
||||
@@ -0,0 +1,4 @@
|
||||
import python
|
||||
import experimental.meta.InlineTaintTest
|
||||
import MakeInlineTaintTest<TestTaintTrackingConfig>
|
||||
import TestSummaries
|
||||
@@ -136,3 +136,108 @@ private class SummarizedCallableJsonLoads extends SummarizedCallable {
|
||||
preservesValue = true
|
||||
}
|
||||
}
|
||||
|
||||
// Repeated summaries
|
||||
private class SummarizedCallableWithSubpath extends SummarizedCallable {
|
||||
SummarizedCallableWithSubpath() { this = "extracted_package.functions.with_subpath" }
|
||||
|
||||
override DataFlow::CallCfgNode getACall() {
|
||||
result =
|
||||
API::moduleImport("extracted_package")
|
||||
.getMember("functions")
|
||||
.getMember("with_subpath")
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::ArgumentNode getACallback() {
|
||||
result =
|
||||
API::moduleImport("extracted_package")
|
||||
.getMember("functions")
|
||||
.getMember("with_subpath")
|
||||
.getAValueReachableFromSource()
|
||||
}
|
||||
|
||||
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||
input = "Argument[0]" and
|
||||
output = "ReturnValue" and
|
||||
preservesValue = false
|
||||
}
|
||||
}
|
||||
|
||||
private class SummarizedCallableWithSubpathAgain extends SummarizedCallable {
|
||||
SummarizedCallableWithSubpathAgain() { this = "extracted_package.functions.with_subpathII" }
|
||||
|
||||
override DataFlow::CallCfgNode getACall() {
|
||||
result =
|
||||
API::moduleImport("extracted_package")
|
||||
.getMember("functions")
|
||||
.getMember("with_subpath")
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::ArgumentNode getACallback() {
|
||||
result =
|
||||
API::moduleImport("extracted_package")
|
||||
.getMember("functions")
|
||||
.getMember("with_subpath")
|
||||
.getAValueReachableFromSource()
|
||||
}
|
||||
|
||||
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||
input = "Argument[0]" and
|
||||
output = "ReturnValue.Attribute[pattern]" and
|
||||
preservesValue = true
|
||||
}
|
||||
}
|
||||
|
||||
private class SummarizedCallableWithoutSubpath extends SummarizedCallable {
|
||||
SummarizedCallableWithoutSubpath() { this = "extracted_package.functions.without_subpath" }
|
||||
|
||||
override DataFlow::CallCfgNode getACall() {
|
||||
result =
|
||||
API::moduleImport("extracted_package")
|
||||
.getMember("functions")
|
||||
.getMember("without_subpath")
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::ArgumentNode getACallback() {
|
||||
result =
|
||||
API::moduleImport("extracted_package")
|
||||
.getMember("functions")
|
||||
.getMember("without_subpath")
|
||||
.getAValueReachableFromSource()
|
||||
}
|
||||
|
||||
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||
input = "Argument[0]" and
|
||||
output = "ReturnValue" and
|
||||
preservesValue = false
|
||||
}
|
||||
}
|
||||
|
||||
private class SummarizedCallableWithoutSubpathAgain extends SummarizedCallable {
|
||||
SummarizedCallableWithoutSubpathAgain() { this = "extracted_package.functions.without_subpathII" }
|
||||
|
||||
override DataFlow::CallCfgNode getACall() {
|
||||
result =
|
||||
API::moduleImport("extracted_package")
|
||||
.getMember("functions")
|
||||
.getMember("without_subpath")
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::ArgumentNode getACallback() {
|
||||
result =
|
||||
API::moduleImport("extracted_package")
|
||||
.getMember("functions")
|
||||
.getMember("without_subpath")
|
||||
.getAValueReachableFromSource()
|
||||
}
|
||||
|
||||
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||
input = "Argument[0]" and
|
||||
output = "ReturnValue.Attribute[pattern]" and
|
||||
preservesValue = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# Bad interaction of two summaries for the same function
|
||||
ts = TAINTED_STRING
|
||||
|
||||
from extracted_package.functions import with_subpath, without_subpath
|
||||
|
||||
# For the function `with_subpath`, flow from the first argument to the return value
|
||||
# can be concluded from its definition. This seems to discard all summaries, including
|
||||
# the one with flow to `ReturnValue.Attribute[pattern]`.
|
||||
ensure_tainted(
|
||||
with_subpath(ts).pattern, # $ tainted
|
||||
with_subpath(ts), # $ tainted
|
||||
with_subpath(ts), # $ tainted
|
||||
)
|
||||
ensure_tainted(
|
||||
without_subpath(ts).pattern, # $ tainted
|
||||
without_subpath(ts), # $ tainted
|
||||
without_subpath(ts), # $ tainted
|
||||
)
|
||||
@@ -0,0 +1,5 @@
|
||||
def with_subpath(x):
|
||||
return x
|
||||
|
||||
def without_subpath(x):
|
||||
pass
|
||||
@@ -4,7 +4,9 @@ edges
|
||||
| summaries.py:32:20:32:25 | ControlFlowNode for SOURCE | summaries.py:32:11:32:26 | ControlFlowNode for identity() | provenance | |
|
||||
| summaries.py:36:1:36:14 | ControlFlowNode for tainted_lambda | summaries.py:37:6:37:19 | ControlFlowNode for tainted_lambda | provenance | |
|
||||
| summaries.py:36:18:36:54 | ControlFlowNode for apply_lambda() | summaries.py:36:1:36:14 | ControlFlowNode for tainted_lambda | provenance | |
|
||||
| summaries.py:36:38:36:38 | ControlFlowNode for x | summaries.py:36:41:36:45 | ControlFlowNode for BinaryExpr | provenance | |
|
||||
| summaries.py:36:48:36:53 | ControlFlowNode for SOURCE | summaries.py:36:18:36:54 | ControlFlowNode for apply_lambda() | provenance | |
|
||||
| summaries.py:36:48:36:53 | ControlFlowNode for SOURCE | summaries.py:36:38:36:38 | ControlFlowNode for x | provenance | |
|
||||
| summaries.py:44:1:44:12 | ControlFlowNode for tainted_list | summaries.py:45:6:45:20 | ControlFlowNode for Subscript | provenance | |
|
||||
| summaries.py:44:1:44:12 | ControlFlowNode for tainted_list [List element] | summaries.py:45:6:45:17 | ControlFlowNode for tainted_list [List element] | provenance | |
|
||||
| summaries.py:44:16:44:33 | ControlFlowNode for reversed() | summaries.py:44:1:44:12 | ControlFlowNode for tainted_list | provenance | |
|
||||
@@ -14,13 +16,17 @@ edges
|
||||
| summaries.py:44:26:44:31 | ControlFlowNode for SOURCE | summaries.py:44:25:44:32 | ControlFlowNode for List | provenance | |
|
||||
| summaries.py:44:26:44:31 | ControlFlowNode for SOURCE | summaries.py:44:25:44:32 | ControlFlowNode for List [List element] | provenance | |
|
||||
| summaries.py:45:6:45:17 | ControlFlowNode for tainted_list [List element] | summaries.py:45:6:45:20 | ControlFlowNode for Subscript | provenance | |
|
||||
| summaries.py:48:15:48:15 | ControlFlowNode for x | summaries.py:49:12:49:18 | ControlFlowNode for BinaryExpr | provenance | |
|
||||
| summaries.py:51:1:51:14 | ControlFlowNode for tainted_mapped [List element] | summaries.py:52:6:52:19 | ControlFlowNode for tainted_mapped [List element] | provenance | |
|
||||
| summaries.py:51:18:51:46 | ControlFlowNode for list_map() [List element] | summaries.py:51:1:51:14 | ControlFlowNode for tainted_mapped [List element] | provenance | |
|
||||
| summaries.py:51:38:51:45 | ControlFlowNode for List [List element] | summaries.py:48:15:48:15 | ControlFlowNode for x | provenance | |
|
||||
| summaries.py:51:38:51:45 | ControlFlowNode for List [List element] | summaries.py:51:18:51:46 | ControlFlowNode for list_map() [List element] | provenance | |
|
||||
| summaries.py:51:39:51:44 | ControlFlowNode for SOURCE | summaries.py:51:38:51:45 | ControlFlowNode for List [List element] | provenance | |
|
||||
| summaries.py:52:6:52:19 | ControlFlowNode for tainted_mapped [List element] | summaries.py:52:6:52:22 | ControlFlowNode for Subscript | provenance | |
|
||||
| summaries.py:54:23:54:23 | ControlFlowNode for x | summaries.py:55:12:55:12 | ControlFlowNode for x | provenance | |
|
||||
| summaries.py:57:1:57:23 | ControlFlowNode for tainted_mapped_explicit [List element] | summaries.py:58:6:58:28 | ControlFlowNode for tainted_mapped_explicit [List element] | provenance | |
|
||||
| summaries.py:57:27:57:63 | ControlFlowNode for list_map() [List element] | summaries.py:57:1:57:23 | ControlFlowNode for tainted_mapped_explicit [List element] | provenance | |
|
||||
| summaries.py:57:55:57:62 | ControlFlowNode for List [List element] | summaries.py:54:23:54:23 | ControlFlowNode for x | provenance | |
|
||||
| summaries.py:57:55:57:62 | ControlFlowNode for List [List element] | summaries.py:57:27:57:63 | ControlFlowNode for list_map() [List element] | provenance | |
|
||||
| summaries.py:57:56:57:61 | ControlFlowNode for SOURCE | summaries.py:57:55:57:62 | ControlFlowNode for List [List element] | provenance | |
|
||||
| summaries.py:58:6:58:28 | ControlFlowNode for tainted_mapped_explicit [List element] | summaries.py:58:6:58:31 | ControlFlowNode for Subscript | provenance | |
|
||||
@@ -46,6 +52,8 @@ nodes
|
||||
| summaries.py:33:6:33:12 | ControlFlowNode for tainted | semmle.label | ControlFlowNode for tainted |
|
||||
| summaries.py:36:1:36:14 | ControlFlowNode for tainted_lambda | semmle.label | ControlFlowNode for tainted_lambda |
|
||||
| summaries.py:36:18:36:54 | ControlFlowNode for apply_lambda() | semmle.label | ControlFlowNode for apply_lambda() |
|
||||
| summaries.py:36:38:36:38 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| summaries.py:36:41:36:45 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr |
|
||||
| summaries.py:36:48:36:53 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| summaries.py:37:6:37:19 | ControlFlowNode for tainted_lambda | semmle.label | ControlFlowNode for tainted_lambda |
|
||||
| summaries.py:44:1:44:12 | ControlFlowNode for tainted_list | semmle.label | ControlFlowNode for tainted_list |
|
||||
@@ -57,12 +65,16 @@ nodes
|
||||
| summaries.py:44:26:44:31 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| summaries.py:45:6:45:17 | ControlFlowNode for tainted_list [List element] | semmle.label | ControlFlowNode for tainted_list [List element] |
|
||||
| summaries.py:45:6:45:20 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| summaries.py:48:15:48:15 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| summaries.py:49:12:49:18 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr |
|
||||
| summaries.py:51:1:51:14 | ControlFlowNode for tainted_mapped [List element] | semmle.label | ControlFlowNode for tainted_mapped [List element] |
|
||||
| summaries.py:51:18:51:46 | ControlFlowNode for list_map() [List element] | semmle.label | ControlFlowNode for list_map() [List element] |
|
||||
| summaries.py:51:38:51:45 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
|
||||
| summaries.py:51:39:51:44 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| summaries.py:52:6:52:19 | ControlFlowNode for tainted_mapped [List element] | semmle.label | ControlFlowNode for tainted_mapped [List element] |
|
||||
| summaries.py:52:6:52:22 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| summaries.py:54:23:54:23 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| summaries.py:55:12:55:12 | ControlFlowNode for x | semmle.label | ControlFlowNode for x |
|
||||
| summaries.py:57:1:57:23 | ControlFlowNode for tainted_mapped_explicit [List element] | semmle.label | ControlFlowNode for tainted_mapped_explicit [List element] |
|
||||
| summaries.py:57:27:57:63 | ControlFlowNode for list_map() [List element] | semmle.label | ControlFlowNode for list_map() [List element] |
|
||||
| summaries.py:57:55:57:62 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
|
||||
@@ -87,6 +99,9 @@ nodes
|
||||
| summaries.py:68:6:68:23 | ControlFlowNode for tainted_resultlist [List element] | semmle.label | ControlFlowNode for tainted_resultlist [List element] |
|
||||
| summaries.py:68:6:68:26 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
subpaths
|
||||
| summaries.py:36:48:36:53 | ControlFlowNode for SOURCE | summaries.py:36:38:36:38 | ControlFlowNode for x | summaries.py:36:41:36:45 | ControlFlowNode for BinaryExpr | summaries.py:36:18:36:54 | ControlFlowNode for apply_lambda() |
|
||||
| summaries.py:51:38:51:45 | ControlFlowNode for List [List element] | summaries.py:48:15:48:15 | ControlFlowNode for x | summaries.py:49:12:49:18 | ControlFlowNode for BinaryExpr | summaries.py:51:18:51:46 | ControlFlowNode for list_map() [List element] |
|
||||
| summaries.py:57:55:57:62 | ControlFlowNode for List [List element] | summaries.py:54:23:54:23 | ControlFlowNode for x | summaries.py:55:12:55:12 | ControlFlowNode for x | summaries.py:57:27:57:63 | ControlFlowNode for list_map() [List element] |
|
||||
invalidSpecComponent
|
||||
#select
|
||||
| summaries.py:33:6:33:12 | ControlFlowNode for tainted | summaries.py:32:20:32:25 | ControlFlowNode for SOURCE | summaries.py:33:6:33:12 | ControlFlowNode for tainted | $@ | summaries.py:32:20:32:25 | ControlFlowNode for SOURCE | ControlFlowNode for SOURCE |
|
||||
|
||||
@@ -34,7 +34,7 @@ def by_value1():
|
||||
a = SOURCE
|
||||
def inner(a_val=a):
|
||||
SINK(a_val) #$ captured
|
||||
SINK_F(a) #$ SPURIOUS: captured
|
||||
SINK_F(a)
|
||||
a = NONSOURCE
|
||||
inner()
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
edges
|
||||
| tests.py:1:35:1:41 | ControlFlowNode for ImportMember | tests.py:1:35:1:41 | ControlFlowNode for request | provenance | |
|
||||
| tests.py:1:35:1:41 | ControlFlowNode for request | tests.py:12:17:12:23 | ControlFlowNode for request | provenance | |
|
||||
| tests.py:1:35:1:41 | ControlFlowNode for request | tests.py:24:9:24:15 | ControlFlowNode for request | provenance | |
|
||||
| tests.py:1:35:1:41 | ControlFlowNode for request | tests.py:36:9:36:15 | ControlFlowNode for request | provenance | |
|
||||
| tests.py:1:35:1:41 | ControlFlowNode for request | tests.py:48:9:48:15 | ControlFlowNode for request | provenance | |
|
||||
| tests.py:1:35:1:41 | ControlFlowNode for request | tests.py:60:9:60:15 | ControlFlowNode for request | provenance | |
|
||||
| tests.py:1:35:1:41 | ControlFlowNode for request | tests.py:72:9:72:15 | ControlFlowNode for request | provenance | |
|
||||
| tests.py:12:5:12:13 | ControlFlowNode for file_path | tests.py:16:39:16:47 | ControlFlowNode for file_path | provenance | |
|
||||
| tests.py:12:17:12:23 | ControlFlowNode for request | tests.py:12:17:12:28 | ControlFlowNode for Attribute | provenance | |
|
||||
| tests.py:12:17:12:28 | ControlFlowNode for Attribute | tests.py:12:17:12:49 | ControlFlowNode for Attribute() | provenance | |
|
||||
| tests.py:12:17:12:49 | ControlFlowNode for Attribute() | tests.py:12:5:12:13 | ControlFlowNode for file_path | provenance | |
|
||||
| tests.py:24:5:24:5 | ControlFlowNode for r | tests.py:28:43:28:43 | ControlFlowNode for r | provenance | |
|
||||
| tests.py:24:9:24:15 | ControlFlowNode for request | tests.py:24:9:24:20 | ControlFlowNode for Attribute | provenance | |
|
||||
| tests.py:24:9:24:20 | ControlFlowNode for Attribute | tests.py:24:9:24:33 | ControlFlowNode for Attribute() | provenance | |
|
||||
| tests.py:24:9:24:33 | ControlFlowNode for Attribute() | tests.py:24:5:24:5 | ControlFlowNode for r | provenance | |
|
||||
| tests.py:36:5:36:5 | ControlFlowNode for r | tests.py:40:43:40:43 | ControlFlowNode for r | provenance | |
|
||||
| tests.py:36:9:36:15 | ControlFlowNode for request | tests.py:36:9:36:20 | ControlFlowNode for Attribute | provenance | |
|
||||
| tests.py:36:9:36:20 | ControlFlowNode for Attribute | tests.py:36:9:36:33 | ControlFlowNode for Attribute() | provenance | |
|
||||
| tests.py:36:9:36:33 | ControlFlowNode for Attribute() | tests.py:36:5:36:5 | ControlFlowNode for r | provenance | |
|
||||
| tests.py:48:5:48:5 | ControlFlowNode for r | tests.py:52:43:52:43 | ControlFlowNode for r | provenance | |
|
||||
| tests.py:48:9:48:15 | ControlFlowNode for request | tests.py:48:9:48:20 | ControlFlowNode for Attribute | provenance | |
|
||||
| tests.py:48:9:48:20 | ControlFlowNode for Attribute | tests.py:48:9:48:33 | ControlFlowNode for Attribute() | provenance | |
|
||||
| tests.py:48:9:48:33 | ControlFlowNode for Attribute() | tests.py:48:5:48:5 | ControlFlowNode for r | provenance | |
|
||||
| tests.py:60:5:60:5 | ControlFlowNode for r | tests.py:64:43:64:43 | ControlFlowNode for r | provenance | |
|
||||
| tests.py:60:9:60:15 | ControlFlowNode for request | tests.py:60:9:60:20 | ControlFlowNode for Attribute | provenance | |
|
||||
| tests.py:60:9:60:20 | ControlFlowNode for Attribute | tests.py:60:9:60:33 | ControlFlowNode for Attribute() | provenance | |
|
||||
| tests.py:60:9:60:33 | ControlFlowNode for Attribute() | tests.py:60:5:60:5 | ControlFlowNode for r | provenance | |
|
||||
| tests.py:72:5:72:5 | ControlFlowNode for r | tests.py:76:43:76:43 | ControlFlowNode for r | provenance | |
|
||||
| tests.py:72:9:72:15 | ControlFlowNode for request | tests.py:72:9:72:20 | ControlFlowNode for Attribute | provenance | |
|
||||
| tests.py:72:9:72:20 | ControlFlowNode for Attribute | tests.py:72:9:72:33 | ControlFlowNode for Attribute() | provenance | |
|
||||
| tests.py:72:9:72:33 | ControlFlowNode for Attribute() | tests.py:72:5:72:5 | ControlFlowNode for r | provenance | |
|
||||
nodes
|
||||
| tests.py:1:35:1:41 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember |
|
||||
| tests.py:1:35:1:41 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| tests.py:12:5:12:13 | ControlFlowNode for file_path | semmle.label | ControlFlowNode for file_path |
|
||||
| tests.py:12:17:12:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| tests.py:12:17:12:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| tests.py:12:17:12:49 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| tests.py:16:39:16:47 | ControlFlowNode for file_path | semmle.label | ControlFlowNode for file_path |
|
||||
| tests.py:24:5:24:5 | ControlFlowNode for r | semmle.label | ControlFlowNode for r |
|
||||
| tests.py:24:9:24:15 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| tests.py:24:9:24:20 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| tests.py:24:9:24:33 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| tests.py:28:43:28:43 | ControlFlowNode for r | semmle.label | ControlFlowNode for r |
|
||||
| tests.py:36:5:36:5 | ControlFlowNode for r | semmle.label | ControlFlowNode for r |
|
||||
| tests.py:36:9:36:15 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| tests.py:36:9:36:20 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| tests.py:36:9:36:33 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| tests.py:40:43:40:43 | ControlFlowNode for r | semmle.label | ControlFlowNode for r |
|
||||
| tests.py:48:5:48:5 | ControlFlowNode for r | semmle.label | ControlFlowNode for r |
|
||||
| tests.py:48:9:48:15 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| tests.py:48:9:48:20 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| tests.py:48:9:48:33 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| tests.py:52:43:52:43 | ControlFlowNode for r | semmle.label | ControlFlowNode for r |
|
||||
| tests.py:60:5:60:5 | ControlFlowNode for r | semmle.label | ControlFlowNode for r |
|
||||
| tests.py:60:9:60:15 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| tests.py:60:9:60:20 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| tests.py:60:9:60:33 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| tests.py:64:43:64:43 | ControlFlowNode for r | semmle.label | ControlFlowNode for r |
|
||||
| tests.py:72:5:72:5 | ControlFlowNode for r | semmle.label | ControlFlowNode for r |
|
||||
| tests.py:72:9:72:15 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| tests.py:72:9:72:20 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| tests.py:72:9:72:33 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| tests.py:76:43:76:43 | ControlFlowNode for r | semmle.label | ControlFlowNode for r |
|
||||
subpaths
|
||||
#select
|
||||
| tests.py:16:39:16:47 | ControlFlowNode for file_path | tests.py:1:35:1:41 | ControlFlowNode for ImportMember | tests.py:16:39:16:47 | ControlFlowNode for file_path | This $@ can reach a $@. | tests.py:1:35:1:41 | ControlFlowNode for ImportMember | user-provided value | tests.py:16:39:16:47 | ControlFlowNode for file_path | costly Unicode normalization operation |
|
||||
| tests.py:28:43:28:43 | ControlFlowNode for r | tests.py:1:35:1:41 | ControlFlowNode for ImportMember | tests.py:28:43:28:43 | ControlFlowNode for r | This $@ can reach a $@. | tests.py:1:35:1:41 | ControlFlowNode for ImportMember | user-provided value | tests.py:28:43:28:43 | ControlFlowNode for r | costly Unicode normalization operation |
|
||||
| tests.py:40:43:40:43 | ControlFlowNode for r | tests.py:1:35:1:41 | ControlFlowNode for ImportMember | tests.py:40:43:40:43 | ControlFlowNode for r | This $@ can reach a $@. | tests.py:1:35:1:41 | ControlFlowNode for ImportMember | user-provided value | tests.py:40:43:40:43 | ControlFlowNode for r | costly Unicode normalization operation |
|
||||
| tests.py:52:43:52:43 | ControlFlowNode for r | tests.py:1:35:1:41 | ControlFlowNode for ImportMember | tests.py:52:43:52:43 | ControlFlowNode for r | This $@ can reach a $@. | tests.py:1:35:1:41 | ControlFlowNode for ImportMember | user-provided value | tests.py:52:43:52:43 | ControlFlowNode for r | costly Unicode normalization operation |
|
||||
| tests.py:64:43:64:43 | ControlFlowNode for r | tests.py:1:35:1:41 | ControlFlowNode for ImportMember | tests.py:64:43:64:43 | ControlFlowNode for r | This $@ can reach a $@. | tests.py:1:35:1:41 | ControlFlowNode for ImportMember | user-provided value | tests.py:64:43:64:43 | ControlFlowNode for r | costly Unicode normalization operation |
|
||||
| tests.py:76:43:76:43 | ControlFlowNode for r | tests.py:1:35:1:41 | ControlFlowNode for ImportMember | tests.py:76:43:76:43 | ControlFlowNode for r | This $@ can reach a $@. | tests.py:1:35:1:41 | ControlFlowNode for ImportMember | user-provided value | tests.py:76:43:76:43 | ControlFlowNode for r | costly Unicode normalization operation |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE-770/UnicodeDoS.ql
|
||||
@@ -0,0 +1,129 @@
|
||||
from flask import Flask, jsonify, request
|
||||
import unicodedata
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
STATIC_DIR = "/home/unknown/"
|
||||
|
||||
|
||||
@app.route("/bad_1")
|
||||
def bad_1():
|
||||
# User controlled data
|
||||
file_path = request.args.get("file_path", "")
|
||||
|
||||
# Normalize the file path using NFKC Unicode normalization
|
||||
return (
|
||||
unicodedata.normalize("NFKC", file_path),
|
||||
200,
|
||||
{"Content-Type": "application/octet-stream"},
|
||||
)
|
||||
|
||||
|
||||
@app.route("/bad_2")
|
||||
def bad_2():
|
||||
r = request.args.get("r", "")
|
||||
|
||||
if len(r) >= 10:
|
||||
# Normalize the r using NFKD Unicode normalization
|
||||
r = unicodedata.normalize("NFKD", r)
|
||||
return r, 200, {"Content-Type": "application/octet-stream"}
|
||||
else:
|
||||
return jsonify({"error": "File not found"}), 404
|
||||
|
||||
|
||||
@app.route("/bad_3")
|
||||
def bad_3():
|
||||
r = request.args.get("r", "")
|
||||
length = len(r)
|
||||
if length >= 1_000:
|
||||
# Normalize the r using NFKD Unicode normalization
|
||||
r = unicodedata.normalize("NFKD", r)
|
||||
return r, 200, {"Content-Type": "application/octet-stream"}
|
||||
else:
|
||||
return jsonify({"error": "File not found"}), 404
|
||||
|
||||
|
||||
@app.route("/bad_4")
|
||||
def bad_4():
|
||||
r = request.args.get("r", "")
|
||||
length = len(r)
|
||||
if 1_000 <= length:
|
||||
# Normalize the r using NFKD Unicode normalization
|
||||
r = unicodedata.normalize("NFKD", r)
|
||||
return r, 200, {"Content-Type": "application/octet-stream"}
|
||||
else:
|
||||
return jsonify({"error": "File not found"}), 404
|
||||
|
||||
|
||||
@app.route("/bad_5")
|
||||
def bad_5():
|
||||
r = request.args.get("r", "")
|
||||
length = len(r)
|
||||
if not length < 1_000:
|
||||
# Normalize the r using NFKD Unicode normalization
|
||||
r = unicodedata.normalize("NFKD", r)
|
||||
return r, 200, {"Content-Type": "application/octet-stream"}
|
||||
else:
|
||||
return jsonify({"error": "File not found"}), 404
|
||||
|
||||
|
||||
@app.route("/bad_6")
|
||||
def bad_6():
|
||||
r = request.args.get("r", "")
|
||||
length = len(r)
|
||||
if not 1_000 > length:
|
||||
# Normalize the r using NFKD Unicode normalization
|
||||
r = unicodedata.normalize("NFKD", r)
|
||||
return r, 200, {"Content-Type": "application/octet-stream"}
|
||||
else:
|
||||
return jsonify({"error": "File not found"}), 404
|
||||
|
||||
|
||||
@app.route("/good_1")
|
||||
def good_1():
|
||||
r = request.args.get("r", "")
|
||||
|
||||
if len(r) <= 1_000:
|
||||
# Normalize the r using NFKD Unicode normalization
|
||||
r = unicodedata.normalize("NFKD", r)
|
||||
return r, 200, {"Content-Type": "application/octet-stream"}
|
||||
else:
|
||||
return jsonify({"error": "File not found"}), 404
|
||||
|
||||
|
||||
@app.route("/good_2")
|
||||
def good_2():
|
||||
r = request.args.get("r", "")
|
||||
MAX_LENGTH = 1_000
|
||||
length = len(r)
|
||||
if length <= MAX_LENGTH:
|
||||
# Normalize the r using NFKD Unicode normalization
|
||||
r = unicodedata.normalize("NFKD", r)
|
||||
return r, 200, {"Content-Type": "application/octet-stream"}
|
||||
else:
|
||||
return jsonify({"error": "File not found"}), 404
|
||||
|
||||
@app.route("/good_3")
|
||||
def good_3():
|
||||
r = request.args.get("r", "")
|
||||
MAX_LENGTH = 1_000
|
||||
length = len(r)
|
||||
if not length >= MAX_LENGTH:
|
||||
# Normalize the r using NFKD Unicode normalization
|
||||
r = unicodedata.normalize("NFKD", r)
|
||||
return r, 200, {"Content-Type": "application/octet-stream"}
|
||||
else:
|
||||
return jsonify({"error": "File not found"}), 404
|
||||
|
||||
|
||||
@app.route("/good_4")
|
||||
def good_4():
|
||||
r = request.args.get("r", "")
|
||||
MAX_LENGTH = 1_000
|
||||
length = len(r)
|
||||
if not MAX_LENGTH <= length:
|
||||
# Normalize the r using NFKD Unicode normalization
|
||||
r = unicodedata.normalize("NFKD", r)
|
||||
return r, 200, {"Content-Type": "application/octet-stream"}
|
||||
else:
|
||||
return jsonify({"error": "File not found"}), 404
|
||||
@@ -0,0 +1,6 @@
|
||||
unreachableNode
|
||||
| test2.py:16:17:16:17 | ControlFlowNode for y | Unreachable node in step of kind load bar. |
|
||||
| test2.py:25:23:25:23 | ControlFlowNode for x | Unreachable node in step of kind load attribute. |
|
||||
| test2.py:25:23:25:23 | ControlFlowNode for x | Unreachable node in step of kind simpleLocalSmallStep. |
|
||||
| test2.py:26:17:26:17 | ControlFlowNode for y | Unreachable node in step of kind load bar. |
|
||||
| test2.py:27:23:27:23 | ControlFlowNode for x | Unreachable node in step of kind simpleLocalSmallStep. |
|
||||
Reference in New Issue
Block a user