Merge branch 'main' into tausbn/python-add-support-for-python-3.12-type-syntax

This commit is contained in:
Taus
2023-11-16 09:59:44 +00:00
1863 changed files with 87157 additions and 29549 deletions

View File

@@ -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])`.

View File

@@ -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

View File

@@ -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() }

View File

@@ -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())
}

View File

@@ -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
/**

View 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"
}
}
}

View 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() }
}
}

View 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() }
}
}

View 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()
}
}
}

View 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"
}
}
}

View File

@@ -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")] }

View File

@@ -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)
}

View File

@@ -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() {

View 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()
}
}
}

View File

@@ -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() }
}
}

View File

@@ -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() }

View File

@@ -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();

View File

@@ -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)

View File

@@ -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

View File

@@ -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>

View File

@@ -0,0 +1 @@
semmle-extractor-options: --lang=3

View File

@@ -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.

View File

@@ -0,0 +1 @@
semmle-extractor-options: --lang=2 --max-import-depth=2 -r package

View File

@@ -0,0 +1 @@
semmle-extractor-options: --lang=2 -F script

View File

@@ -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 |

View File

@@ -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()

View File

@@ -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 |

View File

@@ -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()

View File

@@ -0,0 +1 @@
semmle-extractor-options: --lang=3 --max-import-depth=2 -r package

View File

@@ -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

View File

@@ -0,0 +1,3 @@
def module(args):
pass

View File

@@ -0,0 +1 @@
x = 0

View File

@@ -0,0 +1,2 @@
class Y(object):
pass

View File

@@ -0,0 +1,2 @@
from sys import *

View 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

View File

@@ -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 |

View 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

View File

@@ -0,0 +1,6 @@
def func():
pass
if __name__ == "__main__":
print("I could have done something interesting...")
print("but I didn't")

View File

@@ -0,0 +1,5 @@
import imported
if __name__ == "__main__":
imported.func()
print('Done')

View File

@@ -0,0 +1,3 @@
#!/usr/bin/env python
print("I'm actually a script you see ;)")

View File

@@ -0,0 +1 @@
semmle-extractor-options: --lang=3 -F script

View File

@@ -0,0 +1,3 @@
#!/usr/bin/env python
print('Under construction :)')

View File

@@ -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__ |

View File

@@ -0,0 +1 @@
| 51 |

View File

@@ -0,0 +1 @@
Summary/LinesOfCode.ql

View File

@@ -0,0 +1 @@
| 11 |

View File

@@ -0,0 +1 @@
Summary/LinesOfUserCode.ql

View 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")

View 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

View File

@@ -0,0 +1,5 @@
#!/bin/bash
# Although this is valid python code, it should not be counted as such.
print("foo")

View File

@@ -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

View File

@@ -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()

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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
# ------------------------------------------------------------------------------

View File

@@ -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():

View File

@@ -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 🎉")

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1 +0,0 @@
semmle-extractor-options: --max-import-depth=2 -r package

View File

@@ -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() |

View File

@@ -0,0 +1,2 @@
testFailures
failures

View File

@@ -0,0 +1,2 @@
import python
import experimental.meta.ConceptsTest

View File

@@ -0,0 +1,4 @@
from aiofile import async_open, AIOFile
AIOFile("file", 'r') # $ getAPathArgument="file"
async_open("file", "r") # $ getAPathArgument="file"

View File

@@ -0,0 +1,2 @@
testFailures
failures

View File

@@ -0,0 +1,2 @@
import python
import experimental.meta.ConceptsTest

View File

@@ -0,0 +1,3 @@
import aiofiles
aiofiles.open("file", mode='r') # $ getAPathArgument="file"

View File

@@ -0,0 +1,2 @@
testFailures
failures

View File

@@ -0,0 +1,2 @@
import python
import experimental.meta.ConceptsTest

View File

@@ -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"

View File

@@ -0,0 +1,2 @@
testFailures
failures

View File

@@ -0,0 +1,2 @@
import python
import experimental.meta.ConceptsTest

View File

@@ -0,0 +1,3 @@
from baize.asgi import FileResponse as baizeFileResponse
baizeFileResponse("file") # $ getAPathArgument="file"

View File

@@ -0,0 +1,2 @@
testFailures
failures

View File

@@ -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