mirror of
https://github.com/github/codeql.git
synced 2026-04-25 16:55:19 +02:00
Merge branch 'main' into tausbn/python-add-support-for-python-3.12-type-syntax
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added basic flow for attributes defined on classes, when the attribute lookup is on a direct reference to that class (so not instance, cls parameter, or self parameter). Example: class definition `class Foo: my_tuples = (dangerous, safe)` and usage `SINK(Foo.my_tuples[0])`.
|
||||
@@ -5,13 +5,18 @@
|
||||
// If you add modeling of a new framework/library, remember to add it to the docs in
|
||||
// `docs/codeql/reusables/supported-frameworks.rst`
|
||||
private import semmle.python.frameworks.Aioch
|
||||
private import semmle.python.frameworks.Aiofile
|
||||
private import semmle.python.frameworks.Aiofiles
|
||||
private import semmle.python.frameworks.Aiohttp
|
||||
private import semmle.python.frameworks.Aiomysql
|
||||
private import semmle.python.frameworks.Aiopg
|
||||
private import semmle.python.frameworks.Aiosqlite
|
||||
private import semmle.python.frameworks.Anyio
|
||||
private import semmle.python.frameworks.Asyncpg
|
||||
private import semmle.python.frameworks.Baize
|
||||
private import semmle.python.frameworks.BSon
|
||||
private import semmle.python.frameworks.CassandraDriver
|
||||
private import semmle.python.frameworks.Cherrypy
|
||||
private import semmle.python.frameworks.ClickhouseDriver
|
||||
private import semmle.python.frameworks.Cryptodome
|
||||
private import semmle.python.frameworks.Cryptography
|
||||
@@ -54,6 +59,7 @@ private import semmle.python.frameworks.Requests
|
||||
private import semmle.python.frameworks.RestFramework
|
||||
private import semmle.python.frameworks.Rsa
|
||||
private import semmle.python.frameworks.RuamelYaml
|
||||
private import semmle.python.frameworks.Sanic
|
||||
private import semmle.python.frameworks.ServerLess
|
||||
private import semmle.python.frameworks.Setuptools
|
||||
private import semmle.python.frameworks.Simplejson
|
||||
|
||||
@@ -1352,7 +1352,10 @@ abstract class DataFlowCall extends TDataFlowCall {
|
||||
abstract ControlFlowNode getNode();
|
||||
|
||||
/** Gets the enclosing callable of this call. */
|
||||
abstract DataFlowCallable getEnclosingCallable();
|
||||
DataFlowCallable getEnclosingCallable() { result = getCallableScope(this.getScope()) }
|
||||
|
||||
/** Gets the scope of this node, if any. */
|
||||
abstract Scope getScope();
|
||||
|
||||
/** Gets the location of this dataflow call. */
|
||||
abstract Location getLocation();
|
||||
@@ -1400,7 +1403,7 @@ class NormalCall extends ExtractedDataFlowCall, TNormalCall {
|
||||
|
||||
override ControlFlowNode getNode() { result = call }
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getScope() }
|
||||
override Scope getScope() { result = call.getScope() }
|
||||
|
||||
override DataFlowCallable getCallable() { result.(DataFlowFunction).getScope() = target }
|
||||
|
||||
@@ -1450,7 +1453,7 @@ class PotentialLibraryCall extends ExtractedDataFlowCall, TPotentialLibraryCall
|
||||
|
||||
override ControlFlowNode getNode() { result = call }
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getScope() }
|
||||
override Scope getScope() { result = call.getScope() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1474,6 +1477,8 @@ class SummaryCall extends DataFlowCall, TSummaryCall {
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() { result.asLibraryCallable() = c }
|
||||
|
||||
override Scope getScope() { none() }
|
||||
|
||||
override DataFlowCallable getCallable() { none() }
|
||||
|
||||
override ArgumentNode getArgument(ArgumentPosition apos) { none() }
|
||||
|
||||
@@ -846,10 +846,27 @@ predicate comprehensionStoreStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
|
||||
* ```
|
||||
* data flows from `x` to the attribute `foo` of (the post-update node for) `obj`.
|
||||
*/
|
||||
predicate attributeStoreStep(Node nodeFrom, AttributeContent c, PostUpdateNode nodeTo) {
|
||||
exists(AttrWrite write |
|
||||
write.accesses(nodeTo.getPreUpdateNode(), c.getAttribute()) and
|
||||
nodeFrom = write.getValue()
|
||||
predicate attributeStoreStep(Node nodeFrom, AttributeContent c, Node nodeTo) {
|
||||
exists(Node object |
|
||||
// Normally we target a PostUpdateNode. However, for class definitions the class
|
||||
// is only constructed after evaluating its' entire scope, so in terms of python
|
||||
// evaluations there is no post or pre update nodes, just one node for the class
|
||||
// expression. Therefore we target the class expression directly.
|
||||
//
|
||||
// Note: Due to the way we handle decorators, using a class decorator will result in
|
||||
// there being a post-update node for the class (argument to the decorator). We do
|
||||
// not want to differentiate between these two cases, so still target the class
|
||||
// expression directly.
|
||||
object = nodeTo.(PostUpdateNode).getPreUpdateNode() and
|
||||
not object.asExpr() instanceof ClassExpr
|
||||
or
|
||||
object = nodeTo and
|
||||
object.asExpr() instanceof ClassExpr
|
||||
|
|
||||
exists(AttrWrite write |
|
||||
write.accesses(object, c.getAttribute()) and
|
||||
nodeFrom = write.getValue()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1044,3 +1061,11 @@ class ContentApprox = Unit;
|
||||
/** Gets an approximated value for content `c`. */
|
||||
pragma[inline]
|
||||
ContentApprox getContentApprox(Content c) { any() }
|
||||
|
||||
/** Helper for `.getEnclosingCallable`. */
|
||||
DataFlowCallable getCallableScope(Scope s) {
|
||||
result.getScope() = s
|
||||
or
|
||||
not exists(DataFlowCallable c | c.getScope() = s) and
|
||||
result = getCallableScope(s.getEnclosingScope())
|
||||
}
|
||||
|
||||
@@ -117,14 +117,6 @@ newtype TNode =
|
||||
exists(ParameterPosition ppos | ppos.isKeyword(_) | exists(callable.getParameter(ppos)))
|
||||
}
|
||||
|
||||
/** Helper for `Node::getEnclosingCallable`. */
|
||||
private DataFlowCallable getCallableScope(Scope s) {
|
||||
result.getScope() = s
|
||||
or
|
||||
not exists(DataFlowCallable c | c.getScope() = s) and
|
||||
result = getCallableScope(s.getEnclosingScope())
|
||||
}
|
||||
|
||||
private import semmle.python.internal.CachedStages
|
||||
|
||||
/**
|
||||
|
||||
42
python/ql/lib/semmle/python/frameworks/Aiofile.qll
Normal file
42
python/ql/lib/semmle/python/frameworks/Aiofile.qll
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `aiofile` PyPI package.
|
||||
*
|
||||
* See https://pypi.org/project/aiofile.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
* Provides models for the `aiofile` PyPI package.
|
||||
*
|
||||
* See https://pypi.org/project/aiofile.
|
||||
*/
|
||||
private module Aiofile {
|
||||
/**
|
||||
* A call to the `async_open` function or `AIOFile` constructor from `aiofile` as a sink for Filesystem access.
|
||||
*/
|
||||
class FileResponseCall extends FileSystemAccess::Range, API::CallNode {
|
||||
string methodName;
|
||||
|
||||
FileResponseCall() {
|
||||
this = API::moduleImport("aiofile").getMember("async_open").getACall() and
|
||||
methodName = "async_open"
|
||||
or
|
||||
this = API::moduleImport("aiofile").getMember("AIOFile").getACall() and
|
||||
methodName = "AIOFile"
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
result = this.getParameter(0, "file_specifier").asSink() and
|
||||
methodName = "async_open"
|
||||
or
|
||||
result = this.getParameter(0, "filename").asSink() and
|
||||
methodName = "AIOFile"
|
||||
}
|
||||
}
|
||||
}
|
||||
28
python/ql/lib/semmle/python/frameworks/Aiofiles.qll
Normal file
28
python/ql/lib/semmle/python/frameworks/Aiofiles.qll
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `aiofiles` PyPI package.
|
||||
*
|
||||
* See https://pypi.org/project/aiofiles.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
* Provides models for the `aiofiles` PyPI package.
|
||||
*
|
||||
* See https://pypi.org/project/aiofiles.
|
||||
*/
|
||||
private module Aiofiles {
|
||||
/**
|
||||
* A call to the `open` function from `aiofiles` as a sink for Filesystem access.
|
||||
*/
|
||||
class FileResponseCall extends FileSystemAccess::Range, API::CallNode {
|
||||
FileResponseCall() { this = API::moduleImport("aiofiles").getMember("open").getACall() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getParameter(0, "file").asSink() }
|
||||
}
|
||||
}
|
||||
54
python/ql/lib/semmle/python/frameworks/Anyio.qll
Normal file
54
python/ql/lib/semmle/python/frameworks/Anyio.qll
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `anyio` PyPI package.
|
||||
*
|
||||
* See https://pypi.org/project/anyio.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
* Provides models for the `anyio` PyPI package.
|
||||
*
|
||||
* See https://pypi.org/project/anyio.
|
||||
*/
|
||||
private module Anyio {
|
||||
/**
|
||||
* A call to the `from_path` function from `FileReadStream` or `FileWriteStream` constructors of `anyio.streams.file` as a sink for Filesystem access.
|
||||
*/
|
||||
class FileStreamCall extends FileSystemAccess::Range, API::CallNode {
|
||||
FileStreamCall() {
|
||||
this =
|
||||
API::moduleImport("anyio")
|
||||
.getMember("streams")
|
||||
.getMember("file")
|
||||
.getMember(["FileReadStream", "FileWriteStream"])
|
||||
.getMember("from_path")
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getParameter(0, "path").asSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `Path` constructor from `anyio` as a sink for Filesystem access.
|
||||
*/
|
||||
class PathCall extends FileSystemAccess::Range, API::CallNode {
|
||||
PathCall() { this = API::moduleImport("anyio").getMember("Path").getACall() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getParameter(0).asSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `open_file` function from `anyio` as a sink for Filesystem access.
|
||||
*/
|
||||
class OpenFileCall extends FileSystemAccess::Range, API::CallNode {
|
||||
OpenFileCall() { this = API::moduleImport("anyio").getMember("open_file").getACall() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getParameter(0, "file").asSink() }
|
||||
}
|
||||
}
|
||||
35
python/ql/lib/semmle/python/frameworks/Baize.qll
Normal file
35
python/ql/lib/semmle/python/frameworks/Baize.qll
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `baize` PyPI package.
|
||||
*
|
||||
* See https://pypi.org/project/baize.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
|
||||
private import semmle.python.frameworks.Stdlib
|
||||
|
||||
/**
|
||||
* Provides models for `baize` PyPI package.
|
||||
*
|
||||
* See https://pypi.org/project/baize.
|
||||
*/
|
||||
module Baize {
|
||||
/**
|
||||
* A call to the `baize.asgi.FileResponse` constructor as a sink for Filesystem access.
|
||||
*
|
||||
* it is not contained to Starlette source code but it is mentioned in documents as an alternative to Starlette FileResponse
|
||||
*/
|
||||
class BaizeFileResponseCall extends FileSystemAccess::Range, API::CallNode {
|
||||
BaizeFileResponseCall() {
|
||||
this = API::moduleImport("baize").getMember("asgi").getMember("FileResponse").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
result = this.getParameter(0, "filepath").asSink()
|
||||
}
|
||||
}
|
||||
}
|
||||
48
python/ql/lib/semmle/python/frameworks/Cherrypy.qll
Normal file
48
python/ql/lib/semmle/python/frameworks/Cherrypy.qll
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `cherrypy` PyPI package.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
* Provides models for the `cherrypy` PyPI package.
|
||||
* See https://cherrypy.dev/.
|
||||
*/
|
||||
private module Cherrypy {
|
||||
/**
|
||||
* Holds for an instance of `cherrypy.lib.static`
|
||||
*/
|
||||
API::Node libStatic() {
|
||||
result = API::moduleImport("cherrypy").getMember("lib").getMember("static")
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `serve_file` or `serve_download`or `staticfile` functions of `cherrypy.lib.static` as a sink for Filesystem access.
|
||||
*/
|
||||
class FileResponseCall extends FileSystemAccess::Range, API::CallNode {
|
||||
string funcName;
|
||||
|
||||
FileResponseCall() {
|
||||
this = libStatic().getMember("staticfile").getACall() and
|
||||
funcName = "staticfile"
|
||||
or
|
||||
this = libStatic().getMember("serve_file").getACall() and
|
||||
funcName = "serve_file"
|
||||
or
|
||||
this = libStatic().getMember("serve_download").getACall() and
|
||||
funcName = "serve_download"
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
result = this.getParameter(0, "path").asSink() and funcName = ["serve_download", "serve_file"]
|
||||
or
|
||||
result = this.getParameter(0, "filename").asSink() and
|
||||
funcName = "staticfile"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,6 +128,8 @@ private module CryptodomeModel {
|
||||
this = newCall.getReturn().getMember(methodName).getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getInitialization() { result = newCall }
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.matchesName(cipherName) }
|
||||
|
||||
override DataFlow::Node getAnInput() {
|
||||
@@ -181,21 +183,23 @@ private module CryptodomeModel {
|
||||
class CryptodomeGenericSignatureOperation extends Cryptography::CryptographicOperation::Range,
|
||||
DataFlow::CallCfgNode
|
||||
{
|
||||
API::CallNode newCall;
|
||||
string methodName;
|
||||
string signatureName;
|
||||
|
||||
CryptodomeGenericSignatureOperation() {
|
||||
methodName in ["sign", "verify"] and
|
||||
this =
|
||||
newCall =
|
||||
API::moduleImport(["Crypto", "Cryptodome"])
|
||||
.getMember("Signature")
|
||||
.getMember(signatureName)
|
||||
.getMember("new")
|
||||
.getReturn()
|
||||
.getMember(methodName)
|
||||
.getACall()
|
||||
.getACall() and
|
||||
this = newCall.getReturn().getMember(methodName).getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getInitialization() { result = newCall }
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() {
|
||||
result.matchesName(signatureName)
|
||||
}
|
||||
@@ -221,19 +225,23 @@ private module CryptodomeModel {
|
||||
class CryptodomeGenericHashOperation extends Cryptography::CryptographicOperation::Range,
|
||||
DataFlow::CallCfgNode
|
||||
{
|
||||
API::CallNode newCall;
|
||||
string hashName;
|
||||
|
||||
CryptodomeGenericHashOperation() {
|
||||
exists(API::Node hashModule |
|
||||
hashModule =
|
||||
API::moduleImport(["Crypto", "Cryptodome"]).getMember("Hash").getMember(hashName)
|
||||
API::moduleImport(["Crypto", "Cryptodome"]).getMember("Hash").getMember(hashName) and
|
||||
newCall = hashModule.getMember("new").getACall()
|
||||
|
|
||||
this = hashModule.getMember("new").getACall()
|
||||
this = newCall
|
||||
or
|
||||
this = hashModule.getMember("new").getReturn().getMember("update").getACall()
|
||||
this = newCall.getReturn().getMember("update").getACall()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getInitialization() { result = newCall }
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.matchesName(hashName) }
|
||||
|
||||
override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("data")] }
|
||||
|
||||
@@ -209,18 +209,18 @@ private module CryptographyModel {
|
||||
class CryptographyGenericCipherOperation extends Cryptography::CryptographicOperation::Range,
|
||||
DataFlow::MethodCallNode
|
||||
{
|
||||
API::CallNode init;
|
||||
string algorithmName;
|
||||
string modeName;
|
||||
|
||||
CryptographyGenericCipherOperation() {
|
||||
this =
|
||||
cipherInstance(algorithmName, modeName)
|
||||
.getMember(["decryptor", "encryptor"])
|
||||
.getReturn()
|
||||
.getMember(["update", "update_into"])
|
||||
.getACall()
|
||||
init =
|
||||
cipherInstance(algorithmName, modeName).getMember(["decryptor", "encryptor"]).getACall() and
|
||||
this = init.getReturn().getMember(["update", "update_into"]).getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getInitialization() { result = init }
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() {
|
||||
result.matchesName(algorithmName)
|
||||
}
|
||||
@@ -247,19 +247,17 @@ private module CryptographyModel {
|
||||
}
|
||||
|
||||
/** Gets a reference to a Hash instance using algorithm with `algorithmName`. */
|
||||
private API::Node hashInstance(string algorithmName) {
|
||||
exists(API::CallNode call | result = call.getReturn() |
|
||||
call =
|
||||
API::moduleImport("cryptography")
|
||||
.getMember("hazmat")
|
||||
.getMember("primitives")
|
||||
.getMember("hashes")
|
||||
.getMember("Hash")
|
||||
.getACall() and
|
||||
algorithmClassRef(algorithmName).getReturn().getAValueReachableFromSource() in [
|
||||
call.getArg(0), call.getArgByName("algorithm")
|
||||
]
|
||||
)
|
||||
private API::CallNode hashInstance(string algorithmName) {
|
||||
result =
|
||||
API::moduleImport("cryptography")
|
||||
.getMember("hazmat")
|
||||
.getMember("primitives")
|
||||
.getMember("hashes")
|
||||
.getMember("Hash")
|
||||
.getACall() and
|
||||
algorithmClassRef(algorithmName).getReturn().getAValueReachableFromSource() in [
|
||||
result.getArg(0), result.getArgByName("algorithm")
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -268,12 +266,16 @@ private module CryptographyModel {
|
||||
class CryptographyGenericHashOperation extends Cryptography::CryptographicOperation::Range,
|
||||
DataFlow::MethodCallNode
|
||||
{
|
||||
API::CallNode init;
|
||||
string algorithmName;
|
||||
|
||||
CryptographyGenericHashOperation() {
|
||||
this = hashInstance(algorithmName).getMember("update").getACall()
|
||||
init = hashInstance(algorithmName) and
|
||||
this = init.getReturn().getMember("update").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getInitialization() { result = init }
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() {
|
||||
result.matchesName(algorithmName)
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@ private module Rsa {
|
||||
class RsaEncryptCall extends Cryptography::CryptographicOperation::Range, DataFlow::CallCfgNode {
|
||||
RsaEncryptCall() { this = API::moduleImport("rsa").getMember("encrypt").getACall() }
|
||||
|
||||
override DataFlow::Node getInitialization() { result = this }
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.getName() = "RSA" }
|
||||
|
||||
override DataFlow::Node getAnInput() {
|
||||
@@ -54,6 +56,8 @@ private module Rsa {
|
||||
class RsaDecryptCall extends Cryptography::CryptographicOperation::Range, DataFlow::CallCfgNode {
|
||||
RsaDecryptCall() { this = API::moduleImport("rsa").getMember("decrypt").getACall() }
|
||||
|
||||
override DataFlow::Node getInitialization() { result = this }
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.getName() = "RSA" }
|
||||
|
||||
override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("crypto")] }
|
||||
@@ -69,6 +73,8 @@ private module Rsa {
|
||||
class RsaSignCall extends Cryptography::CryptographicOperation::Range, DataFlow::CallCfgNode {
|
||||
RsaSignCall() { this = API::moduleImport("rsa").getMember("sign").getACall() }
|
||||
|
||||
override DataFlow::Node getInitialization() { result = this }
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() {
|
||||
// signature part
|
||||
result.getName() = "RSA"
|
||||
@@ -96,6 +102,8 @@ private module Rsa {
|
||||
class RsaVerifyCall extends Cryptography::CryptographicOperation::Range, DataFlow::CallCfgNode {
|
||||
RsaVerifyCall() { this = API::moduleImport("rsa").getMember("verify").getACall() }
|
||||
|
||||
override DataFlow::Node getInitialization() { result = this }
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() {
|
||||
// note that technically there is also a hashing operation going on but we don't
|
||||
// know what algorithm is used up front, since it is encoded in the signature
|
||||
@@ -121,6 +129,8 @@ private module Rsa {
|
||||
{
|
||||
RsaComputeHashCall() { this = API::moduleImport("rsa").getMember("compute_hash").getACall() }
|
||||
|
||||
override DataFlow::Node getInitialization() { result = this }
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() {
|
||||
exists(StrConst str, DataFlow::Node hashNameArg |
|
||||
hashNameArg in [this.getArg(1), this.getArgByName("method_name")] and
|
||||
@@ -144,6 +154,8 @@ private module Rsa {
|
||||
class RsaSignHashCall extends Cryptography::CryptographicOperation::Range, DataFlow::CallCfgNode {
|
||||
RsaSignHashCall() { this = API::moduleImport("rsa").getMember("sign_hash").getACall() }
|
||||
|
||||
override DataFlow::Node getInitialization() { result = this }
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.getName() = "RSA" }
|
||||
|
||||
override DataFlow::Node getAnInput() {
|
||||
|
||||
42
python/ql/lib/semmle/python/frameworks/Sanic.qll
Normal file
42
python/ql/lib/semmle/python/frameworks/Sanic.qll
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `sanic` PyPI package.
|
||||
* See https://sanic.dev/.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
* Provides models for the `sanic` PyPI package.
|
||||
* See https://sanic.dev/.
|
||||
*/
|
||||
private module Sanic {
|
||||
/**
|
||||
* Provides models for Sanic applications (an instance of `sanic.Sanic`).
|
||||
*/
|
||||
module App {
|
||||
/** Gets a reference to a Sanic application (an instance of `sanic.Sanic`). */
|
||||
API::Node instance() { result = API::moduleImport("sanic").getMember("Sanic").getReturn() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `file` or `file_stream` functions of `sanic.response` as a sink for Filesystem access.
|
||||
*/
|
||||
class FileResponseCall extends FileSystemAccess::Range, API::CallNode {
|
||||
FileResponseCall() {
|
||||
this =
|
||||
API::moduleImport("sanic")
|
||||
.getMember("response")
|
||||
.getMember(["file", "file_stream"])
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
result = this.getParameter(0, "location").asSink()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,4 +163,16 @@ module Starlette {
|
||||
|
||||
/** DEPRECATED: Alias for Url */
|
||||
deprecated module URL = Url;
|
||||
|
||||
/**
|
||||
* A call to the `starlette.responses.FileResponse` constructor as a sink for Filesystem access.
|
||||
*/
|
||||
class FileResponseCall extends FileSystemAccess::Range, API::CallNode {
|
||||
FileResponseCall() {
|
||||
this =
|
||||
API::moduleImport("starlette").getMember("responses").getMember("FileResponse").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getParameter(0, "path").asSink() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1479,6 +1479,26 @@ private module StdlibPrivate {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `io.FileIO` constructor.
|
||||
* See https://docs.python.org/3/library/io.html#io.FileIO
|
||||
*/
|
||||
private class FileIOCall extends FileSystemAccess::Range, API::CallNode {
|
||||
FileIOCall() { this = API::moduleImport("io").getMember("FileIO").getACall() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getParameter(0, "file").asSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `io.open_code` function.
|
||||
* See https://docs.python.org/3.11/library/io.html#io.open_code
|
||||
*/
|
||||
private class OpenCodeCall extends FileSystemAccess::Range, API::CallNode {
|
||||
OpenCodeCall() { this = API::moduleImport("io").getMember("open_code").getACall() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getParameter(0, "path").asSink() }
|
||||
}
|
||||
|
||||
/** Gets a reference to an open file. */
|
||||
private DataFlow::TypeTrackingNode openFile(DataFlow::TypeTracker t, FileSystemAccess openCall) {
|
||||
t.start() and
|
||||
@@ -2747,6 +2767,8 @@ private module StdlibPrivate {
|
||||
exists(this.getParameter(1, "data"))
|
||||
}
|
||||
|
||||
override DataFlow::Node getInitialization() { result = this }
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.matchesName(hashName) }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = this.getParameter(1, "data").asSink() }
|
||||
@@ -2758,12 +2780,16 @@ private module StdlibPrivate {
|
||||
* A hashing operation by using the `update` method on the result of calling the `hashlib.new` function.
|
||||
*/
|
||||
class HashlibNewUpdateCall extends Cryptography::CryptographicOperation::Range, API::CallNode {
|
||||
API::CallNode init;
|
||||
string hashName;
|
||||
|
||||
HashlibNewUpdateCall() {
|
||||
this = hashlibNewCall(hashName).getReturn().getMember("update").getACall()
|
||||
init = hashlibNewCall(hashName) and
|
||||
this = init.getReturn().getMember("update").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getInitialization() { result = init }
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.matchesName(hashName) }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = this.getArg(0) }
|
||||
@@ -2802,7 +2828,14 @@ private module StdlibPrivate {
|
||||
* (such as `hashlib.md5`), by calling its' `update` method.
|
||||
*/
|
||||
class HashlibHashClassUpdateCall extends HashlibGenericHashOperation {
|
||||
HashlibHashClassUpdateCall() { this = hashClass.getReturn().getMember("update").getACall() }
|
||||
API::CallNode init;
|
||||
|
||||
HashlibHashClassUpdateCall() {
|
||||
init = hashClass.getACall() and
|
||||
this = hashClass.getReturn().getMember("update").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getInitialization() { result = init }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = this.getArg(0) }
|
||||
}
|
||||
@@ -2819,6 +2852,8 @@ private module StdlibPrivate {
|
||||
exists([this.getArg(0), this.getArgByName("string")])
|
||||
}
|
||||
|
||||
override DataFlow::Node getInitialization() { result = this }
|
||||
|
||||
override DataFlow::Node getAnInput() {
|
||||
result = this.getArg(0)
|
||||
or
|
||||
@@ -2865,6 +2900,8 @@ private module StdlibPrivate {
|
||||
exists(this.getParameter(1, "msg").asSink())
|
||||
}
|
||||
|
||||
override DataFlow::Node getInitialization() { result = this }
|
||||
|
||||
override API::Node getDigestArg() { result = digestArg }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = this.getParameter(1, "msg").asSink() }
|
||||
@@ -2876,12 +2913,16 @@ private module StdlibPrivate {
|
||||
* See https://docs.python.org/3.11/library/hmac.html#hmac.HMAC.update
|
||||
*/
|
||||
class HmacUpdateCall extends HmacCryptographicOperation {
|
||||
API::CallNode init;
|
||||
API::Node digestArg;
|
||||
|
||||
HmacUpdateCall() {
|
||||
this = getHmacConstructorCall(digestArg).getReturn().getMember("update").getACall()
|
||||
init = getHmacConstructorCall(digestArg) and
|
||||
this = init.getReturn().getMember("update").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getInitialization() { result = init }
|
||||
|
||||
override API::Node getDigestArg() { result = digestArg }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = this.getParameter(0, "msg").asSink() }
|
||||
@@ -2895,6 +2936,8 @@ private module StdlibPrivate {
|
||||
class HmacDigestCall extends HmacCryptographicOperation {
|
||||
HmacDigestCall() { this = API::moduleImport("hmac").getMember("digest").getACall() }
|
||||
|
||||
override DataFlow::Node getInitialization() { result = this }
|
||||
|
||||
override API::Node getDigestArg() { result = this.getParameter(2, "digest") }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = this.getParameter(1, "msg").asSink() }
|
||||
|
||||
@@ -40,6 +40,9 @@ module Cryptography {
|
||||
/** Gets the algorithm used, if it matches a known `CryptographicAlgorithm`. */
|
||||
CryptographicAlgorithm getAlgorithm() { result = super.getAlgorithm() }
|
||||
|
||||
/** Gets the data-flow node where the cryptographic algorithm used in this operation is configured. */
|
||||
DataFlow::Node getInitialization() { result = super.getInitialization() }
|
||||
|
||||
/** Gets an input the algorithm is used on, for example the plain text input to be encrypted. */
|
||||
DataFlow::Node getAnInput() { result = super.getAnInput() }
|
||||
|
||||
@@ -65,6 +68,9 @@ module Cryptography {
|
||||
* extend `CryptographicOperation` instead.
|
||||
*/
|
||||
abstract class Range extends DataFlow::Node {
|
||||
/** Gets the data-flow node where the cryptographic algorithm used in this operation is configured. */
|
||||
abstract DataFlow::Node getInitialization();
|
||||
|
||||
/** Gets the algorithm used, if it matches a known `CryptographicAlgorithm`. */
|
||||
abstract CryptographicAlgorithm getAlgorithm();
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* James Kirrage, Asiri Rathnayake, Hayo Thielecke: Static Analysis for
|
||||
* Regular Expression Denial-of-Service Attacks. NSS 2013.
|
||||
* (http://www.cs.bham.ac.uk/~hxt/research/reg-exp-sec.pdf)
|
||||
* (https://arxiv.org/abs/1301.0849)
|
||||
* Asiri Rathnayake, Hayo Thielecke: Static Analysis for Regular Expression
|
||||
* Exponential Runtime via Substructural Logics. 2014.
|
||||
* (https://www.cs.bham.ac.uk/~hxt/research/redos_full.pdf)
|
||||
|
||||
@@ -13,18 +13,15 @@
|
||||
import python
|
||||
import semmle.python.Concepts
|
||||
|
||||
from
|
||||
Cryptography::CryptographicOperation operation, Cryptography::CryptographicAlgorithm algorithm,
|
||||
string msgPrefix
|
||||
from Cryptography::CryptographicOperation operation, string msgPrefix
|
||||
where
|
||||
algorithm = operation.getAlgorithm() and
|
||||
// `Cryptography::HashingAlgorithm` and `Cryptography::PasswordHashingAlgorithm` are
|
||||
// handled by `py/weak-sensitive-data-hashing`
|
||||
algorithm instanceof Cryptography::EncryptionAlgorithm and
|
||||
(
|
||||
exists(Cryptography::EncryptionAlgorithm algorithm | algorithm = operation.getAlgorithm() |
|
||||
algorithm.isWeak() and
|
||||
msgPrefix = "The cryptographic algorithm " + operation.getAlgorithm().getName()
|
||||
msgPrefix = "The cryptographic algorithm " + algorithm.getName()
|
||||
)
|
||||
or
|
||||
operation.getBlockMode().isWeak() and msgPrefix = "The block mode " + operation.getBlockMode()
|
||||
select operation, msgPrefix + " is broken or weak, and should not be used."
|
||||
select operation, "$@ is broken or weak, and should not be used.", operation.getInitialization(),
|
||||
msgPrefix
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/ReDoS">ReDoS</a>.</li>
|
||||
<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/Time_complexity">Time complexity</a>.</li>
|
||||
<li>James Kirrage, Asiri Rathnayake, Hayo Thielecke:
|
||||
<a href="http://www.cs.bham.ac.uk/~hxt/research/reg-exp-sec.pdf">Static Analysis for Regular Expression Denial-of-Service Attack</a>.
|
||||
<a href="https://arxiv.org/abs/1301.0849">Static Analysis for Regular Expression Denial-of-Service Attack</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
1
python/ql/src/Summary/options
Normal file
1
python/ql/src/Summary/options
Normal file
@@ -0,0 +1 @@
|
||||
semmle-extractor-options: --lang=3
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added modeling of more `FileSystemAccess` in packages `cherrypy`, `aiofile`, `aiofiles`, `anyio`, `sanic`, `starlette`, `baize`, and `io`. This will mainly affect the _Uncontrolled data used in path expression_ (`py/path-injection`) query.
|
||||
1
python/ql/test/2/library-tests/PointsTo/imports2/options
Normal file
1
python/ql/test/2/library-tests/PointsTo/imports2/options
Normal file
@@ -0,0 +1 @@
|
||||
semmle-extractor-options: --lang=2 --max-import-depth=2 -r package
|
||||
1
python/ql/test/2/library-tests/modules/usage/options
Normal file
1
python/ql/test/2/library-tests/modules/usage/options
Normal file
@@ -0,0 +1 @@
|
||||
semmle-extractor-options: --lang=2 -F script
|
||||
@@ -0,0 +1,57 @@
|
||||
| __init__.py | 1 | ControlFlowNode for ImportExpr | Module package.module | ControlFlowNode for ImportExpr |
|
||||
| __init__.py | 2 | ControlFlowNode for ImportMember | Function module | ControlFlowNode for FunctionExpr |
|
||||
| __init__.py | 2 | ControlFlowNode for module | Function module | ControlFlowNode for FunctionExpr |
|
||||
| __init__.py | 4 | ControlFlowNode for ImportExpr | Module package | ControlFlowNode for ImportExpr |
|
||||
| __init__.py | 4 | ControlFlowNode for ImportMember | Module package.module2 | Entry node for Module package.module2 |
|
||||
| __init__.py | 4 | ControlFlowNode for module3 | Module package.module2 | Entry node for Module package.module2 |
|
||||
| __init__.py | 5 | ControlFlowNode for IntegerLiteral | int 7 | ControlFlowNode for IntegerLiteral |
|
||||
| __init__.py | 5 | ControlFlowNode for module2 | int 7 | ControlFlowNode for IntegerLiteral |
|
||||
| __init__.py | 6 | ControlFlowNode for ImportExpr | Module package | ControlFlowNode for ImportExpr |
|
||||
| __init__.py | 6 | ControlFlowNode for ImportMember | int 7 | ControlFlowNode for IntegerLiteral |
|
||||
| __init__.py | 6 | ControlFlowNode for module4 | int 7 | ControlFlowNode for IntegerLiteral |
|
||||
| __init__.py | 7 | ControlFlowNode for ImportExpr | Module package | ControlFlowNode for ImportExpr |
|
||||
| __init__.py | 7 | ControlFlowNode for ImportMember | Module package.module2 | Entry node for Module package.module2 |
|
||||
| __init__.py | 7 | ControlFlowNode for module5 | Module package.module2 | Entry node for Module package.module2 |
|
||||
| __init__.py | 8 | ControlFlowNode for ImportExpr | Module package | ControlFlowNode for ImportExpr |
|
||||
| __init__.py | 8 | ControlFlowNode for ImportMember | Module package.moduleX | Entry node for Module package.moduleX |
|
||||
| __init__.py | 8 | ControlFlowNode for moduleX | Module package.moduleX | Entry node for Module package.moduleX |
|
||||
| module2.py | 1 | ControlFlowNode for IntegerLiteral | int 0 | ControlFlowNode for IntegerLiteral |
|
||||
| module2.py | 1 | ControlFlowNode for x | int 0 | ControlFlowNode for IntegerLiteral |
|
||||
| module.py | 2 | ControlFlowNode for FunctionExpr | Function module | ControlFlowNode for FunctionExpr |
|
||||
| module.py | 2 | ControlFlowNode for module | Function module | ControlFlowNode for FunctionExpr |
|
||||
| moduleX.py | 1 | ControlFlowNode for ClassExpr | class Y | ControlFlowNode for ClassExpr |
|
||||
| moduleX.py | 1 | ControlFlowNode for Y | class Y | ControlFlowNode for ClassExpr |
|
||||
| moduleX.py | 1 | ControlFlowNode for object | builtin-class object | ControlFlowNode for object |
|
||||
| test.py | 1 | ControlFlowNode for ImportExpr | Module package | ControlFlowNode for ImportExpr |
|
||||
| test.py | 2 | ControlFlowNode for ImportMember | Function module | ControlFlowNode for FunctionExpr |
|
||||
| test.py | 2 | ControlFlowNode for module | Function module | ControlFlowNode for FunctionExpr |
|
||||
| test.py | 4 | ControlFlowNode for ImportExpr | Module package | ControlFlowNode for ImportExpr |
|
||||
| test.py | 5 | ControlFlowNode for ImportMember | Module package.x | Entry node for Module package.x |
|
||||
| test.py | 5 | ControlFlowNode for x | Module package.x | Entry node for Module package.x |
|
||||
| test.py | 8 | ControlFlowNode for C | class C | ControlFlowNode for ClassExpr |
|
||||
| test.py | 8 | ControlFlowNode for ClassExpr | class C | ControlFlowNode for ClassExpr |
|
||||
| test.py | 8 | ControlFlowNode for object | builtin-class object | ControlFlowNode for object |
|
||||
| test.py | 10 | ControlFlowNode for ImportExpr | Module package | ControlFlowNode for ImportExpr |
|
||||
| test.py | 10 | ControlFlowNode for ImportMember | int 7 | ControlFlowNode for IntegerLiteral |
|
||||
| test.py | 10 | ControlFlowNode for module2 | int 7 | ControlFlowNode for IntegerLiteral |
|
||||
| test.py | 12 | ControlFlowNode for FunctionExpr | Function f | ControlFlowNode for FunctionExpr |
|
||||
| test.py | 12 | ControlFlowNode for f | Function f | ControlFlowNode for FunctionExpr |
|
||||
| test.py | 13 | ControlFlowNode for ImportExpr | Module package | ControlFlowNode for ImportExpr |
|
||||
| test.py | 13 | ControlFlowNode for ImportMember | Module package.x | Entry node for Module package.x |
|
||||
| test.py | 13 | ControlFlowNode for x | Module package.x | Entry node for Module package.x |
|
||||
| test.py | 15 | ControlFlowNode for ImportExpr | Module package | ControlFlowNode for ImportExpr |
|
||||
| test.py | 15 | ControlFlowNode for ImportMember | Module package.moduleX | Entry node for Module package.moduleX |
|
||||
| test.py | 15 | ControlFlowNode for moduleX | Module package.moduleX | Entry node for Module package.moduleX |
|
||||
| test.py | 16 | ControlFlowNode for Attribute | class Y | ControlFlowNode for ClassExpr |
|
||||
| test.py | 16 | ControlFlowNode for moduleX | Module package.moduleX | Entry node for Module package.moduleX |
|
||||
| test.py | 19 | ControlFlowNode for ImportExpr | Module tty | ControlFlowNode for ImportExpr |
|
||||
| test.py | 19 | ControlFlowNode for tty | Module tty | ControlFlowNode for ImportExpr |
|
||||
| test.py | 22 | ControlFlowNode for Attribute | Builtin-function exc_info | ControlFlowNode for from sys import * |
|
||||
| test.py | 22 | ControlFlowNode for x | Module package.x | Entry node for Module package.x |
|
||||
| test.py | 24 | ControlFlowNode for IntegerLiteral | int 0 | ControlFlowNode for IntegerLiteral |
|
||||
| test.py | 24 | ControlFlowNode for argv | int 0 | ControlFlowNode for IntegerLiteral |
|
||||
| test.py | 27 | ControlFlowNode for ImportExpr | Module sys | ControlFlowNode for ImportExpr |
|
||||
| test.py | 31 | ControlFlowNode for argv | list object | ControlFlowNode for from sys import * |
|
||||
| test.py | 33 | ControlFlowNode for ImportExpr | Module socket | ControlFlowNode for ImportExpr |
|
||||
| test.py | 34 | ControlFlowNode for timeout | builtin-class TimeoutError | ControlFlowNode for from _socket import * |
|
||||
| x.py | 2 | ControlFlowNode for ImportExpr | Module sys | ControlFlowNode for ImportExpr |
|
||||
@@ -0,0 +1,9 @@
|
||||
import python
|
||||
|
||||
from int line, ControlFlowNode f, Object o, ControlFlowNode orig
|
||||
where
|
||||
not f.getLocation().getFile().inStdlib() and
|
||||
f.refersTo(o, orig) and
|
||||
line = f.getLocation().getStartLine() and
|
||||
line != 0
|
||||
select f.getLocation().getFile().getShortName(), line, f.toString(), o.toString(), orig.toString()
|
||||
@@ -0,0 +1,57 @@
|
||||
| __init__.py | 1 | ControlFlowNode for ImportExpr | Module package.module | builtin-class module | ControlFlowNode for ImportExpr |
|
||||
| __init__.py | 2 | ControlFlowNode for ImportMember | Function module | builtin-class function | ControlFlowNode for FunctionExpr |
|
||||
| __init__.py | 2 | ControlFlowNode for module | Function module | builtin-class function | ControlFlowNode for FunctionExpr |
|
||||
| __init__.py | 4 | ControlFlowNode for ImportExpr | Module package | builtin-class module | ControlFlowNode for ImportExpr |
|
||||
| __init__.py | 4 | ControlFlowNode for ImportMember | Module package.module2 | builtin-class module | Entry node for Module package.module2 |
|
||||
| __init__.py | 4 | ControlFlowNode for module3 | Module package.module2 | builtin-class module | Entry node for Module package.module2 |
|
||||
| __init__.py | 5 | ControlFlowNode for IntegerLiteral | int 7 | builtin-class int | ControlFlowNode for IntegerLiteral |
|
||||
| __init__.py | 5 | ControlFlowNode for module2 | int 7 | builtin-class int | ControlFlowNode for IntegerLiteral |
|
||||
| __init__.py | 6 | ControlFlowNode for ImportExpr | Module package | builtin-class module | ControlFlowNode for ImportExpr |
|
||||
| __init__.py | 6 | ControlFlowNode for ImportMember | int 7 | builtin-class int | ControlFlowNode for IntegerLiteral |
|
||||
| __init__.py | 6 | ControlFlowNode for module4 | int 7 | builtin-class int | ControlFlowNode for IntegerLiteral |
|
||||
| __init__.py | 7 | ControlFlowNode for ImportExpr | Module package | builtin-class module | ControlFlowNode for ImportExpr |
|
||||
| __init__.py | 7 | ControlFlowNode for ImportMember | Module package.module2 | builtin-class module | Entry node for Module package.module2 |
|
||||
| __init__.py | 7 | ControlFlowNode for module5 | Module package.module2 | builtin-class module | Entry node for Module package.module2 |
|
||||
| __init__.py | 8 | ControlFlowNode for ImportExpr | Module package | builtin-class module | ControlFlowNode for ImportExpr |
|
||||
| __init__.py | 8 | ControlFlowNode for ImportMember | Module package.moduleX | builtin-class module | Entry node for Module package.moduleX |
|
||||
| __init__.py | 8 | ControlFlowNode for moduleX | Module package.moduleX | builtin-class module | Entry node for Module package.moduleX |
|
||||
| module2.py | 1 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | ControlFlowNode for IntegerLiteral |
|
||||
| module2.py | 1 | ControlFlowNode for x | int 0 | builtin-class int | ControlFlowNode for IntegerLiteral |
|
||||
| module.py | 2 | ControlFlowNode for FunctionExpr | Function module | builtin-class function | ControlFlowNode for FunctionExpr |
|
||||
| module.py | 2 | ControlFlowNode for module | Function module | builtin-class function | ControlFlowNode for FunctionExpr |
|
||||
| moduleX.py | 1 | ControlFlowNode for ClassExpr | class Y | builtin-class type | ControlFlowNode for ClassExpr |
|
||||
| moduleX.py | 1 | ControlFlowNode for Y | class Y | builtin-class type | ControlFlowNode for ClassExpr |
|
||||
| moduleX.py | 1 | ControlFlowNode for object | builtin-class object | builtin-class type | ControlFlowNode for object |
|
||||
| test.py | 1 | ControlFlowNode for ImportExpr | Module package | builtin-class module | ControlFlowNode for ImportExpr |
|
||||
| test.py | 2 | ControlFlowNode for ImportMember | Function module | builtin-class function | ControlFlowNode for FunctionExpr |
|
||||
| test.py | 2 | ControlFlowNode for module | Function module | builtin-class function | ControlFlowNode for FunctionExpr |
|
||||
| test.py | 4 | ControlFlowNode for ImportExpr | Module package | builtin-class module | ControlFlowNode for ImportExpr |
|
||||
| test.py | 5 | ControlFlowNode for ImportMember | Module package.x | builtin-class module | Entry node for Module package.x |
|
||||
| test.py | 5 | ControlFlowNode for x | Module package.x | builtin-class module | Entry node for Module package.x |
|
||||
| test.py | 8 | ControlFlowNode for C | class C | builtin-class type | ControlFlowNode for ClassExpr |
|
||||
| test.py | 8 | ControlFlowNode for ClassExpr | class C | builtin-class type | ControlFlowNode for ClassExpr |
|
||||
| test.py | 8 | ControlFlowNode for object | builtin-class object | builtin-class type | ControlFlowNode for object |
|
||||
| test.py | 10 | ControlFlowNode for ImportExpr | Module package | builtin-class module | ControlFlowNode for ImportExpr |
|
||||
| test.py | 10 | ControlFlowNode for ImportMember | int 7 | builtin-class int | ControlFlowNode for IntegerLiteral |
|
||||
| test.py | 10 | ControlFlowNode for module2 | int 7 | builtin-class int | ControlFlowNode for IntegerLiteral |
|
||||
| test.py | 12 | ControlFlowNode for FunctionExpr | Function f | builtin-class function | ControlFlowNode for FunctionExpr |
|
||||
| test.py | 12 | ControlFlowNode for f | Function f | builtin-class function | ControlFlowNode for FunctionExpr |
|
||||
| test.py | 13 | ControlFlowNode for ImportExpr | Module package | builtin-class module | ControlFlowNode for ImportExpr |
|
||||
| test.py | 13 | ControlFlowNode for ImportMember | Module package.x | builtin-class module | Entry node for Module package.x |
|
||||
| test.py | 13 | ControlFlowNode for x | Module package.x | builtin-class module | Entry node for Module package.x |
|
||||
| test.py | 15 | ControlFlowNode for ImportExpr | Module package | builtin-class module | ControlFlowNode for ImportExpr |
|
||||
| test.py | 15 | ControlFlowNode for ImportMember | Module package.moduleX | builtin-class module | Entry node for Module package.moduleX |
|
||||
| test.py | 15 | ControlFlowNode for moduleX | Module package.moduleX | builtin-class module | Entry node for Module package.moduleX |
|
||||
| test.py | 16 | ControlFlowNode for Attribute | class Y | builtin-class type | ControlFlowNode for ClassExpr |
|
||||
| test.py | 16 | ControlFlowNode for moduleX | Module package.moduleX | builtin-class module | Entry node for Module package.moduleX |
|
||||
| test.py | 19 | ControlFlowNode for ImportExpr | Module tty | builtin-class module | ControlFlowNode for ImportExpr |
|
||||
| test.py | 19 | ControlFlowNode for tty | Module tty | builtin-class module | ControlFlowNode for ImportExpr |
|
||||
| test.py | 22 | ControlFlowNode for Attribute | Builtin-function exc_info | builtin-class builtin_function_or_method | ControlFlowNode for from sys import * |
|
||||
| test.py | 22 | ControlFlowNode for x | Module package.x | builtin-class module | Entry node for Module package.x |
|
||||
| test.py | 24 | ControlFlowNode for IntegerLiteral | int 0 | builtin-class int | ControlFlowNode for IntegerLiteral |
|
||||
| test.py | 24 | ControlFlowNode for argv | int 0 | builtin-class int | ControlFlowNode for IntegerLiteral |
|
||||
| test.py | 27 | ControlFlowNode for ImportExpr | Module sys | builtin-class module | ControlFlowNode for ImportExpr |
|
||||
| test.py | 31 | ControlFlowNode for argv | list object | builtin-class list | ControlFlowNode for from sys import * |
|
||||
| test.py | 33 | ControlFlowNode for ImportExpr | Module socket | builtin-class module | ControlFlowNode for ImportExpr |
|
||||
| test.py | 34 | ControlFlowNode for timeout | builtin-class TimeoutError | builtin-class type | ControlFlowNode for from _socket import * |
|
||||
| x.py | 2 | ControlFlowNode for ImportExpr | Module sys | builtin-class module | ControlFlowNode for ImportExpr |
|
||||
@@ -0,0 +1,10 @@
|
||||
import python
|
||||
|
||||
from int line, ControlFlowNode f, Object o, ClassObject cls, ControlFlowNode orig
|
||||
where
|
||||
not f.getLocation().getFile().inStdlib() and
|
||||
f.refersTo(o, cls, orig) and
|
||||
line = f.getLocation().getStartLine() and
|
||||
line != 0
|
||||
select f.getLocation().getFile().getShortName(), line, f.toString(), o.toString(), cls.toString(),
|
||||
orig.toString()
|
||||
1
python/ql/test/3/library-tests/PointsTo/imports/options
Normal file
1
python/ql/test/3/library-tests/PointsTo/imports/options
Normal file
@@ -0,0 +1 @@
|
||||
semmle-extractor-options: --lang=3 --max-import-depth=2 -r package
|
||||
@@ -0,0 +1,15 @@
|
||||
from .module \
|
||||
import module
|
||||
|
||||
from . import module2 as module3
|
||||
module2 = 7
|
||||
from . import module2 as module4
|
||||
from . import module3 as module5
|
||||
from package import moduleX
|
||||
|
||||
#We should now have:
|
||||
#module2 = 7
|
||||
#module3 = package.module2
|
||||
#module4 = 7
|
||||
#module5 = package.module2
|
||||
#moduleX = package.moduleX
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
def module(args):
|
||||
pass
|
||||
@@ -0,0 +1 @@
|
||||
x = 0
|
||||
@@ -0,0 +1,2 @@
|
||||
class Y(object):
|
||||
pass
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
from sys import *
|
||||
34
python/ql/test/3/library-tests/PointsTo/imports/test.py
Normal file
34
python/ql/test/3/library-tests/PointsTo/imports/test.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from package \
|
||||
import module
|
||||
|
||||
from package \
|
||||
import x
|
||||
#Should work correctly in nested scopes as well.
|
||||
|
||||
class C(object):
|
||||
|
||||
from package import module2
|
||||
|
||||
def f(self):
|
||||
from package import x
|
||||
|
||||
from package import moduleX
|
||||
moduleX.Y
|
||||
|
||||
#A small stdlib module to test version handling.
|
||||
import tty
|
||||
|
||||
#Check imports of builtin-objects using import * with no corresponding variable.
|
||||
x.exc_info
|
||||
|
||||
argv = 0
|
||||
|
||||
try:
|
||||
from sys import *
|
||||
except:
|
||||
pass
|
||||
|
||||
argv
|
||||
|
||||
from socket import *
|
||||
timeout
|
||||
@@ -0,0 +1,6 @@
|
||||
| file://:0:0:0:0 | Module sys | isUsedAsModule |
|
||||
| file://:0:0:0:0 | Module sys.monitoring | isUsedAsModule |
|
||||
| imported.py:0:0:0:0 | Module imported | isUsedAsModule |
|
||||
| main.py:0:0:0:0 | Module main | isUsedAsScript |
|
||||
| myscript.py:0:0:0:0 | Script myscript | isUsedAsScript |
|
||||
| script:0:0:0:0 | Script script | isUsedAsScript |
|
||||
16
python/ql/test/3/library-tests/modules/usage/ModuleUsage.ql
Normal file
16
python/ql/test/3/library-tests/modules/usage/ModuleUsage.ql
Normal file
@@ -0,0 +1,16 @@
|
||||
import python
|
||||
|
||||
from ModuleValue mv, string usage
|
||||
where
|
||||
// builtin module has different name in Python 2 and 3
|
||||
not mv = Module::builtinModule() and
|
||||
(
|
||||
mv.isUsedAsModule() and usage = "isUsedAsModule"
|
||||
or
|
||||
mv.isUsedAsScript() and usage = "isUsedAsScript"
|
||||
or
|
||||
not mv.isUsedAsModule() and
|
||||
not mv.isUsedAsScript() and
|
||||
usage = "<UNKNOWN>"
|
||||
)
|
||||
select mv, usage
|
||||
6
python/ql/test/3/library-tests/modules/usage/imported.py
Normal file
6
python/ql/test/3/library-tests/modules/usage/imported.py
Normal file
@@ -0,0 +1,6 @@
|
||||
def func():
|
||||
pass
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("I could have done something interesting...")
|
||||
print("but I didn't")
|
||||
5
python/ql/test/3/library-tests/modules/usage/main.py
Normal file
5
python/ql/test/3/library-tests/modules/usage/main.py
Normal file
@@ -0,0 +1,5 @@
|
||||
import imported
|
||||
|
||||
if __name__ == "__main__":
|
||||
imported.func()
|
||||
print('Done')
|
||||
3
python/ql/test/3/library-tests/modules/usage/myscript.py
Executable file
3
python/ql/test/3/library-tests/modules/usage/myscript.py
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
print("I'm actually a script you see ;)")
|
||||
1
python/ql/test/3/library-tests/modules/usage/options
Normal file
1
python/ql/test/3/library-tests/modules/usage/options
Normal file
@@ -0,0 +1 @@
|
||||
semmle-extractor-options: --lang=3 -F script
|
||||
3
python/ql/test/3/library-tests/modules/usage/script
Executable file
3
python/ql/test/3/library-tests/modules/usage/script
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
print('Under construction :)')
|
||||
@@ -1,9 +1,12 @@
|
||||
| builtin-class object | __class__ | Property __class__ | method-wrapper __get__ | method-wrapper __set__ | method-wrapper __delete__ |
|
||||
| builtin-class type | __abstractmethods__ | Property __abstractmethods__ | method-wrapper __get__ | method-wrapper __set__ | method-wrapper __delete__ |
|
||||
| builtin-class type | __annotations__ | Property __annotations__ | method-wrapper __get__ | method-wrapper __set__ | method-wrapper __delete__ |
|
||||
| builtin-class type | __bases__ | Property __bases__ | method-wrapper __get__ | method-wrapper __set__ | method-wrapper __delete__ |
|
||||
| builtin-class type | __dict__ | Property __dict__ | method-wrapper __get__ | method-wrapper __set__ | method-wrapper __delete__ |
|
||||
| builtin-class type | __doc__ | Property __doc__ | method-wrapper __get__ | method-wrapper __set__ | method-wrapper __delete__ |
|
||||
| builtin-class type | __module__ | Property __module__ | method-wrapper __get__ | method-wrapper __set__ | method-wrapper __delete__ |
|
||||
| builtin-class type | __mro__ | Property __mro__ | method-wrapper __get__ | method-wrapper __set__ | method-wrapper __delete__ |
|
||||
| builtin-class type | __name__ | Property __name__ | method-wrapper __get__ | method-wrapper __set__ | method-wrapper __delete__ |
|
||||
| builtin-class type | __qualname__ | Property __qualname__ | method-wrapper __get__ | method-wrapper __set__ | method-wrapper __delete__ |
|
||||
| builtin-class type | __text_signature__ | Property __text_signature__ | method-wrapper __get__ | method-wrapper __set__ | method-wrapper __delete__ |
|
||||
| builtin-class type | __text_signature__ | Property __text_signature__ | method-wrapper __get__ | method-wrapper __set__ | method-wrapper __delete__ |
|
||||
| builtin-class type | __type_params__ | Property __type_params__ | method-wrapper __get__ | method-wrapper __set__ | method-wrapper __delete__ |
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
| 51 |
|
||||
1
python/ql/test/3/query-tests/Summary/LinesOfCode.qlref
Normal file
1
python/ql/test/3/query-tests/Summary/LinesOfCode.qlref
Normal file
@@ -0,0 +1 @@
|
||||
Summary/LinesOfCode.ql
|
||||
@@ -0,0 +1 @@
|
||||
| 11 |
|
||||
@@ -0,0 +1 @@
|
||||
Summary/LinesOfUserCode.ql
|
||||
7
python/ql/test/3/query-tests/Summary/also_python_code
Executable file
7
python/ql/test/3/query-tests/Summary/also_python_code
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# although this is actually Python code, it is not included by the extractor by default.
|
||||
|
||||
print("this is also code")
|
||||
|
||||
print("but just dummy code")
|
||||
26
python/ql/test/3/query-tests/Summary/my_file.py
Normal file
26
python/ql/test/3/query-tests/Summary/my_file.py
Normal file
@@ -0,0 +1,26 @@
|
||||
"""
|
||||
module level docstring
|
||||
|
||||
is not included
|
||||
"""
|
||||
# this line is not code
|
||||
|
||||
# `tty` was chosen for stability over python versions (so we don't get diffrent results
|
||||
# on different computers, that has different versions of Python).
|
||||
#
|
||||
# According to https://github.com/python/cpython/tree/master/Lib (at 2021-04-23) `tty`
|
||||
# was last changed in 2001, so chances of this being changed in the future are slim.
|
||||
import tty
|
||||
|
||||
s = """
|
||||
all these lines are code
|
||||
"""
|
||||
|
||||
print(s)
|
||||
|
||||
def func():
|
||||
"""
|
||||
this string is a doc-string. Although the module-level docstring is not considered
|
||||
code, this one apparently is ¯\_(ツ)_/¯
|
||||
"""
|
||||
pass
|
||||
5
python/ql/test/3/query-tests/Summary/not_python
Executable file
5
python/ql/test/3/query-tests/Summary/not_python
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Although this is valid python code, it should not be counted as such.
|
||||
|
||||
print("foo")
|
||||
@@ -1,9 +1,5 @@
|
||||
uniqueEnclosingCallable
|
||||
uniqueCallEnclosingCallable
|
||||
| new_cls_param.py:14:6:14:16 | classmethod() | Call should have one enclosing callable but has 0. |
|
||||
| test.py:21:6:21:17 | staticmethod() | Call should have one enclosing callable but has 0. |
|
||||
| test.py:25:6:25:16 | classmethod() | Call should have one enclosing callable but has 0. |
|
||||
| test.py:29:6:29:16 | classmethod() | Call should have one enclosing callable but has 0. |
|
||||
uniqueType
|
||||
uniqueNodeLocation
|
||||
missingLocation
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# Originally we had module and functions as `DataFlowCallable``, and any call inside a
|
||||
# class scope would not have a result for getEnclosingCallable. Since this was only a
|
||||
# consistency error for calls, originally we added a new `DataFlowClassScope` only for
|
||||
# those classes that had a call in their scope. That's why all the class definitions in
|
||||
# this test do a call to the dummy function `func`.
|
||||
#
|
||||
# Note: this was shortsighted, since most DataFlow::Node use `getCallableScope` helper
|
||||
# to define their .getEnclosingCallable(), which picks the first DataFlowCallable to
|
||||
# contain the node. (so for some classes that would be DataFlowClassScope, and for some
|
||||
# it would be the module/function containing the class definition)
|
||||
|
||||
def func(*args, **kwargs):
|
||||
print("func()")
|
||||
|
||||
class Cls:
|
||||
func()
|
||||
class Inner:
|
||||
func()
|
||||
|
||||
def other_func():
|
||||
class Cls2:
|
||||
func()
|
||||
return Cls2
|
||||
|
||||
x = other_func()
|
||||
@@ -1,7 +1,5 @@
|
||||
uniqueEnclosingCallable
|
||||
uniqueCallEnclosingCallable
|
||||
| datamodel.py:71:6:71:16 | classmethod() | Call should have one enclosing callable but has 0. |
|
||||
| datamodel.py:76:6:76:17 | staticmethod() | Call should have one enclosing callable but has 0. |
|
||||
uniqueType
|
||||
uniqueNodeLocation
|
||||
missingLocation
|
||||
|
||||
@@ -186,14 +186,20 @@ SINK(asyncio.run(c.coro(SOURCE))) # $ MISSING: flow
|
||||
class A:
|
||||
|
||||
def __await__(self):
|
||||
# yield SOURCE -- see https://groups.google.com/g/dev-python/c/_lrrc-vp9TI?pli=1
|
||||
return (yield from asyncio.coroutine(lambda: SOURCE)())
|
||||
fut = asyncio.Future()
|
||||
fut.set_result(SOURCE)
|
||||
yield from fut
|
||||
|
||||
async def agen(x):
|
||||
async def atest_custom_await_impl():
|
||||
a = A()
|
||||
return await a
|
||||
x = await a
|
||||
# TODO: Figure out how to actually return something from our custom __await__
|
||||
# implementation. The problem is we have to play nicely with the asyncio framework,
|
||||
# which have their own expectations on what a return value from __await__ should look
|
||||
# like.
|
||||
assert x is None
|
||||
SINK_F(x)
|
||||
|
||||
SINK(asyncio.run(agen(SOURCE))) # $ MISSING: flow
|
||||
|
||||
# Asynchronous generator functions
|
||||
# A function or method which is defined using async def and which uses the yield statement is called a asynchronous generator function. Such a function, when called, returns an asynchronous iterator object which can be used in an async for statement to execute the body of the function.
|
||||
|
||||
@@ -338,9 +338,9 @@ class C:
|
||||
|
||||
@expects(2)
|
||||
def test_attribute_reference():
|
||||
SINK(C.a) #$ MISSING:flow="SOURCE, l:-4 -> C.a"
|
||||
SINK(C.a) # $ flow="SOURCE, l:-5 -> C.a"
|
||||
c = C()
|
||||
SINK(c.a) #$ MISSING:flow="SOURCE, l:-6 -> c.a"
|
||||
SINK(c.a) # $ MISSING: flow="SOURCE, l:-7 -> c.a"
|
||||
|
||||
# overriding __getattr__ should be tested by the class coverage tests
|
||||
|
||||
|
||||
@@ -237,6 +237,124 @@ def test_field_on_compound_arg(cond_true=True, cond_false=False):
|
||||
SINK(y.attr) # $ MISSING: flow
|
||||
SINK_F(z.attr) # $ MISSING: flow
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Content in class attribute
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
class WithTuple:
|
||||
my_tuple = (SOURCE, NONSOURCE)
|
||||
|
||||
def test_inst(self):
|
||||
SINK(self.my_tuple[0]) # $ MISSING: flow
|
||||
SINK_F(self.my_tuple[1])
|
||||
|
||||
def test_inst_no_call(self):
|
||||
SINK(self.my_tuple[0]) # $ MISSING: flow
|
||||
SINK_F(self.my_tuple[1])
|
||||
|
||||
@classmethod
|
||||
def test_cm(cls):
|
||||
SINK(cls.my_tuple[0]) # $ flow="SOURCE, l:-12 -> cls.my_tuple[0]"
|
||||
SINK_F(cls.my_tuple[1])
|
||||
|
||||
@classmethod
|
||||
def test_cm_no_call(cls):
|
||||
SINK(cls.my_tuple[0]) # $ MISSING: flow="SOURCE, l:-8 -> cls.my_tuple[0]"
|
||||
SINK_F(cls.my_tuple[1])
|
||||
|
||||
|
||||
@expects(2*4) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
|
||||
def test_WithTuple():
|
||||
SINK(WithTuple.my_tuple[0]) # $ flow="SOURCE, l:-23 -> WithTuple.my_tuple[0]"
|
||||
SINK_F(WithTuple.my_tuple[1])
|
||||
|
||||
WithTuple.test_cm()
|
||||
|
||||
inst = WithTuple()
|
||||
inst.test_inst()
|
||||
|
||||
SINK(inst.my_tuple[0]) # $ MISSING: flow
|
||||
SINK_F(inst.my_tuple[1])
|
||||
|
||||
|
||||
@expects(4) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
|
||||
def test_inst_override():
|
||||
inst = WithTuple()
|
||||
|
||||
# setting attribute on instance does not override class attribute, it's only on the
|
||||
# instance!
|
||||
inst.my_tuple = (NONSOURCE, SOURCE)
|
||||
|
||||
SINK_F(inst.my_tuple[0])
|
||||
SINK(inst.my_tuple[1]) # $ flow="SOURCE, l:-3 -> inst.my_tuple[1]"
|
||||
|
||||
SINK(WithTuple.my_tuple[0]) # $ flow="SOURCE, l:-46 -> WithTuple.my_tuple[0]"
|
||||
SINK_F(WithTuple.my_tuple[1])
|
||||
|
||||
|
||||
class WithTuple2:
|
||||
my_tuple = (NONSOURCE,)
|
||||
|
||||
def set_to_source():
|
||||
WithTuple2.my_tuple = (SOURCE,)
|
||||
|
||||
@expects(4) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
|
||||
def test_global_flow_to_class_attribute():
|
||||
inst = WithTuple2()
|
||||
SINK_F(WithTuple2.my_tuple[0])
|
||||
SINK_F(inst.my_tuple[0])
|
||||
|
||||
set_to_source()
|
||||
|
||||
SINK(WithTuple2.my_tuple[0]) # $ MISSING: flow="SOURCE, l:-10 -> WithTuple2.my_tuple[0]"
|
||||
SINK(inst.my_tuple[0]) # $ MISSING: flow="SOURCE, l:-11 -> inst.my_tuple[0]"
|
||||
|
||||
|
||||
class Outer:
|
||||
src = SOURCE
|
||||
class Inner:
|
||||
src = SOURCE
|
||||
|
||||
@expects(2) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
|
||||
def test_nested_class():
|
||||
SINK(Outer.src) # $ flow="SOURCE, l:-6 -> Outer.src"
|
||||
SINK(Outer.Inner.src) # $ flow="SOURCE, l:-5 -> Outer.Inner.src"
|
||||
|
||||
# --------------------------------------
|
||||
# unique classes from functions
|
||||
# --------------------------------------
|
||||
def make_class():
|
||||
# a fresh class is returned each time this function is called
|
||||
class C:
|
||||
my_tuple = (NONSOURCE,)
|
||||
return C
|
||||
|
||||
@expects(8) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
|
||||
def test_unique_class():
|
||||
# This test highlights that if we use the _ClassExpr_ itself as the target/source
|
||||
# for jumpsteps, we will end up with spurious flow (that is, we will think that
|
||||
# x_cls and y_cls are the same, so by updating .my_tuple on x_cls we might propagate
|
||||
# that to y_cls as well -- it might not matter too much in reality, but certainly an
|
||||
# interesting corner case)
|
||||
x_cls = make_class()
|
||||
y_cls = make_class()
|
||||
|
||||
assert x_cls != y_cls
|
||||
|
||||
x_inst = x_cls()
|
||||
y_inst = y_cls()
|
||||
|
||||
SINK_F(x_cls.my_tuple[0])
|
||||
SINK_F(x_inst.my_tuple[0])
|
||||
SINK_F(y_cls.my_tuple[0])
|
||||
SINK_F(y_inst.my_tuple[0])
|
||||
|
||||
x_cls.my_tuple = (SOURCE,)
|
||||
SINK(x_cls.my_tuple[0]) # $ flow="SOURCE, l:-1 -> x_cls.my_tuple[0]"
|
||||
SINK(x_inst.my_tuple[0]) # $ MISSING: flow="SOURCE, l:-2 -> x_inst.my_tuple[0]"
|
||||
SINK_F(y_cls.my_tuple[0])
|
||||
SINK_F(y_inst.my_tuple[0])
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Crosstalk test -- using different function based on conditional
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
@@ -64,7 +64,7 @@ class Unsafe:
|
||||
def test_value_pattern():
|
||||
match SOURCE:
|
||||
case Unsafe.VALUE as x:
|
||||
SINK(x) #$ flow="SOURCE, l:-2 -> x" MISSING: flow="SOURCE, l:-5 -> x"
|
||||
SINK(x) #$ flow="SOURCE, l:-2 -> x" flow="SOURCE, l:-5 -> x"
|
||||
|
||||
@expects(2)
|
||||
def test_sequence_pattern_tuple():
|
||||
|
||||
@@ -84,3 +84,5 @@ if __name__ == "__main__":
|
||||
# The below fails when trying to import modules
|
||||
# check_tests_valid("module-initialization.test")
|
||||
# check_tests_valid("module-initialization.testOnce")
|
||||
|
||||
print("\n🎉 All tests passed 🎉")
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
uniqueEnclosingCallable
|
||||
uniqueCallEnclosingCallable
|
||||
| test_collections.py:39:17:39:38 | Lambda() | Call should have one enclosing callable but has 0. |
|
||||
| test_collections.py:39:17:39:38 | Lambda() | Call should have one enclosing callable but has 0. |
|
||||
| test_collections.py:45:19:45:24 | mod() | Call should have one enclosing callable but has 0. |
|
||||
| test_collections.py:45:19:45:24 | mod() | Call should have one enclosing callable but has 0. |
|
||||
| test_collections.py:52:13:52:24 | mod_local() | Call should have one enclosing callable but has 0. |
|
||||
| test_collections.py:52:13:52:24 | mod_local() | Call should have one enclosing callable but has 0. |
|
||||
uniqueType
|
||||
uniqueNodeLocation
|
||||
missingLocation
|
||||
|
||||
@@ -1,36 +1,5 @@
|
||||
uniqueEnclosingCallable
|
||||
uniqueCallEnclosingCallable
|
||||
| code/bound_method_arg.py:5:6:5:16 | classmethod() | Call should have one enclosing callable but has 0. |
|
||||
| code/callable_as_argument.py:37:6:37:17 | staticmethod() | Call should have one enclosing callable but has 0. |
|
||||
| code/callable_as_argument.py:49:10:49:21 | staticmethod() | Call should have one enclosing callable but has 0. |
|
||||
| code/class_construction.py:13:6:13:16 | classmethod() | Call should have one enclosing callable but has 0. |
|
||||
| code/class_properties.py:9:6:9:13 | property() | Call should have one enclosing callable but has 0. |
|
||||
| code/class_properties.py:11:9:11:32 | print() | Call should have one enclosing callable but has 0. |
|
||||
| code/class_properties.py:14:6:14:15 | Attribute() | Call should have one enclosing callable but has 0. |
|
||||
| code/class_properties.py:19:6:19:16 | Attribute() | Call should have one enclosing callable but has 0. |
|
||||
| code/class_properties.py:36:12:36:62 | property() | Call should have one enclosing callable but has 0. |
|
||||
| code/class_properties.py:38:6:38:13 | property() | Call should have one enclosing callable but has 0. |
|
||||
| code/class_properties.py:40:9:40:38 | print() | Call should have one enclosing callable but has 0. |
|
||||
| code/class_subclass.py:10:6:10:17 | staticmethod() | Call should have one enclosing callable but has 0. |
|
||||
| code/class_subclass.py:14:6:14:16 | classmethod() | Call should have one enclosing callable but has 0. |
|
||||
| code/class_subclass.py:104:6:104:17 | staticmethod() | Call should have one enclosing callable but has 0. |
|
||||
| code/class_subclass.py:108:6:108:16 | classmethod() | Call should have one enclosing callable but has 0. |
|
||||
| code/class_subclass.py:112:6:112:17 | staticmethod() | Call should have one enclosing callable but has 0. |
|
||||
| code/class_subclass.py:116:6:116:16 | classmethod() | Call should have one enclosing callable but has 0. |
|
||||
| code/class_subclass.py:120:6:120:16 | classmethod() | Call should have one enclosing callable but has 0. |
|
||||
| code/class_subclass.py:149:6:149:17 | staticmethod() | Call should have one enclosing callable but has 0. |
|
||||
| code/class_subclass.py:153:6:153:16 | classmethod() | Call should have one enclosing callable but has 0. |
|
||||
| code/class_super.py:13:6:13:16 | classmethod() | Call should have one enclosing callable but has 0. |
|
||||
| code/class_super.py:28:6:28:17 | staticmethod() | Call should have one enclosing callable but has 0. |
|
||||
| code/class_super.py:36:6:36:16 | classmethod() | Call should have one enclosing callable but has 0. |
|
||||
| code/class_super.py:40:6:40:16 | classmethod() | Call should have one enclosing callable but has 0. |
|
||||
| code/func_defined_outside_class.py:17:18:17:41 | staticmethod() | Call should have one enclosing callable but has 0. |
|
||||
| code/func_defined_outside_class.py:18:18:18:40 | classmethod() | Call should have one enclosing callable but has 0. |
|
||||
| code/func_defined_outside_class.py:38:11:38:21 | _gen() | Call should have one enclosing callable but has 0. |
|
||||
| code/func_defined_outside_class.py:39:11:39:21 | _gen() | Call should have one enclosing callable but has 0. |
|
||||
| code/nested_class.py:3:10:3:21 | staticmethod() | Call should have one enclosing callable but has 0. |
|
||||
| code/nested_class.py:7:10:7:21 | staticmethod() | Call should have one enclosing callable but has 0. |
|
||||
| code/self_passing.py:60:6:60:16 | classmethod() | Call should have one enclosing callable but has 0. |
|
||||
uniqueType
|
||||
uniqueNodeLocation
|
||||
missingLocation
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
uniqueEnclosingCallable
|
||||
uniqueCallEnclosingCallable
|
||||
| test_captured.py:7:22:7:25 | p() | Call should have one enclosing callable but has 0. |
|
||||
| test_captured.py:7:22:7:25 | p() | Call should have one enclosing callable but has 0. |
|
||||
| test_captured.py:14:26:14:30 | pp() | Call should have one enclosing callable but has 0. |
|
||||
| test_captured.py:14:26:14:30 | pp() | Call should have one enclosing callable but has 0. |
|
||||
uniqueType
|
||||
uniqueNodeLocation
|
||||
missingLocation
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
semmle-extractor-options: --max-import-depth=2 -r package
|
||||
@@ -3,9 +3,5 @@
|
||||
| code/l_calls.py:12:1:12:20 | ControlFlowNode for ClassExpr | code/l_calls.py:16:16:16:18 | ControlFlowNode for cls |
|
||||
| code/l_calls.py:12:1:12:20 | ControlFlowNode for ClassExpr | code/l_calls.py:24:13:24:22 | ControlFlowNode for Attribute() |
|
||||
| code/l_calls.py:12:1:12:20 | ControlFlowNode for ClassExpr | code/l_calls.py:25:16:25:16 | ControlFlowNode for a |
|
||||
| code/l_calls.py:33:5:33:23 | ControlFlowNode for FunctionExpr | code/l_calls.py:39:1:39:3 | ControlFlowNode for Attribute |
|
||||
| code/l_calls.py:48:5:48:30 | ControlFlowNode for FunctionExpr | code/l_calls.py:53:1:53:3 | ControlFlowNode for Attribute |
|
||||
| code/q_super.py:48:5:48:17 | ControlFlowNode for ClassExpr | code/q_super.py:51:25:51:29 | ControlFlowNode for Attribute |
|
||||
| code/q_super.py:63:5:63:17 | ControlFlowNode for ClassExpr | code/q_super.py:66:19:66:23 | ControlFlowNode for Attribute |
|
||||
| code/t_type.py:3:1:3:16 | ControlFlowNode for ClassExpr | code/t_type.py:6:1:6:9 | ControlFlowNode for type() |
|
||||
| code/t_type.py:3:1:3:16 | ControlFlowNode for ClassExpr | code/t_type.py:13:5:13:13 | ControlFlowNode for type() |
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
testFailures
|
||||
failures
|
||||
@@ -0,0 +1,2 @@
|
||||
import python
|
||||
import experimental.meta.ConceptsTest
|
||||
@@ -0,0 +1,4 @@
|
||||
from aiofile import async_open, AIOFile
|
||||
|
||||
AIOFile("file", 'r') # $ getAPathArgument="file"
|
||||
async_open("file", "r") # $ getAPathArgument="file"
|
||||
@@ -0,0 +1,2 @@
|
||||
testFailures
|
||||
failures
|
||||
@@ -0,0 +1,2 @@
|
||||
import python
|
||||
import experimental.meta.ConceptsTest
|
||||
@@ -0,0 +1,3 @@
|
||||
import aiofiles
|
||||
|
||||
aiofiles.open("file", mode='r') # $ getAPathArgument="file"
|
||||
@@ -0,0 +1,2 @@
|
||||
testFailures
|
||||
failures
|
||||
@@ -0,0 +1,2 @@
|
||||
import python
|
||||
import experimental.meta.ConceptsTest
|
||||
@@ -0,0 +1,8 @@
|
||||
import anyio
|
||||
from anyio.streams.file import FileReadStream, FileWriteStream
|
||||
from anyio import Path
|
||||
|
||||
anyio.open_file("file", 'r') # $ getAPathArgument="file"
|
||||
FileReadStream.from_path("file") # $ getAPathArgument="file"
|
||||
FileWriteStream.from_path("file") # $ getAPathArgument="file"
|
||||
Path("file") # $ getAPathArgument="file"
|
||||
@@ -0,0 +1,2 @@
|
||||
testFailures
|
||||
failures
|
||||
@@ -0,0 +1,2 @@
|
||||
import python
|
||||
import experimental.meta.ConceptsTest
|
||||
@@ -0,0 +1,3 @@
|
||||
from baize.asgi import FileResponse as baizeFileResponse
|
||||
|
||||
baizeFileResponse("file") # $ getAPathArgument="file"
|
||||
@@ -0,0 +1,2 @@
|
||||
testFailures
|
||||
failures
|
||||
@@ -0,0 +1,2 @@
|
||||
import python
|
||||
import experimental.meta.ConceptsTest
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user