mirror of
https://github.com/github/codeql.git
synced 2025-12-24 04:36:35 +01:00
Merge branch 'main' into py-restframework
This commit is contained in:
15
python/ql/lib/BUILD.bazel
Normal file
15
python/ql/lib/BUILD.bazel
Normal file
@@ -0,0 +1,15 @@
|
||||
load("@rules_pkg//:mappings.bzl", "pkg_files")
|
||||
|
||||
package(default_visibility = ["//python:__pkg__"])
|
||||
|
||||
pkg_files(
|
||||
name = "dbscheme",
|
||||
srcs = ["semmlecode.python.dbscheme"],
|
||||
prefix = "python",
|
||||
)
|
||||
|
||||
pkg_files(
|
||||
name = "dbscheme-stats",
|
||||
srcs = ["semmlecode.python.dbscheme.stats"],
|
||||
prefix = "python",
|
||||
)
|
||||
@@ -1,3 +1,38 @@
|
||||
## 0.11.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Added better support for API graphs when encountering `from ... import *`. For example in the code `from foo import *; Bar()`, we will now find a result for `API::moduleImport("foo").getMember("Bar").getACall()`
|
||||
* Deleted the deprecated `isBarrierGuard` predicate from the dataflow library and its uses, use `isBarrier` and the `BarrierGuard` module instead.
|
||||
* Deleted the deprecated `getAUse`, `getAnImmediateUse`, `getARhs`, and `getAValueReachingRhs` predicates from the `API::Node` class.
|
||||
* Deleted the deprecated `fullyQualifiedToAPIGraphPath` class from `SubclassFinder.qll`, use `fullyQualifiedToApiGraphPath` instead.
|
||||
* Deleted the deprecated `Paths.qll` file.
|
||||
* Deleted the deprecated `semmle.python.security.performance` folder, use `semmle.python.security.regexp` instead.
|
||||
* Deleted the deprecated `semmle.python.security.strings` and `semmle.python.web` folders.
|
||||
* Improved modeling of decoding through pickle related functions (which can lead to code execution), resulting in additional sinks for the _Deserializing untrusted input_ query (`py/unsafe-deserialization`). Added support for `pandas.read_pickle`, `numpy.load` and `joblib.load`.
|
||||
|
||||
## 0.11.0
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Django Rest Framework better handles custom `ModelViewSet` classes functions
|
||||
* Regular expression fragments residing inside implicitly concatenated strings now have better location information.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Subterms of regular expressions encoded as single-line string literals now have better source-location information.
|
||||
|
||||
## 0.10.5
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.10.4
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Regular expressions containing multiple parse mode flags are now interpretted correctly. For example `"(?is)abc.*"` with both the `i` and `s` flags.
|
||||
* Added `shlex.quote` as a sanitizer for the `py/shell-command-constructed-from-input` query.
|
||||
|
||||
## 0.10.3
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added `shlex.quote` as a sanitizer for the `py/shell-command-constructed-from-input` query.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: fix
|
||||
---
|
||||
* Subterms of regular expressions encoded as single-line string literals now have better source-location information.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Regular expression fragments residing inside implicitly concatenated strings now have better location information.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Django Rest Framework better handles custom `ModelViewSet` classes functions
|
||||
4
python/ql/lib/change-notes/2023-10-17-contextmanager.md
Normal file
4
python/ql/lib/change-notes/2023-10-17-contextmanager.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added support for functions decorated with `contextlib.contextmanager`.
|
||||
@@ -1,4 +1,6 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
## 0.10.4
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Regular expressions containing multiple parse mode flags are now interpretted correctly. For example `"(?is)abc.*"` with both the `i` and `s` flags.
|
||||
* Added `shlex.quote` as a sanitizer for the `py/shell-command-constructed-from-input` query.
|
||||
3
python/ql/lib/change-notes/released/0.10.5.md
Normal file
3
python/ql/lib/change-notes/released/0.10.5.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.10.5
|
||||
|
||||
No user-facing changes.
|
||||
10
python/ql/lib/change-notes/released/0.11.0.md
Normal file
10
python/ql/lib/change-notes/released/0.11.0.md
Normal file
@@ -0,0 +1,10 @@
|
||||
## 0.11.0
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Django Rest Framework better handles custom `ModelViewSet` classes functions
|
||||
* Regular expression fragments residing inside implicitly concatenated strings now have better location information.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Subterms of regular expressions encoded as single-line string literals now have better source-location information.
|
||||
12
python/ql/lib/change-notes/released/0.11.1.md
Normal file
12
python/ql/lib/change-notes/released/0.11.1.md
Normal file
@@ -0,0 +1,12 @@
|
||||
## 0.11.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Added better support for API graphs when encountering `from ... import *`. For example in the code `from foo import *; Bar()`, we will now find a result for `API::moduleImport("foo").getMember("Bar").getACall()`
|
||||
* Deleted the deprecated `isBarrierGuard` predicate from the dataflow library and its uses, use `isBarrier` and the `BarrierGuard` module instead.
|
||||
* Deleted the deprecated `getAUse`, `getAnImmediateUse`, `getARhs`, and `getAValueReachingRhs` predicates from the `API::Node` class.
|
||||
* Deleted the deprecated `fullyQualifiedToAPIGraphPath` class from `SubclassFinder.qll`, use `fullyQualifiedToApiGraphPath` instead.
|
||||
* Deleted the deprecated `Paths.qll` file.
|
||||
* Deleted the deprecated `semmle.python.security.performance` folder, use `semmle.python.security.regexp` instead.
|
||||
* Deleted the deprecated `semmle.python.security.strings` and `semmle.python.web` folders.
|
||||
* Improved modeling of decoding through pickle related functions (which can lead to code execution), resulting in additional sinks for the _Deserializing untrusted input_ query (`py/unsafe-deserialization`). Added support for `pandas.read_pickle`, `numpy.load` and `joblib.load`.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.10.3
|
||||
lastReleaseVersion: 0.11.1
|
||||
|
||||
6
python/ql/lib/experimental/cryptography/Concepts.qll
Normal file
6
python/ql/lib/experimental/cryptography/Concepts.qll
Normal file
@@ -0,0 +1,6 @@
|
||||
import experimental.cryptography.CryptoArtifact
|
||||
import experimental.cryptography.CryptoAlgorithmNames
|
||||
import semmle.python.ApiGraphs
|
||||
import experimental.cryptography.modules.stdlib.HashlibModule as HashLibModule
|
||||
import experimental.cryptography.modules.stdlib.HmacModule as HMacModule
|
||||
import experimental.cryptography.modules.CryptographyModule as CryptographyModule
|
||||
228
python/ql/lib/experimental/cryptography/CryptoAlgorithmNames.qll
Normal file
228
python/ql/lib/experimental/cryptography/CryptoAlgorithmNames.qll
Normal file
@@ -0,0 +1,228 @@
|
||||
/**
|
||||
* Names of known cryptographic algorithms.
|
||||
* The names are standardized into upper-case, no spaces, dashes or underscores.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns a string to represent generally unknown algorithms.
|
||||
* Predicate is to be used to get a consistent string representation
|
||||
* for unknown algorithms.
|
||||
*/
|
||||
string unknownAlgorithm() { result = "UNKNOWN" }
|
||||
|
||||
string getHashType() { result = "HASH" }
|
||||
|
||||
string getSymmetricEncryptionType() { result = "SYMMETRIC_ENCRYPTION" }
|
||||
|
||||
string getAsymmetricEncryptionType() { result = "ASYMMETRIC_ENCRYPTION" }
|
||||
|
||||
string getKeyDerivationType() { result = "KEY_DERIVATION" }
|
||||
|
||||
string getCipherBlockModeType() { result = "BLOCK_MODE" }
|
||||
|
||||
string getSymmetricPaddingType() { result = "SYMMETRIC_PADDING" }
|
||||
|
||||
string getAsymmetricPaddingType() { result = "ASYMMETRIC_PADDING" }
|
||||
|
||||
string getEllipticCurveType() { result = "ELLIPTIC_CURVE" }
|
||||
|
||||
string getSignatureType() { result = "SIGNATURE" }
|
||||
|
||||
string getKeyExchangeType() { result = "KEY_EXCHANGE" }
|
||||
|
||||
predicate isKnownType(string algType) {
|
||||
algType in [
|
||||
getHashType(), getSymmetricEncryptionType(), getAsymmetricEncryptionType(),
|
||||
getKeyDerivationType(), getCipherBlockModeType(), getSymmetricPaddingType(),
|
||||
getAsymmetricPaddingType(), getEllipticCurveType(), getSignatureType(), getKeyExchangeType()
|
||||
]
|
||||
}
|
||||
|
||||
predicate isKnownAlgorithm(string name) { isKnownAlgorithm(name, _) }
|
||||
|
||||
predicate isKnownAlgorithm(string name, string algType) {
|
||||
isHashingAlgorithm(name) and algType = "HASH"
|
||||
or
|
||||
isEncryptionAlgorithm(name, algType) and
|
||||
algType in ["SYMMETRIC_ENCRYPTION", "ASYMMETRIC_ENCRYPTION"]
|
||||
or
|
||||
isKeyDerivationAlgorithm(name) and algType = "KEY_DERIVATION"
|
||||
or
|
||||
isCipherBlockModeAlgorithm(name) and algType = "BLOCK_MODE"
|
||||
or
|
||||
isPaddingAlgorithm(name, algType) and algType in ["SYMMETRIC_PADDING", "ASYMMETRIC_PADDING"]
|
||||
or
|
||||
isEllipticCurveAlgorithm(name) and algType = "ELLIPTIC_CURVE"
|
||||
or
|
||||
isSignatureAlgorithm(name) and algType = "SIGNATURE"
|
||||
or
|
||||
isKeyExchangeAlgorithm(name) and algType = "KEY_EXCHANGE"
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` is a known hashing algorithm in the model/library.
|
||||
*/
|
||||
predicate isHashingAlgorithm(string name) {
|
||||
name =
|
||||
[
|
||||
"BLAKE2", "BLAKE2B", "BLAKE2S", "SHA2", "SHA224", "SHA256", "SHA384", "SHA512", "SHA512224",
|
||||
"SHA512256", "SHA3", "SHA3224", "SHA3256", "SHA3384", "SHA3512", "SHAKE128", "SHAKE256",
|
||||
"SM3", "WHIRLPOOL", "POLY1305", "HAVEL128", "MD2", "MD4", "MD5", "PANAMA", "RIPEMD",
|
||||
"RIPEMD128", "RIPEMD256", "RIPEMD160", "RIPEMD320", "SHA0", "SHA1", "SHA", "MGF1", "MGF1SHA1",
|
||||
"MDC2", "SIPHASH"
|
||||
]
|
||||
}
|
||||
|
||||
predicate isEncryptionAlgorithm(string name, string algType) {
|
||||
isAsymmetricEncryptionAlgorithm(name) and algType = "ASYMMETRIC_ENCRYPTION"
|
||||
or
|
||||
isSymmetricEncryptionAlgorithm(name) and algType = "SYMMETRIC_ENCRYPTION"
|
||||
}
|
||||
|
||||
predicate isEncryptionAlgorithm(string name) { isEncryptionAlgorithm(name, _) }
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known symmetric encryption algorithm.
|
||||
*/
|
||||
predicate isSymmetricEncryptionAlgorithm(string name) {
|
||||
// NOTE: AES is meant to caputure all possible key lengths
|
||||
name =
|
||||
[
|
||||
"AES", "AES128", "AES192", "AES256", "ARIA", "BLOWFISH", "BF", "ECIES", "CAST", "CAST5",
|
||||
"CAMELLIA", "CAMELLIA128", "CAMELLIA192", "CAMELLIA256", "CHACHA", "CHACHA20",
|
||||
"CHACHA20POLY1305", "GOST", "GOSTR34102001", "GOSTR341094", "GOSTR341194", "GOST2814789",
|
||||
"GOSTR341194", "GOST2814789", "GOST28147", "GOSTR341094", "GOST89", "GOST94", "GOST34102012",
|
||||
"GOST34112012", "IDEA", "RABBIT", "SEED", "SM4", "DES", "DESX", "3DES", "TDES", "2DES",
|
||||
"DES3", "TRIPLEDES", "TDEA", "TRIPLEDEA", "ARC2", "RC2", "ARC4", "RC4", "ARCFOUR", "ARC5",
|
||||
"RC5", "MAGMA", "KUZNYECHIK"
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known key derivation algorithm.
|
||||
*/
|
||||
predicate isKeyDerivationAlgorithm(string name) {
|
||||
name =
|
||||
[
|
||||
"ARGON2", "CONCATKDF", "CONCATKDFHASH", "CONCATKDFHMAC", "KBKDFCMAC", "BCRYPT", "HKDF",
|
||||
"HKDFEXPAND", "KBKDF", "KBKDFHMAC", "PBKDF1", "PBKDF2", "PBKDF2HMAC", "PKCS5", "SCRYPT",
|
||||
"X963KDF", "EVPKDF"
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known cipher block mode
|
||||
*/
|
||||
predicate isCipherBlockModeAlgorithm(string name) {
|
||||
name = ["CBC", "GCM", "CCM", "CFB", "OFB", "CFB8", "CTR", "OPENPGP", "XTS", "EAX", "SIV", "ECB"]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known padding algorithm
|
||||
*/
|
||||
predicate isPaddingAlgorithm(string name, string algType) {
|
||||
isSymmetricPaddingAlgorithm(name) and algType = "SYMMETRIC_PADDING"
|
||||
or
|
||||
isAsymmetricPaddingAlgorithm(name) and algType = "ASYMMETRIC_PADDING"
|
||||
}
|
||||
|
||||
/**
|
||||
* holds if `name` corresponds to a known symmetric padding algorithm
|
||||
*/
|
||||
predicate isSymmetricPaddingAlgorithm(string name) { name = ["PKCS7", "ANSIX923"] }
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known asymmetric padding algorithm
|
||||
*/
|
||||
predicate isAsymmetricPaddingAlgorithm(string name) { name = ["OAEP", "PKCS1V15", "PSS", "KEM"] }
|
||||
|
||||
predicate isBrainpoolCurve(string curveName, int keySize) {
|
||||
// ALL BRAINPOOL CURVES
|
||||
keySize in [160, 192, 224, 256, 320, 384, 512] and
|
||||
(
|
||||
curveName = "BRAINPOOLP" + keySize.toString() + "R1"
|
||||
or
|
||||
curveName = "BRAINPOOLP" + keySize.toString() + "T1"
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSecCurve(string curveName, int keySize) {
|
||||
// ALL SEC CURVES
|
||||
keySize in [112, 113, 128, 131, 160, 163, 192, 193, 224, 233, 239, 256, 283, 384, 409, 521, 571] and
|
||||
exists(string suff | suff in ["R1", "R2", "K1"] |
|
||||
curveName = "SECT" + keySize.toString() + suff or
|
||||
curveName = "SECP" + keySize.toString() + suff
|
||||
)
|
||||
}
|
||||
|
||||
predicate isC2Curve(string curveName, int keySize) {
|
||||
// ALL C2 CURVES
|
||||
keySize in [163, 176, 191, 208, 239, 272, 304, 359, 368, 431] and
|
||||
exists(string pre, string suff |
|
||||
pre in ["PNB", "ONB", "TNB"] and suff in ["V1", "V2", "V3", "V4", "V5", "W1", "R1"]
|
||||
|
|
||||
curveName = "C2" + pre + keySize.toString() + suff
|
||||
)
|
||||
}
|
||||
|
||||
predicate isPrimeCurve(string curveName, int keySize) {
|
||||
// ALL PRIME CURVES
|
||||
keySize in [192, 239, 256] and
|
||||
exists(string suff | suff in ["V1", "V2", "V3"] | curveName = "PRIME" + keySize.toString() + suff)
|
||||
}
|
||||
|
||||
predicate isEllipticCurveAlgorithm(string curveName) { isEllipticCurveAlgorithm(curveName, _) }
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known elliptic curve.
|
||||
*/
|
||||
predicate isEllipticCurveAlgorithm(string curveName, int keySize) {
|
||||
isSecCurve(curveName, keySize)
|
||||
or
|
||||
isBrainpoolCurve(curveName, keySize)
|
||||
or
|
||||
isC2Curve(curveName, keySize)
|
||||
or
|
||||
isPrimeCurve(curveName, keySize)
|
||||
or
|
||||
curveName = "ES256" and keySize = 256
|
||||
or
|
||||
curveName = "CURVE25519" and keySize = 255
|
||||
or
|
||||
curveName = "X25519" and keySize = 255
|
||||
or
|
||||
curveName = "ED25519" and keySize = 255
|
||||
or
|
||||
curveName = "CURVE448" and keySize = 448 // TODO: need to check the key size
|
||||
or
|
||||
curveName = "ED448" and keySize = 448
|
||||
or
|
||||
curveName = "X448" and keySize = 448
|
||||
or
|
||||
curveName = "NUMSP256T1" and keySize = 256
|
||||
or
|
||||
curveName = "NUMSP384T1" and keySize = 384
|
||||
or
|
||||
curveName = "NUMSP512T1" and keySize = 512
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known signature algorithm.
|
||||
*/
|
||||
predicate isSignatureAlgorithm(string name) {
|
||||
name =
|
||||
[
|
||||
"DSA", "ECDSA", "EDDSA", "ES256", "ES256K", "ES384", "ES512", "ED25519", "ED448", "ECDSA256",
|
||||
"ECDSA384", "ECDSA512"
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` is a key exchange algorithm.
|
||||
*/
|
||||
predicate isKeyExchangeAlgorithm(string name) { name = ["ECDH", "DIFFIEHELLMAN", "X25519", "X448"] }
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a known asymmetric encryption.
|
||||
*/
|
||||
predicate isAsymmetricEncryptionAlgorithm(string name) { name = ["RSA"] }
|
||||
264
python/ql/lib/experimental/cryptography/CryptoArtifact.qll
Normal file
264
python/ql/lib/experimental/cryptography/CryptoArtifact.qll
Normal file
@@ -0,0 +1,264 @@
|
||||
import python
|
||||
private import semmle.python.ApiGraphs
|
||||
private import experimental.cryptography.CryptoAlgorithmNames
|
||||
private import experimental.cryptography.utils.Utils as Utils
|
||||
|
||||
/*
|
||||
* A cryptographic artifact is a DataFlow::Node associated with some
|
||||
* operation, algorithm, or any other aspect of cryptography.
|
||||
*/
|
||||
|
||||
abstract class CryptographicArtifact extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* Associates a symmetric encryption algorithm with a block mode.
|
||||
* The DataFlow::Node representing this association should be the
|
||||
* point where the algorithm and block mode are combined.
|
||||
* This may be at the call to encryption or in the construction
|
||||
* of an object prior to encryption.
|
||||
*/
|
||||
abstract class SymmetricCipher extends CryptographicArtifact {
|
||||
abstract SymmetricEncryptionAlgorithm getEncryptionAlgorithm();
|
||||
|
||||
abstract BlockMode getBlockMode();
|
||||
|
||||
final predicate hasBlockMode() { exists(this.getBlockMode()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A cryptographic operation is a method call that invokes a cryptographic
|
||||
* algorithm (encrypt/decrypt) or a function in support of a cryptographic algorithm
|
||||
* (key generation).
|
||||
*
|
||||
* Since operations are related to or in support of algorithms, operations must
|
||||
* provide a reference to their associated algorithm. Often operataions themselves
|
||||
* encapsulate algorithms, so operations can also extend CryptographicAlgorithm
|
||||
* and refer to themselves as the target algorithm.
|
||||
*/
|
||||
abstract class CryptographicOperation extends CryptographicArtifact, API::CallNode {
|
||||
bindingset[paramName, ind]
|
||||
final DataFlow::Node getParameterSource(int ind, string paramName) {
|
||||
result = Utils::getUltimateSrcFromApiNode(this.(API::CallNode).getParameter(ind, paramName))
|
||||
}
|
||||
|
||||
final string getAlgorithmName() {
|
||||
if exists(this.getAlgorithm().getName())
|
||||
then result = this.getAlgorithm().getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
final predicate hasAlgorithm() { exists(this.getAlgorithm()) }
|
||||
|
||||
final predicate isUnknownAlgorithm() {
|
||||
this.getAlgorithmName() = unknownAlgorithm()
|
||||
or
|
||||
not this.hasAlgorithm()
|
||||
}
|
||||
|
||||
// TODO: this might have to be parameterized by a configuration source for
|
||||
// situations where an operation is passed an algorithm
|
||||
abstract CryptographicAlgorithm getAlgorithm();
|
||||
}
|
||||
|
||||
/** A key generation operation for asymmetric keys */
|
||||
abstract class KeyGen extends CryptographicOperation {
|
||||
int getAKeySizeInBits() { result = this.getKeySizeInBits(_) }
|
||||
|
||||
final predicate hasKeySize(DataFlow::Node configSrc) { exists(this.getKeySizeInBits(configSrc)) }
|
||||
|
||||
final predicate hasKeySize() { exists(this.getAKeySizeInBits()) }
|
||||
|
||||
abstract DataFlow::Node getKeyConfigSrc();
|
||||
|
||||
abstract int getKeySizeInBits(DataFlow::Node configSrc);
|
||||
}
|
||||
|
||||
abstract class AsymmetricKeyGen extends KeyGen { }
|
||||
|
||||
abstract class SymmetricKeyGen extends KeyGen { }
|
||||
|
||||
/**
|
||||
* A cryptographic algorithm is a `CryptographicArtifact`
|
||||
* representing a cryptographic algorithm (see `CryptoAlgorithmNames.qll`).
|
||||
* Cryptographic algorithms can be functions referencing common crypto algorithms (e.g., hashlib.md5)
|
||||
* or strings that are used in cryptographic operation configurations (e.g., hashlib.new("md5")).
|
||||
* Cryptogrpahic algorithms may also be operations that wrap or abstract one or
|
||||
* more algorithms (e.g., cyrptography.fernet.Fernet and AES, CBC and PKCS7).
|
||||
*
|
||||
* In principle, this class should model the location where an algorithm enters the program, not
|
||||
* necessarily where it is used.
|
||||
*/
|
||||
abstract class CryptographicAlgorithm extends CryptographicArtifact {
|
||||
abstract string getName();
|
||||
|
||||
// TODO: handle case where name isn't known, not just unknown?
|
||||
/**
|
||||
* Normalizes a raw name into a normalized name as found in `CryptoAlgorithmNames.qll`.
|
||||
* Subclassess should override for more api-specific normalization.
|
||||
* By deafult, converts a raw name to upper-case with no hyphen, underscore, hash, or space.
|
||||
*/
|
||||
bindingset[s]
|
||||
string normalizeName(string s) {
|
||||
exists(string normStr | normStr = s.toUpperCase().regexpReplaceAll("[-_ ]", "") |
|
||||
result = normStr and isKnownAlgorithm(result)
|
||||
or
|
||||
result = unknownAlgorithm() and not isKnownAlgorithm(normStr)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// class UnknownAlgorithm extends DataFlow::Node instanceof CryptographicAlgorithm{
|
||||
// UnknownAlgorithm(){
|
||||
// super.getName() = unknownAlgorithm()
|
||||
// }
|
||||
// }
|
||||
abstract class HashAlgorithm extends CryptographicAlgorithm {
|
||||
final string getHashName() {
|
||||
if exists(string n | n = this.getName() and isHashingAlgorithm(n))
|
||||
then isHashingAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class KeyDerivationAlgorithm extends CryptographicAlgorithm {
|
||||
final string getKDFName() {
|
||||
if exists(string n | n = this.getName() and isKeyDerivationAlgorithm(n))
|
||||
then isKeyDerivationAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class KeyDerivationOperation extends CryptographicOperation {
|
||||
DataFlow::Node getIterationSizeSrc() { none() }
|
||||
|
||||
DataFlow::Node getSaltConfigSrc() { none() }
|
||||
|
||||
DataFlow::Node getHashConfigSrc() { none() }
|
||||
|
||||
// TODO: get encryption algorithm for CBC-based KDF?
|
||||
DataFlow::Node getDerivedKeySizeSrc() { none() }
|
||||
|
||||
DataFlow::Node getModeSrc() { none() }
|
||||
|
||||
// TODO: add more to cover all the parameters of most KDF operations? Perhaps subclass for each type?
|
||||
abstract predicate requiresIteration();
|
||||
|
||||
abstract predicate requiresSalt();
|
||||
|
||||
abstract predicate requiresHash();
|
||||
|
||||
//abstract predicate requiresKeySize(); // Going to assume all requires a size
|
||||
abstract predicate requiresMode();
|
||||
}
|
||||
|
||||
/**
|
||||
* A parent class to represent any algorithm for which
|
||||
* asymmetric cryptography is involved.
|
||||
* Intended to be distinct from AsymmetricEncryptionAlgorithm
|
||||
* which is intended only for asymmetric algorithms that specifically encrypt.
|
||||
*/
|
||||
abstract class AsymmetricAlgorithm extends CryptographicAlgorithm { }
|
||||
|
||||
abstract class EncryptionAlgorithm extends CryptographicAlgorithm {
|
||||
final predicate isAsymmetric() { this instanceof AsymmetricEncryptionAlgorithm }
|
||||
|
||||
final predicate isSymmetric() { not this.isAsymmetric() }
|
||||
// NOTE: DO_NOT add getEncryptionName here, we rely on the fact the parent
|
||||
// class does not have this common predicate.
|
||||
}
|
||||
|
||||
/**
|
||||
* Algorithms directly or indirectly related to asymmetric encryption,
|
||||
* e.g., RSA, DSA, but also RSA padding algorithms
|
||||
*/
|
||||
abstract class AsymmetricEncryptionAlgorithm extends AsymmetricAlgorithm, EncryptionAlgorithm {
|
||||
final string getEncryptionName() {
|
||||
if exists(string n | n = this.getName() and isAsymmetricEncryptionAlgorithm(n))
|
||||
then isAsymmetricEncryptionAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Algorithms directly or indirectly related to symmetric encryption,
|
||||
* e.g., AES, DES, but also block modes and padding
|
||||
*/
|
||||
abstract class SymmetricEncryptionAlgorithm extends EncryptionAlgorithm {
|
||||
final string getEncryptionName() {
|
||||
if exists(string n | n = this.getName() and isSymmetricEncryptionAlgorithm(n))
|
||||
then isSymmetricEncryptionAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
// TODO: add a stream cipher predicate?
|
||||
}
|
||||
|
||||
// Used only to categorize all padding into a single object,
|
||||
// DO_NOT add predicates here. Only for categorization purposes.
|
||||
abstract class PaddingAlgorithm extends CryptographicAlgorithm { }
|
||||
|
||||
abstract class SymmetricPadding extends PaddingAlgorithm {
|
||||
final string getPaddingName() {
|
||||
if exists(string n | n = this.getName() and isSymmetricPaddingAlgorithm(n))
|
||||
then isSymmetricPaddingAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AsymmetricPadding extends PaddingAlgorithm {
|
||||
final string getPaddingName() {
|
||||
if exists(string n | n = this.getName() and isAsymmetricPaddingAlgorithm(n))
|
||||
then isAsymmetricPaddingAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class EllipticCurveAlgorithm extends AsymmetricAlgorithm {
|
||||
final string getCurveName() {
|
||||
if exists(string n | n = this.getName() and isEllipticCurveAlgorithm(n))
|
||||
then isEllipticCurveAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
final int getCurveBitSize() { isEllipticCurveAlgorithm(this.getCurveName(), result) }
|
||||
}
|
||||
|
||||
abstract class BlockMode extends CryptographicAlgorithm {
|
||||
final string getBlockModeName() {
|
||||
if exists(string n | n = this.getName() and isCipherBlockModeAlgorithm(n))
|
||||
then isCipherBlockModeAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source of the IV configuration.
|
||||
*/
|
||||
abstract DataFlow::Node getIVorNonce();
|
||||
|
||||
final predicate hasIVorNonce() { exists(this.getIVorNonce()) }
|
||||
}
|
||||
|
||||
abstract class KeyWrapOperation extends CryptographicOperation { }
|
||||
|
||||
abstract class AuthenticatedEncryptionAlgorithm extends SymmetricEncryptionAlgorithm {
|
||||
final string getAuthticatedEncryptionName() {
|
||||
if exists(string n | n = this.getName() and isSymmetricEncryptionAlgorithm(n))
|
||||
then isSymmetricEncryptionAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class KeyExchangeAlgorithm extends AsymmetricAlgorithm {
|
||||
final string getKeyExchangeName() {
|
||||
if exists(string n | n = this.getName() and isKeyExchangeAlgorithm(n))
|
||||
then isKeyExchangeAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class SigningAlgorithm extends AsymmetricAlgorithm {
|
||||
final string getSigningName() {
|
||||
if exists(string n | n = this.getName() and isSignatureAlgorithm(n))
|
||||
then isSignatureAlgorithm(result) and result = this.getName()
|
||||
else result = unknownAlgorithm()
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,237 @@
|
||||
import python
|
||||
import semmle.python.ApiGraphs
|
||||
import experimental.cryptography.CryptoArtifact
|
||||
private import experimental.cryptography.utils.Utils as Utils
|
||||
private import experimental.cryptography.CryptoAlgorithmNames
|
||||
|
||||
/**
|
||||
* `hashlib` is a ptyhon standard library module for hashing algorithms.
|
||||
* https://docs.python.org/3/library/hashlib.html
|
||||
* This is an abstract class to reference all hashlib artifacts.
|
||||
*/
|
||||
// -----------------------------------------------
|
||||
// Hash Artifacts
|
||||
// -----------------------------------------------
|
||||
module Hashes {
|
||||
/**
|
||||
* Represents a hash algorithm used by `hashlib.new`, where the hash algorithm is a string in the first argument.
|
||||
*/
|
||||
class HashlibNewHashAlgorithm extends HashAlgorithm {
|
||||
HashlibNewHashAlgorithm() {
|
||||
this =
|
||||
Utils::getUltimateSrcFromApiNode(API::moduleImport("hashlib")
|
||||
.getMember("new")
|
||||
.getACall()
|
||||
.getParameter(0, "name"))
|
||||
}
|
||||
|
||||
override string getName() {
|
||||
result = super.normalizeName(this.asExpr().(StrConst).getText())
|
||||
or
|
||||
// if not a known/static string, assume from an outside source and the algorithm is UNKNOWN
|
||||
not this.asExpr() instanceof StrConst and result = unknownAlgorithm()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies hashlib.pbdkf2_hmac calls, identifying the hash algorithm used
|
||||
* in the hmac (matching kdf is handled separately by `HashlibPbkdf2HMACArtifact`).
|
||||
*
|
||||
* https://docs.python.org/3/library/hashlib.html#hashlib.pbkdf2_hmac
|
||||
*/
|
||||
class HashlibPbkdf2HMACHashAlgorithm extends HashAlgorithm {
|
||||
HashlibPbkdf2HMACHashAlgorithm() {
|
||||
this =
|
||||
Utils::getUltimateSrcFromApiNode(API::moduleImport("hashlib")
|
||||
.getMember("pbkdf2_hmac")
|
||||
.getACall()
|
||||
.getParameter(0, "hash_name"))
|
||||
}
|
||||
|
||||
override string getName() {
|
||||
result = super.normalizeName(this.asExpr().(StrConst).getText())
|
||||
or
|
||||
// if not a known/static string, assume from an outside source and the algorithm is UNKNOWN
|
||||
not this.asExpr() instanceof StrConst and result = unknownAlgorithm()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a call to `hashlib.file_digest` where the hash algorithm is the first argument in `digest`
|
||||
* `nameSrc` is the source of the first argument.
|
||||
*
|
||||
* https://docs.python.org/3/library/hashlib.html#hashlib.file_digest
|
||||
*
|
||||
* NOTE: the digest argument can be, in addition to a string,
|
||||
* a callable that returns a hash object or a hash constructor.
|
||||
* These cases are not considered here since they would be detected separately.
|
||||
* Specifically, other non-string cases are themselves considered sources for alerts, e.g.,
|
||||
* references to hashlib.sha512 is found by `HashlibMemberAlgorithm`.
|
||||
* The only exception is if the source is not a string constant or HashlibMemberAlgorithm.
|
||||
* In these cases, the algorithm is considered 'UNKNOWN'.
|
||||
*/
|
||||
class HashlibFileDigestAlgorithm extends HashAlgorithm {
|
||||
HashlibFileDigestAlgorithm() {
|
||||
this =
|
||||
Utils::getUltimateSrcFromApiNode(API::moduleImport("hashlib")
|
||||
.getMember("file_digest")
|
||||
.getACall()
|
||||
.getParameter(1, "digest")) and
|
||||
// Ignore sources that are hash constructors, allow `HashlibMemberAlgorithm` to detect these
|
||||
this != hashlibMemberHashAlgorithm(_) and
|
||||
// Ignore sources that are HMAC objects, to be handled by HmacModule
|
||||
this != API::moduleImport("hmac").getMember("new").getACall() and
|
||||
this != API::moduleImport("hmac").getMember("HMAC").getACall()
|
||||
}
|
||||
|
||||
override string getName() {
|
||||
// Name is a string constant or consider the name unknown
|
||||
// NOTE: we are excluding hmac.new and hmac.HMAC constructor calls so we are expecting
|
||||
// a string or an outside configuration only
|
||||
result = super.normalizeName(this.asExpr().(StrConst).getText())
|
||||
or
|
||||
not this.asExpr() instanceof StrConst and
|
||||
result = unknownAlgorithm()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a member access of hashlib that is an algorithm invocation.
|
||||
* `hashName` is the name of the hash algorithm.
|
||||
*
|
||||
* Note: oringally a variant of this predicate was in codeql/github/main
|
||||
* to a predicate to avoid a bad join order.
|
||||
*/
|
||||
// Copying use of nomagic from similar predicate in codeql/main
|
||||
pragma[nomagic]
|
||||
DataFlow::Node hashlibMemberHashAlgorithm(string hashName) {
|
||||
result = API::moduleImport("hashlib").getMember(hashName).asSource() and
|
||||
// Don't matches known non-hash members
|
||||
not hashName in [
|
||||
"new", "pbkdf2_hmac", "algorithms_available", "algorithms_guaranteed", "file_digest"
|
||||
] and
|
||||
// Don't match things like __file__
|
||||
not hashName.regexpMatch("_.*")
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies hashing algorithm members (i.e., functions) of the `hashlib` module,
|
||||
* e.g., `hashlib.sha512`.
|
||||
*/
|
||||
class HashlibMemberAlgorithm extends HashAlgorithm {
|
||||
HashlibMemberAlgorithm() { this = hashlibMemberHashAlgorithm(_) }
|
||||
|
||||
override string getName() {
|
||||
exists(string rawName |
|
||||
result = super.normalizeName(rawName) and this = hashlibMemberHashAlgorithm(rawName)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------
|
||||
// Key Derivation Functions
|
||||
// -----------------------------------------------
|
||||
module KDF {
|
||||
// NOTE: Only finds the params of `pbkdf2_hmac` that are non-optional
|
||||
// dk_len is optional, i.e., can be None, and if addressed in this predicate
|
||||
// would result in an unsatisfiable predicate.
|
||||
predicate hashlibPBDKF2HMACKDFRequiredParams(
|
||||
HashlibPbkdf2HMACOperation kdf, API::Node hashParam, API::Node saltParam,
|
||||
API::Node iterationParam
|
||||
) {
|
||||
kdf.getParameter(0, "hash_name") = hashParam and
|
||||
kdf.getParameter(2, "salt") = saltParam and
|
||||
kdf.getParameter(3, "iterations") = iterationParam
|
||||
}
|
||||
|
||||
predicate hashlibPBDKF2HMACKDFOptionalParams(HashlibPbkdf2HMACOperation kdf, API::Node keylenParam) {
|
||||
kdf.getParameter(4, "dklen") = keylenParam
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies kery derivation function hashlib.pbdkf2_hmac accesses.
|
||||
* https://docs.python.org/3/library/hashlib.html#hashlib.pbkdf2_hmac
|
||||
*/
|
||||
class HashlibPbkdf2HMACOperation extends KeyDerivationAlgorithm, KeyDerivationOperation {
|
||||
HashlibPbkdf2HMACOperation() {
|
||||
this = API::moduleImport("hashlib").getMember("pbkdf2_hmac").getACall()
|
||||
}
|
||||
|
||||
override string getName() { result = super.normalizeName("pbkdf2_hmac") }
|
||||
|
||||
override DataFlow::Node getIterationSizeSrc() {
|
||||
exists(API::Node it | hashlibPBDKF2HMACKDFRequiredParams(this, _, _, it) |
|
||||
result = Utils::getUltimateSrcFromApiNode(it)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getSaltConfigSrc() {
|
||||
exists(API::Node s | hashlibPBDKF2HMACKDFRequiredParams(this, _, s, _) |
|
||||
result = Utils::getUltimateSrcFromApiNode(s)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getHashConfigSrc() {
|
||||
exists(API::Node h | hashlibPBDKF2HMACKDFRequiredParams(this, h, _, _) |
|
||||
result = Utils::getUltimateSrcFromApiNode(h)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getDerivedKeySizeSrc() {
|
||||
exists(API::Node dk | hashlibPBDKF2HMACKDFOptionalParams(this, dk) |
|
||||
result = Utils::getUltimateSrcFromApiNode(dk)
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: if DK is none, then the length is based on the hash type, if hash length not known, must call this unknown
|
||||
// The issue is the src is what we model not the size
|
||||
// For now, we are not modeling this and are relying on the fact that the accepted hashes are of accepted length.
|
||||
// I.e., any query looking at length will ignore cases where it is unknown
|
||||
override KeyDerivationAlgorithm getAlgorithm() { result = this }
|
||||
|
||||
override predicate requiresHash() { any() }
|
||||
|
||||
override predicate requiresMode() { none() }
|
||||
|
||||
override predicate requiresSalt() { any() }
|
||||
|
||||
override predicate requiresIteration() { any() }
|
||||
}
|
||||
|
||||
// TODO: better modeling of scrypt
|
||||
/**
|
||||
* Identifies key derivation fucntion hashlib.scrypt accesses.
|
||||
*/
|
||||
class HashlibScryptAlgorithm extends KeyDerivationAlgorithm, KeyDerivationOperation {
|
||||
HashlibScryptAlgorithm() { this = API::moduleImport("hashlib").getMember("scrypt").getACall() }
|
||||
|
||||
override string getName() { result = super.normalizeName("scrypt") }
|
||||
|
||||
override DataFlow::Node getIterationSizeSrc() { none() }
|
||||
|
||||
override DataFlow::Node getSaltConfigSrc() {
|
||||
// TODO: need to address getting salt from params, unsure how this works in CodeQL
|
||||
// since the signature is defined as hashlib.scrypt(password, *, salt, n, r, p, maxmem=0, dklen=64)
|
||||
// What position is 'salt' then such that we can reliably extract it?
|
||||
none()
|
||||
}
|
||||
|
||||
override DataFlow::Node getHashConfigSrc() { none() }
|
||||
|
||||
override DataFlow::Node getDerivedKeySizeSrc() {
|
||||
//TODO: see comment for getSaltConfigSrc above
|
||||
none()
|
||||
}
|
||||
|
||||
override KeyDerivationAlgorithm getAlgorithm() { result = this }
|
||||
|
||||
override predicate requiresHash() { none() }
|
||||
|
||||
override predicate requiresMode() { none() }
|
||||
|
||||
override predicate requiresSalt() { any() }
|
||||
|
||||
override predicate requiresIteration() { none() }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import python
|
||||
import semmle.python.ApiGraphs
|
||||
import experimental.cryptography.CryptoArtifact
|
||||
private import experimental.cryptography.utils.Utils as Utils
|
||||
private import experimental.cryptography.CryptoAlgorithmNames
|
||||
private import experimental.cryptography.modules.stdlib.HashlibModule as HashlibModule
|
||||
|
||||
/**
|
||||
* `hmac` is a ptyhon standard library module for key-based hashing algorithms.
|
||||
* https://docs.python.org/3/library/hmac.html
|
||||
*/
|
||||
// -----------------------------------------------
|
||||
// Hash Artifacts
|
||||
// -----------------------------------------------
|
||||
module Hashes {
|
||||
class GenericHmacHashCall extends API::CallNode {
|
||||
GenericHmacHashCall() {
|
||||
this = API::moduleImport("hmac").getMember("new").getACall() or
|
||||
this = API::moduleImport("hmac").getMember("HMAC").getACall() or
|
||||
this = API::moduleImport("hmac").getMember("digest").getACall()
|
||||
}
|
||||
}
|
||||
|
||||
DataFlow::Node getDigestModParamSrc(GenericHmacHashCall call) {
|
||||
result = Utils::getUltimateSrcFromApiNode(call.(API::CallNode).getParameter(2, "digestmod"))
|
||||
}
|
||||
|
||||
/**
|
||||
* This class captures the common behavior for all HMAC operations:
|
||||
* hmac.HMAC https://docs.python.org/3/library/hmac.html#hmac.HMAC
|
||||
* hmac.new https://docs.python.org/3/library/hmac.html#hmac.new
|
||||
* hmac.digest https://docs.python.org/3/library/hmac.html#hmac.digest
|
||||
* These operations commonly set the algorithm as a string in the third argument (`digestmod`)
|
||||
* of the operation itself.
|
||||
*
|
||||
* NOTE: `digestmod` is the digest name, digest constructor or module for the HMAC object to use, however
|
||||
* this class only identifies string names. The other forms are found by CryptopgraphicArtifacts,
|
||||
* modeled in `HmacHMACConsArtifact` and `Hashlib.qll`, specifically through hashlib.new and
|
||||
* direct member accesses (e.g., hashlib.md5).
|
||||
*
|
||||
* Where no `digestmod` exists, the algorithm is assumed to be `md5` per the docs found here:
|
||||
* https://docs.python.org/3/library/hmac.html#hmac.new
|
||||
*
|
||||
* Where `digestmod` exists but is not a string and not a hashlib algorithm, it is assumed
|
||||
* to be `UNKNOWN`. Note this includes cases wheere the digest is provided as a `A module supporting PEP 247.`
|
||||
* Such modules are currently not modeled.
|
||||
*/
|
||||
class HmacGenericAlgorithm extends HashAlgorithm {
|
||||
HmacGenericAlgorithm() {
|
||||
exists(GenericHmacHashCall c |
|
||||
if not exists(getDigestModParamSrc(c)) then this = c else this = getDigestModParamSrc(c)
|
||||
) and
|
||||
// Ignore case where the algorithm is a hashlib algorithm, rely on `HashlibMemberAlgorithm` to catch these cases
|
||||
not this instanceof HashlibModule::Hashes::HashlibMemberAlgorithm
|
||||
// NOTE: the docs say the digest can be `A module supporting PEP 247.`, however, this is not modeled, and will be considered UNKNOWN
|
||||
}
|
||||
|
||||
override string getName() {
|
||||
// when the this is a generic hmac call
|
||||
// it means the algorithm parameter was not identified, assume the default case of 'md5' (per the docs)
|
||||
if this instanceof GenericHmacHashCall
|
||||
then result = super.normalizeName("MD5")
|
||||
else (
|
||||
// Else get the string name, if its a string constant, or UNKNOWN if otherwise
|
||||
result = super.normalizeName(this.asExpr().(StrConst).getText())
|
||||
or
|
||||
not this.asExpr() instanceof StrConst and result = unknownAlgorithm()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Patches all DataFlow::CallCfgNode adding a getTarget predicate to a new
|
||||
* subclass of CallCfgNode
|
||||
*/
|
||||
|
||||
import python
|
||||
private import semmle.python.dataflow.new.internal.TypeTrackerSpecific
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
class CallCfgNodeWithTarget extends DataFlow::Node instanceof DataFlow::CallCfgNode {
|
||||
DataFlow::Node getTarget() { returnStep(result, this) }
|
||||
}
|
||||
21
python/ql/lib/experimental/cryptography/utils/Utils.qll
Normal file
21
python/ql/lib/experimental/cryptography/utils/Utils.qll
Normal file
@@ -0,0 +1,21 @@
|
||||
import python
|
||||
private import semmle.python.ApiGraphs
|
||||
private import experimental.cryptography.utils.CallCfgNodeWithTarget
|
||||
|
||||
/**
|
||||
* Gets an ultimate local source (not a source in a library)
|
||||
*/
|
||||
DataFlow::Node getUltimateSrcFromApiNode(API::Node n) {
|
||||
result = n.getAValueReachingSink() and
|
||||
(
|
||||
// the result is a call to a library only
|
||||
result instanceof CallCfgNodeWithTarget and
|
||||
not result.(CallCfgNodeWithTarget).getTarget().asExpr().getEnclosingModule().inSource()
|
||||
or
|
||||
// the result is not a call, and not a function signataure or parameter
|
||||
not result instanceof CallCfgNodeWithTarget and
|
||||
not result instanceof DataFlow::ParameterNode and
|
||||
not result.asExpr() instanceof FunctionExpr and
|
||||
result.asExpr().getEnclosingModule().inSource()
|
||||
)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/python-all
|
||||
version: 0.10.4-dev
|
||||
version: 0.11.2-dev
|
||||
groups: python
|
||||
dbscheme: semmlecode.python.dbscheme
|
||||
extractor: python
|
||||
|
||||
@@ -155,18 +155,6 @@ module API {
|
||||
*/
|
||||
DataFlow::LocalSourceNode asSource() { Impl::use(this, result) }
|
||||
|
||||
/** DEPRECATED. This predicate has been renamed to `getAValueReachableFromSource()`. */
|
||||
deprecated DataFlow::Node getAUse() { result = this.getAValueReachableFromSource() }
|
||||
|
||||
/** DEPRECATED. This predicate has been renamed to `asSource()`. */
|
||||
deprecated DataFlow::LocalSourceNode getAnImmediateUse() { result = this.asSource() }
|
||||
|
||||
/** DEPRECATED. This predicate has been renamed to `asSink()`. */
|
||||
deprecated DataFlow::Node getARhs() { result = this.asSink() }
|
||||
|
||||
/** DEPRECATED. This predicate has been renamed to `getAValueReachingSink()`. */
|
||||
deprecated DataFlow::Node getAValueReachingRhs() { result = this.getAValueReachingSink() }
|
||||
|
||||
/**
|
||||
* Gets a call to the function represented by this API component.
|
||||
*/
|
||||
|
||||
@@ -547,6 +547,31 @@ class IfExprNode extends ControlFlowNode {
|
||||
override IfExp getNode() { result = super.getNode() }
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to an assignment expression such as `lhs := rhs`. */
|
||||
class AssignmentExprNode extends ControlFlowNode {
|
||||
AssignmentExprNode() { toAst(this) instanceof AssignExpr }
|
||||
|
||||
/** Gets the flow node corresponding to the left-hand side of the assignment expression */
|
||||
ControlFlowNode getTarget() {
|
||||
exists(AssignExpr a |
|
||||
this.getNode() = a and
|
||||
a.getTarget() = result.getNode() and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the flow node corresponding to the right-hand side of the assignment expression */
|
||||
ControlFlowNode getValue() {
|
||||
exists(AssignExpr a |
|
||||
this.getNode() = a and
|
||||
a.getValue() = result.getNode() and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
)
|
||||
}
|
||||
|
||||
override AssignExpr getNode() { result = super.getNode() }
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a binary expression, such as `x + y` */
|
||||
class BinaryExprNode extends ControlFlowNode {
|
||||
BinaryExprNode() { toAst(this) instanceof BinaryExpr }
|
||||
@@ -630,6 +655,8 @@ class DefinitionNode extends ControlFlowNode {
|
||||
Stages::AST::ref() and
|
||||
exists(Assign a | a.getATarget().getAFlowNode() = this)
|
||||
or
|
||||
exists(AssignExpr a | a.getTarget().getAFlowNode() = this)
|
||||
or
|
||||
exists(AnnAssign a | a.getTarget().getAFlowNode() = this and exists(a.getValue()))
|
||||
or
|
||||
exists(Alias a | a.getAsname().getAFlowNode() = this)
|
||||
@@ -787,6 +814,9 @@ private AstNode assigned_value(Expr lhs) {
|
||||
/* lhs = result */
|
||||
exists(Assign a | a.getATarget() = lhs and result = a.getValue())
|
||||
or
|
||||
/* lhs := result */
|
||||
exists(AssignExpr a | a.getTarget() = lhs and result = a.getValue())
|
||||
or
|
||||
/* lhs : annotation = result */
|
||||
exists(AnnAssign a | a.getTarget() = lhs and result = a.getValue())
|
||||
or
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
private import semmle.python.frameworks.Aioch
|
||||
private import semmle.python.frameworks.Aiohttp
|
||||
private import semmle.python.frameworks.Aiomysql
|
||||
private import semmle.python.frameworks.Aiosqlite
|
||||
private import semmle.python.frameworks.Aiopg
|
||||
private import semmle.python.frameworks.Aiosqlite
|
||||
private import semmle.python.frameworks.Asyncpg
|
||||
private import semmle.python.frameworks.BSon
|
||||
private import semmle.python.frameworks.CassandraDriver
|
||||
@@ -28,6 +28,7 @@ private import semmle.python.frameworks.Httpx
|
||||
private import semmle.python.frameworks.Idna
|
||||
private import semmle.python.frameworks.Invoke
|
||||
private import semmle.python.frameworks.Jmespath
|
||||
private import semmle.python.frameworks.Joblib
|
||||
private import semmle.python.frameworks.Ldap
|
||||
private import semmle.python.frameworks.Ldap3
|
||||
private import semmle.python.frameworks.Libtaxii
|
||||
@@ -37,7 +38,9 @@ private import semmle.python.frameworks.MarkupSafe
|
||||
private import semmle.python.frameworks.Multidict
|
||||
private import semmle.python.frameworks.Mysql
|
||||
private import semmle.python.frameworks.MySQLdb
|
||||
private import semmle.python.frameworks.Numpy
|
||||
private import semmle.python.frameworks.Oracledb
|
||||
private import semmle.python.frameworks.Pandas
|
||||
private import semmle.python.frameworks.Peewee
|
||||
private import semmle.python.frameworks.Phoenixdb
|
||||
private import semmle.python.frameworks.Psycopg2
|
||||
@@ -52,11 +55,11 @@ private import semmle.python.frameworks.RestFramework
|
||||
private import semmle.python.frameworks.Rsa
|
||||
private import semmle.python.frameworks.RuamelYaml
|
||||
private import semmle.python.frameworks.ServerLess
|
||||
private import semmle.python.frameworks.Setuptools
|
||||
private import semmle.python.frameworks.Simplejson
|
||||
private import semmle.python.frameworks.SqlAlchemy
|
||||
private import semmle.python.frameworks.Starlette
|
||||
private import semmle.python.frameworks.Stdlib
|
||||
private import semmle.python.frameworks.Setuptools
|
||||
private import semmle.python.frameworks.Toml
|
||||
private import semmle.python.frameworks.Tornado
|
||||
private import semmle.python.frameworks.Twisted
|
||||
|
||||
@@ -34,40 +34,3 @@ class StringConstCompareBarrier extends DataFlow::Node {
|
||||
this = DataFlow::BarrierGuard<stringConstCompare/3>::getABarrierNode()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `StringConstCompareBarrier` instead.
|
||||
*
|
||||
* A validation of unknown node by comparing with a constant string value.
|
||||
*/
|
||||
deprecated class StringConstCompare extends DataFlow::BarrierGuard, CompareNode {
|
||||
ControlFlowNode checked_node;
|
||||
boolean safe_branch;
|
||||
|
||||
StringConstCompare() {
|
||||
exists(StrConst str_const, Cmpop op |
|
||||
op = any(Eq eq) and safe_branch = true
|
||||
or
|
||||
op = any(NotEq ne) and safe_branch = false
|
||||
|
|
||||
this.operands(str_const.getAFlowNode(), op, checked_node)
|
||||
or
|
||||
this.operands(checked_node, op, str_const.getAFlowNode())
|
||||
)
|
||||
or
|
||||
exists(IterableNode str_const_iterable, Cmpop op |
|
||||
op = any(In in_) and safe_branch = true
|
||||
or
|
||||
op = any(NotIn ni) and safe_branch = false
|
||||
|
|
||||
forall(ControlFlowNode elem | elem = str_const_iterable.getAnElement() |
|
||||
elem.getNode() instanceof StrConst
|
||||
) and
|
||||
this.operands(checked_node, op, str_const_iterable)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate checks(ControlFlowNode node, boolean branch) {
|
||||
node = checked_node and branch = safe_branch
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,6 +240,19 @@ predicate hasPropertyDecorator(Function func) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the function `func` has a `contextlib.contextmanager`.
|
||||
*/
|
||||
predicate hasContextmanagerDecorator(Function func) {
|
||||
exists(ControlFlowNode contextmanager |
|
||||
contextmanager.(NameNode).getId() = "contextmanager" and contextmanager.(NameNode).isGlobal()
|
||||
or
|
||||
contextmanager.(AttrNode).getObject("contextmanager").(NameNode).getId() = "contextlib"
|
||||
|
|
||||
func.getADecorator() = contextmanager.getNode()
|
||||
)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Callables
|
||||
// =============================================================================
|
||||
@@ -1604,6 +1617,24 @@ class ExtractedReturnNode extends ReturnNode, CfgNode {
|
||||
override ReturnKind getKind() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that represents the value yielded by a callable with a
|
||||
* `contextlib.contextmanager` decorator. We treat this as a normal return, which makes
|
||||
* things just work when used in a `with` statement -- technically calling the function
|
||||
* directly will give you a `contextlib._GeneratorContextManager` instance, so it's a
|
||||
* slight workaround solution.
|
||||
*
|
||||
* See https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager
|
||||
*/
|
||||
class YieldNodeInContextManagerFunction extends ReturnNode, CfgNode {
|
||||
YieldNodeInContextManagerFunction() {
|
||||
hasContextmanagerDecorator(node.getScope()) and
|
||||
node = any(Yield yield).getValue().getAFlowNode()
|
||||
}
|
||||
|
||||
override ReturnKind getKind() { any() }
|
||||
}
|
||||
|
||||
/** A data-flow node that represents the output of a call. */
|
||||
abstract class OutNode extends Node {
|
||||
/** Gets the underlying call, where this node is a corresponding output of kind `kind`. */
|
||||
|
||||
@@ -91,21 +91,6 @@ abstract class Configuration extends string {
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
@@ -225,29 +210,6 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
@@ -288,9 +250,7 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
|
||||
getConfig(state).isBarrier(node)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
@@ -91,21 +91,6 @@ abstract class Configuration extends string {
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
@@ -225,29 +210,6 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
@@ -288,9 +250,7 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
|
||||
getConfig(state).isBarrier(node)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
@@ -91,21 +91,6 @@ abstract class Configuration extends string {
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
@@ -225,29 +210,6 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
@@ -288,9 +250,7 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
|
||||
getConfig(state).isBarrier(node)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
@@ -91,21 +91,6 @@ abstract class Configuration extends string {
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
@@ -225,29 +210,6 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
@@ -288,9 +250,7 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
|
||||
getConfig(state).isBarrier(node)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
@@ -357,6 +357,9 @@ module EssaFlow {
|
||||
// If expressions
|
||||
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(IfExprNode).getAnOperand()
|
||||
or
|
||||
// Assignment expressions
|
||||
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(AssignmentExprNode).getValue()
|
||||
or
|
||||
// boolean inline expressions such as `x or y` or `x and y`
|
||||
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(BoolExprNode).getAnOperand()
|
||||
or
|
||||
|
||||
@@ -580,32 +580,6 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `BarrierGuard` module instead.
|
||||
*
|
||||
* A guard that validates some expression.
|
||||
*
|
||||
* To use this in a configuration, extend the class and provide a
|
||||
* characteristic predicate precisely specifying the guard, and override
|
||||
* `checks` to specify what is being validated and in which branch.
|
||||
*
|
||||
* It is important that all extending classes in scope are disjoint.
|
||||
*/
|
||||
deprecated class BarrierGuard extends GuardNode {
|
||||
/** Holds if this guard validates `node` upon evaluating to `branch`. */
|
||||
abstract predicate checks(ControlFlowNode node, boolean branch);
|
||||
|
||||
/** Gets a node guarded by this guard. */
|
||||
final ExprNode getAGuardedNode() {
|
||||
exists(EssaDefinition def, ControlFlowNode node, boolean branch |
|
||||
AdjacentUses::useOfDef(def, node) and
|
||||
this.checks(node, branch) and
|
||||
AdjacentUses::useOfDef(def, result.asCfgNode()) and
|
||||
this.controlsBlock(result.asCfgNode().getBasicBlock(), branch)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Algebraic datatype for tracking data content associated with values.
|
||||
* Content can be collection elements or object attributes.
|
||||
|
||||
@@ -11,6 +11,7 @@ import DataFlowPublic
|
||||
private import DataFlowPrivate
|
||||
private import semmle.python.internal.CachedStages
|
||||
private import semmle.python.internal.Awaited
|
||||
private import semmle.python.dataflow.new.internal.ImportStar
|
||||
|
||||
/**
|
||||
* A data flow node that is a source of local flow. This includes things like
|
||||
@@ -39,6 +40,22 @@ class LocalSourceNode extends Node {
|
||||
this instanceof ExprNode and
|
||||
not simpleLocalFlowStepForTypetracking(_, this)
|
||||
or
|
||||
// For `from foo import *; foo_function()`, we want to let the variables we think
|
||||
// could originate in `foo` (such as `foo_function`) to be available in the API
|
||||
// graph. This requires them to be local sources. They would not be from the code
|
||||
// just above, since the CFG node has flow going into it from its corresponding
|
||||
// `GlobalSsaVariable`. (a different work-around is to change API graphs to not rely
|
||||
// as heavily on LocalSourceNode; I initially tried this, but it relied on a lot of
|
||||
// copy-pasted code, and it requires some non-trivial deprecation for downgrading
|
||||
// the result type of `.asSource()` to DataFlow::Node, so we've opted for this
|
||||
// approach instead).
|
||||
//
|
||||
// Note: This is only needed at the module level -- uses inside functions appear as
|
||||
// LocalSourceNodes as we expect.
|
||||
//
|
||||
// TODO: When rewriting SSA, we should be able to remove this workaround
|
||||
ImportStar::namePossiblyDefinedInImportStar(this.(ExprNode).getNode(), _, any(Module m))
|
||||
or
|
||||
// We include all module variable nodes, as these act as stepping stones between writes and
|
||||
// reads of global variables. Without them, type tracking based on `LocalSourceNode`s would be
|
||||
// unable to track across global variables.
|
||||
|
||||
@@ -116,33 +116,6 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited
|
||||
* when the flow state is `state`.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
|
||||
none()
|
||||
}
|
||||
|
||||
deprecated final override predicate isBarrierGuard(
|
||||
DataFlow::BarrierGuard guard, DataFlow::FlowState state
|
||||
) {
|
||||
this.isSanitizerGuard(guard, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
*/
|
||||
|
||||
@@ -116,33 +116,6 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited
|
||||
* when the flow state is `state`.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
|
||||
none()
|
||||
}
|
||||
|
||||
deprecated final override predicate isBarrierGuard(
|
||||
DataFlow::BarrierGuard guard, DataFlow::FlowState state
|
||||
) {
|
||||
this.isSanitizerGuard(guard, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
*/
|
||||
|
||||
@@ -116,33 +116,6 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited
|
||||
* when the flow state is `state`.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
|
||||
none()
|
||||
}
|
||||
|
||||
deprecated final override predicate isBarrierGuard(
|
||||
DataFlow::BarrierGuard guard, DataFlow::FlowState state
|
||||
) {
|
||||
this.isSanitizerGuard(guard, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
*/
|
||||
|
||||
@@ -116,33 +116,6 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited
|
||||
* when the flow state is `state`.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
|
||||
none()
|
||||
}
|
||||
|
||||
deprecated final override predicate isBarrierGuard(
|
||||
DataFlow::BarrierGuard guard, DataFlow::FlowState state
|
||||
) {
|
||||
this.isSanitizerGuard(guard, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
*/
|
||||
|
||||
33
python/ql/lib/semmle/python/frameworks/Joblib.qll
Normal file
33
python/ql/lib/semmle/python/frameworks/Joblib.qll
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `joblib` PyPI package.
|
||||
* See https://pypi.org/project/joblib/.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
* Provides models for the `joblib` PyPI package.
|
||||
* See https://pypi.org/project/joblib/.
|
||||
*/
|
||||
private module Joblib {
|
||||
/**
|
||||
* A call to `joblib.load`
|
||||
* See https://pypi.org/project/joblib/
|
||||
*/
|
||||
private class JoblibLoadCall extends Decoding::Range, DataFlow::CallCfgNode {
|
||||
JoblibLoadCall() { this = API::moduleImport("joblib").getMember("load").getACall() }
|
||||
|
||||
override predicate mayExecuteInput() { any() }
|
||||
|
||||
override DataFlow::Node getAnInput() {
|
||||
result in [this.getArg(0), this.getArgByName("filename")]
|
||||
}
|
||||
|
||||
override DataFlow::Node getOutput() { result = this }
|
||||
|
||||
override string getFormat() { result = "joblib" }
|
||||
}
|
||||
}
|
||||
42
python/ql/lib/semmle/python/frameworks/Numpy.qll
Normal file
42
python/ql/lib/semmle/python/frameworks/Numpy.qll
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `numpy` PyPI package.
|
||||
* See https://pypi.org/project/numpy/.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
* Provides models for the `numpy` PyPI package.
|
||||
* See https://pypi.org/project/numpy/.
|
||||
*/
|
||||
private module Numpy {
|
||||
/**
|
||||
* A call to `numpy.load`
|
||||
* See https://numpy.org/doc/stable/reference/generated/numpy.load.html
|
||||
*/
|
||||
private class NumpyLoadCall extends Decoding::Range, API::CallNode {
|
||||
NumpyLoadCall() { this = API::moduleImport("numpy").getMember("load").getACall() }
|
||||
|
||||
override predicate mayExecuteInput() {
|
||||
this.getParameter(2, "allow_pickle")
|
||||
.getAValueReachingSink()
|
||||
.asExpr()
|
||||
.(ImmutableLiteral)
|
||||
.booleanValue() = true
|
||||
}
|
||||
|
||||
override DataFlow::Node getAnInput() { result = this.getParameter(0, "filename").asSink() }
|
||||
|
||||
override DataFlow::Node getOutput() { result = this }
|
||||
|
||||
override string getFormat() {
|
||||
result = "numpy"
|
||||
or
|
||||
this.mayExecuteInput() and result = "pickle"
|
||||
}
|
||||
}
|
||||
}
|
||||
37
python/ql/lib/semmle/python/frameworks/Pandas.qll
Normal file
37
python/ql/lib/semmle/python/frameworks/Pandas.qll
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `pandas` PyPI package.
|
||||
* See https://pypi.org/project/pandas/.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
* Provides models for the `pandas` PyPI package.
|
||||
* See https://pypi.org/project/pandas/.
|
||||
*/
|
||||
private module Pandas {
|
||||
/**
|
||||
* A call to `pandas.read_pickle`
|
||||
* See https://pypi.org/project/pandas/
|
||||
* See https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_pickle.html
|
||||
*/
|
||||
private class PandasReadPickleCall extends Decoding::Range, DataFlow::CallCfgNode {
|
||||
PandasReadPickleCall() {
|
||||
this = API::moduleImport("pandas").getMember("read_pickle").getACall()
|
||||
}
|
||||
|
||||
override predicate mayExecuteInput() { any() }
|
||||
|
||||
override DataFlow::Node getAnInput() {
|
||||
result in [this.getArg(0), this.getArgByName("filepath_or_buffer")]
|
||||
}
|
||||
|
||||
override DataFlow::Node getOutput() { result = this }
|
||||
|
||||
override string getFormat() { result = "pickle" }
|
||||
}
|
||||
}
|
||||
@@ -73,9 +73,6 @@ private module NotExposed {
|
||||
result = "moduleImport(\"" + fullyQualified.replaceAll(".", "\").getMember(\"") + "\")"
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for fullyQualifiedToApiGraphPath */
|
||||
deprecated predicate fullyQualifiedToAPIGraphPath = fullyQualifiedToApiGraphPath/1;
|
||||
|
||||
bindingset[this]
|
||||
abstract class FindSubclassesSpec extends string {
|
||||
abstract API::Node getAlreadyModeledClass();
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import semmle.python.dataflow.Implementation
|
||||
|
||||
deprecated module TaintTrackingPaths {
|
||||
predicate edge(TaintTrackingNode src, TaintTrackingNode dest, string label) {
|
||||
exists(TaintTrackingNode source, TaintTrackingNode sink |
|
||||
source.getConfiguration().hasFlowPath(source, sink) and
|
||||
source.getASuccessor*() = src and
|
||||
src.getASuccessor(label) = dest and
|
||||
dest.getASuccessor*() = sink
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
deprecated query predicate edges(TaintTrackingNode fromnode, TaintTrackingNode tonode) {
|
||||
TaintTrackingPaths::edge(fromnode, tonode, _)
|
||||
}
|
||||
@@ -31,13 +31,6 @@ module CodeInjection {
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Sanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "code injection" vulnerabilities.
|
||||
*/
|
||||
abstract deprecated class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
*/
|
||||
|
||||
@@ -24,10 +24,6 @@ deprecated class Configuration extends TaintTracking::Configuration {
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
private module CodeInjectionConfig implements DataFlow::ConfigSig {
|
||||
|
||||
@@ -31,13 +31,6 @@ module CommandInjection {
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Sanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "command injection" vulnerabilities.
|
||||
*/
|
||||
abstract deprecated class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
*/
|
||||
|
||||
@@ -24,10 +24,6 @@ deprecated class Configuration extends TaintTracking::Configuration {
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -41,20 +41,6 @@ module LdapInjection {
|
||||
*/
|
||||
abstract class FilterSanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `DnSanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "ldap injection" vulnerabilities.
|
||||
*/
|
||||
abstract deprecated class DnSanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `FilterSanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "ldap injection" vulnerabilities.
|
||||
*/
|
||||
abstract deprecated class FilterSanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
*/
|
||||
|
||||
@@ -27,10 +27,6 @@ deprecated class DnConfiguration extends TaintTracking::Configuration {
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof DnSink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof DnSanitizer }
|
||||
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof DnSanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
private module LdapInjectionDnConfig implements DataFlow::ConfigSig {
|
||||
@@ -58,10 +54,6 @@ deprecated class FilterConfiguration extends TaintTracking::Configuration {
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof FilterSink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof FilterSanitizer }
|
||||
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof FilterSanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
private module LdapInjectionFilterConfig implements DataFlow::ConfigSig {
|
||||
|
||||
@@ -31,13 +31,6 @@ module LogInjection {
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Sanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "log injection" vulnerabilities.
|
||||
*/
|
||||
abstract deprecated class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
*/
|
||||
|
||||
@@ -24,10 +24,6 @@ deprecated class Configuration extends TaintTracking::Configuration {
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
private module LogInjectionConfig implements DataFlow::ConfigSig {
|
||||
|
||||
@@ -42,13 +42,6 @@ module PathInjection {
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Sanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "path injection" vulnerabilities.
|
||||
*/
|
||||
abstract deprecated class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
*/
|
||||
|
||||
@@ -53,10 +53,6 @@ deprecated class Configuration extends TaintTracking::Configuration {
|
||||
state instanceof NormalizedUnchecked
|
||||
}
|
||||
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(
|
||||
DataFlow::Node nodeFrom, DataFlow::FlowState stateFrom, DataFlow::Node nodeTo,
|
||||
DataFlow::FlowState stateTo
|
||||
|
||||
@@ -46,13 +46,6 @@ module PolynomialReDoS {
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Sanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "polynomial regular expression denial of service (ReDoS)" vulnerabilities.
|
||||
*/
|
||||
abstract deprecated class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
*/
|
||||
|
||||
@@ -24,10 +24,6 @@ deprecated class Configuration extends TaintTracking::Configuration {
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
private module PolynomialReDoSConfig implements DataFlow::ConfigSig {
|
||||
|
||||
@@ -32,13 +32,6 @@ module ReflectedXss {
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Sanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "reflected server-side cross-site scripting" vulnerabilities.
|
||||
*/
|
||||
abstract deprecated class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
*/
|
||||
|
||||
@@ -24,10 +24,6 @@ deprecated class Configuration extends TaintTracking::Configuration {
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
private module ReflectedXssConfig implements DataFlow::ConfigSig {
|
||||
|
||||
@@ -39,13 +39,6 @@ module RegexInjection {
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Sanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "regular expression injection" vulnerabilities.
|
||||
*/
|
||||
abstract deprecated class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
*/
|
||||
|
||||
@@ -25,10 +25,6 @@ deprecated class Configuration extends TaintTracking::Configuration {
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
private module RegexInjectionConfig implements DataFlow::ConfigSig {
|
||||
|
||||
@@ -43,13 +43,6 @@ module ServerSideRequestForgery {
|
||||
*/
|
||||
abstract class FullUrlControlSanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Sanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "Server-side request forgery" vulnerabilities.
|
||||
*/
|
||||
abstract deprecated class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
*/
|
||||
|
||||
@@ -35,10 +35,6 @@ deprecated class FullServerSideRequestForgeryConfiguration extends TaintTracking
|
||||
or
|
||||
node instanceof FullUrlControlSanitizer
|
||||
}
|
||||
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,10 +89,6 @@ deprecated class PartialServerSideRequestForgeryConfiguration extends TaintTrack
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,13 +31,6 @@ module SqlInjection {
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Sanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "SQL injection" vulnerabilities.
|
||||
*/
|
||||
abstract deprecated class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
*/
|
||||
|
||||
@@ -24,10 +24,6 @@ deprecated class Configuration extends TaintTracking::Configuration {
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
private module SqlInjectionConfig implements DataFlow::ConfigSig {
|
||||
|
||||
@@ -31,13 +31,6 @@ module StackTraceExposure {
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Sanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "stack trace exposure" vulnerabilities.
|
||||
*/
|
||||
abstract deprecated class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of exception info, considered as a flow source.
|
||||
*/
|
||||
|
||||
@@ -25,10 +25,6 @@ deprecated class Configuration extends TaintTracking::Configuration {
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
|
||||
// A stack trace is accessible as the `__traceback__` attribute of a caught exception.
|
||||
// see https://docs.python.org/3/reference/datamodel.html#traceback-objects
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
|
||||
@@ -31,13 +31,6 @@ module UnsafeDeserialization {
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Sanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "code execution from deserialization" vulnerabilities.
|
||||
*/
|
||||
abstract deprecated class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
*/
|
||||
|
||||
@@ -24,10 +24,6 @@ deprecated class Configuration extends TaintTracking::Configuration {
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
private module UnsafeDeserializationConfig implements DataFlow::ConfigSig {
|
||||
|
||||
@@ -31,13 +31,6 @@ module UrlRedirect {
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Sanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "URL redirection" vulnerabilities.
|
||||
*/
|
||||
abstract deprecated class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
*/
|
||||
|
||||
@@ -24,10 +24,6 @@ deprecated class Configuration extends TaintTracking::Configuration {
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
private module UrlRedirectConfig implements DataFlow::ConfigSig {
|
||||
|
||||
@@ -29,13 +29,6 @@ module XpathInjection {
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Sanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "XPath injection" vulnerabilities.
|
||||
*/
|
||||
abstract deprecated class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
*/
|
||||
|
||||
@@ -24,10 +24,6 @@ deprecated class Configuration extends TaintTracking::Configuration {
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
private module XpathInjectionConfig implements DataFlow::ConfigSig {
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
/** DEPRECATED. Import `semmle.python.security.regexp.ExponentialBackTracking` instead. */
|
||||
|
||||
deprecated import semmle.python.security.regexp.ExponentialBackTracking as Dep
|
||||
import Dep
|
||||
@@ -1,4 +0,0 @@
|
||||
/** DEPRECATED. Import `semmle.python.security.regexp.NfaUtils` instead. */
|
||||
|
||||
deprecated import semmle.python.security.regexp.NfaUtils as Dep
|
||||
import Dep
|
||||
@@ -1,4 +0,0 @@
|
||||
/** DEPRECATED. Import `semmle.python.security.regexp.SuperlinearBackTracking` instead. */
|
||||
|
||||
deprecated import semmle.python.security.regexp.SuperlinearBackTracking as Dep
|
||||
import Dep
|
||||
@@ -1,124 +0,0 @@
|
||||
import python
|
||||
private import Common
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
|
||||
/** An extensible kind of taint representing any kind of string. */
|
||||
abstract deprecated class StringKind extends TaintKind {
|
||||
bindingset[this]
|
||||
StringKind() { this = this }
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
name in [
|
||||
"capitalize", "casefold", "center", "expandtabs", "format", "format_map", "ljust", "lstrip",
|
||||
"lower", "replace", "rjust", "rstrip", "strip", "swapcase", "title", "upper", "zfill",
|
||||
/* encode/decode is technically not correct, but close enough */
|
||||
"encode", "decode"
|
||||
] and
|
||||
result = this
|
||||
or
|
||||
name in ["partition", "rpartition", "rsplit", "split", "splitlines"] and
|
||||
result.(SequenceKind).getItem() = this
|
||||
}
|
||||
|
||||
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
result = this and
|
||||
(
|
||||
slice(fromnode, tonode) or
|
||||
tonode.(BinaryExprNode).getAnOperand() = fromnode or
|
||||
os_path_join(fromnode, tonode) or
|
||||
str_format(fromnode, tonode) or
|
||||
encode_decode(fromnode, tonode) or
|
||||
to_str(fromnode, tonode) or
|
||||
f_string(fromnode, tonode)
|
||||
)
|
||||
or
|
||||
result = this and copy_call(fromnode, tonode)
|
||||
}
|
||||
|
||||
override ClassValue getType() {
|
||||
result = Value::named("bytes") or
|
||||
result = Value::named("str") or
|
||||
result = Value::named("unicode")
|
||||
}
|
||||
}
|
||||
|
||||
deprecated private class StringEqualitySanitizer extends Sanitizer {
|
||||
StringEqualitySanitizer() { this = "string equality sanitizer" }
|
||||
|
||||
/* The test `if untrusted == "KNOWN_VALUE":` sanitizes `untrusted` on its `true` edge. */
|
||||
override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) {
|
||||
taint instanceof StringKind and
|
||||
exists(ControlFlowNode const, Cmpop op | const.getNode() instanceof StrConst |
|
||||
(
|
||||
test.getTest().(CompareNode).operands(const, op, _)
|
||||
or
|
||||
test.getTest().(CompareNode).operands(_, op, const)
|
||||
) and
|
||||
(
|
||||
op instanceof Eq and test.getSense() = true
|
||||
or
|
||||
op instanceof NotEq and test.getSense() = false
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** tonode = ....format(fromnode) */
|
||||
deprecated private predicate str_format(ControlFlowNode fromnode, CallNode tonode) {
|
||||
tonode.getFunction().(AttrNode).getName() = "format" and
|
||||
tonode.getAnArg() = fromnode
|
||||
}
|
||||
|
||||
/** tonode = codec.[en|de]code(fromnode) */
|
||||
deprecated private predicate encode_decode(ControlFlowNode fromnode, CallNode tonode) {
|
||||
exists(FunctionObject func, string name |
|
||||
not func.getFunction().isMethod() and
|
||||
func.getACall() = tonode and
|
||||
tonode.getAnArg() = fromnode and
|
||||
func.getName() = name
|
||||
|
|
||||
name = "encode" or
|
||||
name = "decode" or
|
||||
name = "decodestring"
|
||||
)
|
||||
}
|
||||
|
||||
/** tonode = str(fromnode) */
|
||||
deprecated private predicate to_str(ControlFlowNode fromnode, CallNode tonode) {
|
||||
tonode.getAnArg() = fromnode and
|
||||
(
|
||||
tonode = ClassValue::bytes().getACall()
|
||||
or
|
||||
tonode = ClassValue::unicode().getACall()
|
||||
)
|
||||
}
|
||||
|
||||
/** tonode = fromnode[:] */
|
||||
deprecated private predicate slice(ControlFlowNode fromnode, SubscriptNode tonode) {
|
||||
exists(Slice all |
|
||||
all = tonode.getIndex().getNode() and
|
||||
not exists(all.getStart()) and
|
||||
not exists(all.getStop()) and
|
||||
tonode.getObject() = fromnode
|
||||
)
|
||||
}
|
||||
|
||||
/** tonode = os.path.join(..., fromnode, ...) */
|
||||
deprecated private predicate os_path_join(ControlFlowNode fromnode, CallNode tonode) {
|
||||
tonode = Value::named("os.path.join").getACall() and
|
||||
tonode.getAnArg() = fromnode
|
||||
}
|
||||
|
||||
/** tonode = f"... {fromnode} ..." */
|
||||
deprecated private predicate f_string(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
tonode.getNode().(Fstring).getAValue() = fromnode.getNode()
|
||||
}
|
||||
|
||||
/**
|
||||
* A kind of "taint", representing a dictionary mapping str->"taint"
|
||||
*
|
||||
* DEPRECATED: Use `ExternalStringDictKind` instead.
|
||||
*/
|
||||
deprecated class StringDictKind extends DictKind {
|
||||
StringDictKind() { this.getValue() instanceof StringKind }
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import python
|
||||
|
||||
/** A call that returns a copy (or similar) of the argument */
|
||||
deprecated predicate copy_call(ControlFlowNode fromnode, CallNode tonode) {
|
||||
tonode.getFunction().(AttrNode).getObject("copy") = fromnode
|
||||
or
|
||||
exists(ModuleValue copy, string name | name = "copy" or name = "deepcopy" |
|
||||
copy.attr(name).(FunctionValue).getACall() = tonode and
|
||||
tonode.getArg(0) = fromnode
|
||||
)
|
||||
or
|
||||
tonode.getFunction().pointsTo(Value::named("reversed")) and
|
||||
tonode.getArg(0) = fromnode
|
||||
}
|
||||
@@ -1,318 +0,0 @@
|
||||
import python
|
||||
import Basic
|
||||
private import Common
|
||||
|
||||
/**
|
||||
* An extensible kind of taint representing an externally controlled string.
|
||||
*/
|
||||
abstract deprecated class ExternalStringKind extends StringKind {
|
||||
bindingset[this]
|
||||
ExternalStringKind() { this = this }
|
||||
|
||||
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
result = StringKind.super.getTaintForFlowStep(fromnode, tonode)
|
||||
or
|
||||
tonode.(SequenceNode).getElement(_) = fromnode and
|
||||
result.(ExternalStringSequenceKind).getItem() = this
|
||||
or
|
||||
json_load(fromnode, tonode) and result.(ExternalJsonKind).getValue() = this
|
||||
or
|
||||
tonode.(DictNode).getAValue() = fromnode and result.(ExternalStringDictKind).getValue() = this
|
||||
or
|
||||
urlsplit(fromnode, tonode) and result.(ExternalUrlSplitResult).getItem() = this
|
||||
or
|
||||
urlparse(fromnode, tonode) and result.(ExternalUrlParseResult).getItem() = this
|
||||
or
|
||||
parse_qs(fromnode, tonode) and result.(ExternalStringDictKind).getValue() = this
|
||||
or
|
||||
parse_qsl(fromnode, tonode) and result.(SequenceKind).getItem().(SequenceKind).getItem() = this
|
||||
}
|
||||
}
|
||||
|
||||
/** A kind of "taint", representing a sequence, with a "taint" member */
|
||||
deprecated class ExternalStringSequenceKind extends SequenceKind {
|
||||
ExternalStringSequenceKind() { this.getItem() instanceof ExternalStringKind }
|
||||
}
|
||||
|
||||
/**
|
||||
* An hierarchical dictionary or list where the entire structure is externally controlled
|
||||
* This is typically a parsed JSON object.
|
||||
*/
|
||||
deprecated class ExternalJsonKind extends TaintKind {
|
||||
ExternalJsonKind() { this = "json[" + any(ExternalStringKind key) + "]" }
|
||||
|
||||
/** Gets the taint kind for item in this sequence */
|
||||
TaintKind getValue() {
|
||||
this = "json[" + result + "]"
|
||||
or
|
||||
result = this
|
||||
}
|
||||
|
||||
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
this.taints(fromnode) and
|
||||
json_subscript_taint(tonode, fromnode, this, result)
|
||||
or
|
||||
result = this and copy_call(fromnode, tonode)
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
name = "get" and result = this.getValue()
|
||||
}
|
||||
}
|
||||
|
||||
/** A kind of "taint", representing a dictionary mapping keys to tainted strings. */
|
||||
deprecated class ExternalStringDictKind extends DictKind {
|
||||
ExternalStringDictKind() { this.getValue() instanceof ExternalStringKind }
|
||||
}
|
||||
|
||||
/**
|
||||
* A kind of "taint", representing a dictionary mapping keys to sequences of
|
||||
* tainted strings.
|
||||
*/
|
||||
deprecated class ExternalStringSequenceDictKind extends DictKind {
|
||||
ExternalStringSequenceDictKind() { this.getValue() instanceof ExternalStringSequenceKind }
|
||||
}
|
||||
|
||||
/** TaintKind for the result of `urlsplit(tainted_string)` */
|
||||
deprecated class ExternalUrlSplitResult extends ExternalStringSequenceKind {
|
||||
// https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlsplit
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
result = super.getTaintOfAttribute(name)
|
||||
or
|
||||
name in [
|
||||
// namedtuple field names
|
||||
"scheme", "netloc", "path", "query", "fragment",
|
||||
// class methods
|
||||
"password", "username", "hostname",
|
||||
] and
|
||||
result instanceof ExternalStringKind
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
result = super.getTaintOfMethodResult(name)
|
||||
or
|
||||
name = "geturl" and
|
||||
result instanceof ExternalStringKind
|
||||
}
|
||||
}
|
||||
|
||||
/** TaintKind for the result of `urlparse(tainted_string)` */
|
||||
deprecated class ExternalUrlParseResult extends ExternalStringSequenceKind {
|
||||
// https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlparse
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
result = super.getTaintOfAttribute(name)
|
||||
or
|
||||
name in [
|
||||
// namedtuple field names
|
||||
"scheme", "netloc", "path", "params", "query", "fragment",
|
||||
// class methods
|
||||
"username", "password", "hostname",
|
||||
] and
|
||||
result instanceof ExternalStringKind
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
result = super.getTaintOfMethodResult(name)
|
||||
or
|
||||
name = "geturl" and
|
||||
result instanceof ExternalStringKind
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper for getTaintForStep() */
|
||||
pragma[noinline]
|
||||
deprecated private predicate json_subscript_taint(
|
||||
SubscriptNode sub, ControlFlowNode obj, ExternalJsonKind seq, TaintKind key
|
||||
) {
|
||||
sub.isLoad() and
|
||||
sub.getObject() = obj and
|
||||
key = seq.getValue()
|
||||
}
|
||||
|
||||
deprecated private predicate json_load(ControlFlowNode fromnode, CallNode tonode) {
|
||||
tonode = Value::named("json.loads").getACall() and
|
||||
tonode.getArg(0) = fromnode
|
||||
}
|
||||
|
||||
deprecated private predicate urlsplit(ControlFlowNode fromnode, CallNode tonode) {
|
||||
// This could be implemented as `exists(FunctionValue` without the explicit six part,
|
||||
// but then our tests will need to import +100 modules, so for now this slightly
|
||||
// altered version gets to live on.
|
||||
exists(Value urlsplit |
|
||||
(
|
||||
urlsplit = Value::named("six.moves.urllib.parse.urlsplit")
|
||||
or
|
||||
// Python 2
|
||||
urlsplit = Value::named("urlparse.urlsplit")
|
||||
or
|
||||
// Python 3
|
||||
urlsplit = Value::named("urllib.parse.urlsplit")
|
||||
) and
|
||||
tonode = urlsplit.getACall() and
|
||||
tonode.getArg(0) = fromnode
|
||||
)
|
||||
}
|
||||
|
||||
deprecated private predicate urlparse(ControlFlowNode fromnode, CallNode tonode) {
|
||||
// This could be implemented as `exists(FunctionValue` without the explicit six part,
|
||||
// but then our tests will need to import +100 modules, so for now this slightly
|
||||
// altered version gets to live on.
|
||||
exists(Value urlparse |
|
||||
(
|
||||
urlparse = Value::named("six.moves.urllib.parse.urlparse")
|
||||
or
|
||||
// Python 2
|
||||
urlparse = Value::named("urlparse.urlparse")
|
||||
or
|
||||
// Python 3
|
||||
urlparse = Value::named("urllib.parse.urlparse")
|
||||
) and
|
||||
tonode = urlparse.getACall() and
|
||||
tonode.getArg(0) = fromnode
|
||||
)
|
||||
}
|
||||
|
||||
deprecated private predicate parse_qs(ControlFlowNode fromnode, CallNode tonode) {
|
||||
// This could be implemented as `exists(FunctionValue` without the explicit six part,
|
||||
// but then our tests will need to import +100 modules, so for now this slightly
|
||||
// altered version gets to live on.
|
||||
exists(Value parse_qs |
|
||||
(
|
||||
parse_qs = Value::named("six.moves.urllib.parse.parse_qs")
|
||||
or
|
||||
// Python 2
|
||||
parse_qs = Value::named("urlparse.parse_qs")
|
||||
or
|
||||
// Python 2 deprecated version of `urlparse.parse_qs`
|
||||
parse_qs = Value::named("cgi.parse_qs")
|
||||
or
|
||||
// Python 3
|
||||
parse_qs = Value::named("urllib.parse.parse_qs")
|
||||
) and
|
||||
tonode = parse_qs.getACall() and
|
||||
(
|
||||
tonode.getArg(0) = fromnode
|
||||
or
|
||||
tonode.getArgByName("qs") = fromnode
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
deprecated private predicate parse_qsl(ControlFlowNode fromnode, CallNode tonode) {
|
||||
// This could be implemented as `exists(FunctionValue` without the explicit six part,
|
||||
// but then our tests will need to import +100 modules, so for now this slightly
|
||||
// altered version gets to live on.
|
||||
exists(Value parse_qsl |
|
||||
(
|
||||
parse_qsl = Value::named("six.moves.urllib.parse.parse_qsl")
|
||||
or
|
||||
// Python 2
|
||||
parse_qsl = Value::named("urlparse.parse_qsl")
|
||||
or
|
||||
// Python 2 deprecated version of `urlparse.parse_qsl`
|
||||
parse_qsl = Value::named("cgi.parse_qsl")
|
||||
or
|
||||
// Python 3
|
||||
parse_qsl = Value::named("urllib.parse.parse_qsl")
|
||||
) and
|
||||
tonode = parse_qsl.getACall() and
|
||||
(
|
||||
tonode.getArg(0) = fromnode
|
||||
or
|
||||
tonode.getArgByName("qs") = fromnode
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** A kind of "taint", representing an open file-like object from an external source. */
|
||||
deprecated class ExternalFileObject extends TaintKind {
|
||||
ExternalStringKind valueKind;
|
||||
|
||||
ExternalFileObject() { this = "file[" + valueKind + "]" }
|
||||
|
||||
/** Gets the taint kind for the contents of this file */
|
||||
TaintKind getValue() { result = valueKind }
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
name in ["read", "readline"] and result = this.getValue()
|
||||
or
|
||||
name = "readlines" and result.(SequenceKind).getItem() = this.getValue()
|
||||
}
|
||||
|
||||
override TaintKind getTaintForIteration() { result = this.getValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporary sanitizer for the tainted result from `urlsplit` and `urlparse`. Can be used to reduce FPs until
|
||||
* we have better support for namedtuples.
|
||||
*
|
||||
* Will clear **all** taint on a test of the kind. That is, on the true edge of any matching test,
|
||||
* all fields/indexes will be cleared of taint.
|
||||
*
|
||||
* Handles:
|
||||
* - `if splitres.netloc == "KNOWN_VALUE"`
|
||||
* - `if splitres[0] == "KNOWN_VALUE"`
|
||||
*/
|
||||
deprecated class UrlsplitUrlparseTempSanitizer extends Sanitizer {
|
||||
// TODO: remove this once we have better support for named tuples
|
||||
UrlsplitUrlparseTempSanitizer() { this = "UrlsplitUrlparseTempSanitizer" }
|
||||
|
||||
override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) {
|
||||
(
|
||||
taint instanceof ExternalUrlSplitResult
|
||||
or
|
||||
taint instanceof ExternalUrlParseResult
|
||||
) and
|
||||
exists(ControlFlowNode full_use |
|
||||
full_use.(SubscriptNode).getObject() = test.getInput().getAUse()
|
||||
or
|
||||
full_use.(AttrNode).getObject() = test.getInput().getAUse()
|
||||
|
|
||||
this.clears_taint(full_use, test.getTest(), test.getSense())
|
||||
)
|
||||
}
|
||||
|
||||
private predicate clears_taint(ControlFlowNode tainted, ControlFlowNode test, boolean sense) {
|
||||
this.test_equality_with_const(test, tainted, sense)
|
||||
or
|
||||
this.test_in_const_seq(test, tainted, sense)
|
||||
or
|
||||
test.(UnaryExprNode).getNode().getOp() instanceof Not and
|
||||
exists(ControlFlowNode nested_test |
|
||||
nested_test = test.(UnaryExprNode).getOperand() and
|
||||
this.clears_taint(tainted, nested_test, sense.booleanNot())
|
||||
)
|
||||
}
|
||||
|
||||
/** holds for `== "KNOWN_VALUE"` on `true` edge, and `!= "KNOWN_VALUE"` on `false` edge */
|
||||
private predicate test_equality_with_const(CompareNode cmp, ControlFlowNode tainted, boolean sense) {
|
||||
exists(ControlFlowNode const, Cmpop op | const.getNode() instanceof StrConst |
|
||||
(
|
||||
cmp.operands(const, op, tainted)
|
||||
or
|
||||
cmp.operands(tainted, op, const)
|
||||
) and
|
||||
(
|
||||
op instanceof Eq and sense = true
|
||||
or
|
||||
op instanceof NotEq and sense = false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** holds for `in ["KNOWN_VALUE", ...]` on `true` edge, and `not in ["KNOWN_VALUE", ...]` on `false` edge */
|
||||
private predicate test_in_const_seq(CompareNode cmp, ControlFlowNode tainted, boolean sense) {
|
||||
exists(SequenceNode const_seq, Cmpop op |
|
||||
forall(ControlFlowNode elem | elem = const_seq.getAnElement() |
|
||||
elem.getNode() instanceof StrConst
|
||||
)
|
||||
|
|
||||
cmp.operands(tainted, op, const_seq) and
|
||||
(
|
||||
op instanceof In and sense = true
|
||||
or
|
||||
op instanceof NotIn and sense = false
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import python
|
||||
import External
|
||||
|
||||
/**
|
||||
* A kind of taint representing an externally controlled string.
|
||||
* This class is a simple sub-class of `ExternalStringKind`.
|
||||
*/
|
||||
deprecated class UntrustedStringKind extends ExternalStringKind {
|
||||
UntrustedStringKind() { this = "externally controlled string" }
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
import python
|
||||
import semmle.python.dataflow.Implementation
|
||||
import semmle.python.security.strings.External
|
||||
import HttpConstants
|
||||
|
||||
/** Generic taint source from a http request */
|
||||
abstract deprecated class HttpRequestTaintSource extends TaintSource { }
|
||||
|
||||
/**
|
||||
* Taint kind representing the WSGI environment.
|
||||
* As specified in PEP 3333. https://www.python.org/dev/peps/pep-3333/#environ-variables
|
||||
*/
|
||||
deprecated class WsgiEnvironment extends TaintKind {
|
||||
WsgiEnvironment() { this = "wsgi.environment" }
|
||||
|
||||
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
result = this and Implementation::copyCall(fromnode, tonode)
|
||||
or
|
||||
result = this and
|
||||
tonode.(CallNode).getFunction().pointsTo(ClassValue::dict()) and
|
||||
tonode.(CallNode).getArg(0) = fromnode
|
||||
or
|
||||
exists(Value key, string text |
|
||||
tonode.(CallNode).getFunction().(AttrNode).getObject("get") = fromnode and
|
||||
tonode.(CallNode).getArg(0).pointsTo(key)
|
||||
or
|
||||
tonode.(SubscriptNode).getObject() = fromnode and
|
||||
tonode.isLoad() and
|
||||
tonode.(SubscriptNode).getIndex().pointsTo(key)
|
||||
|
|
||||
key = Value::forString(text) and
|
||||
result instanceof ExternalStringKind and
|
||||
(
|
||||
text = "QUERY_STRING" or
|
||||
text = "PATH_INFO" or
|
||||
text.matches("HTTP\\_%")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A standard morsel object from a HTTP request, a value in a cookie,
|
||||
* typically an instance of `http.cookies.Morsel`
|
||||
*/
|
||||
deprecated class UntrustedMorsel extends TaintKind {
|
||||
UntrustedMorsel() { this = "http.Morsel" }
|
||||
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
result instanceof ExternalStringKind and
|
||||
name = "value"
|
||||
}
|
||||
}
|
||||
|
||||
/** A standard cookie object from a HTTP request, typically an instance of `http.cookies.SimpleCookie` */
|
||||
deprecated class UntrustedCookie extends TaintKind {
|
||||
UntrustedCookie() { this = "http.Cookie" }
|
||||
|
||||
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
tonode.(SubscriptNode).getObject() = fromnode and
|
||||
result instanceof UntrustedMorsel
|
||||
}
|
||||
}
|
||||
|
||||
abstract deprecated class CookieOperation extends @py_flow_node {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
|
||||
abstract ControlFlowNode getKey();
|
||||
|
||||
abstract ControlFlowNode getValue();
|
||||
}
|
||||
|
||||
abstract deprecated class CookieGet extends CookieOperation { }
|
||||
|
||||
abstract deprecated class CookieSet extends CookieOperation { }
|
||||
|
||||
/** Generic taint sink in a http response */
|
||||
abstract deprecated class HttpResponseTaintSink extends TaintSink {
|
||||
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
|
||||
}
|
||||
|
||||
abstract deprecated class HttpRedirectTaintSink extends TaintSink {
|
||||
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
|
||||
}
|
||||
|
||||
deprecated module Client {
|
||||
// TODO: user-input in other than URL:
|
||||
// - `data`, `json` for `requests.post`
|
||||
// - `body` for `HTTPConnection.request`
|
||||
// - headers?
|
||||
// TODO: Add more library support
|
||||
// - urllib3 https://github.com/urllib3/urllib3
|
||||
// - httpx https://github.com/encode/httpx
|
||||
/**
|
||||
* An outgoing http request
|
||||
*
|
||||
* For example:
|
||||
* conn = HTTPConnection('example.com')
|
||||
* conn.request('GET', '/path')
|
||||
*/
|
||||
abstract class HttpRequest extends ControlFlowNode {
|
||||
/**
|
||||
* Get any ControlFlowNode that is used to construct the final URL.
|
||||
*
|
||||
* In the HTTPConnection example, there is a result for both `'example.com'` and for `'/path'`.
|
||||
*/
|
||||
abstract ControlFlowNode getAUrlPart();
|
||||
|
||||
abstract string getMethodUpper();
|
||||
}
|
||||
|
||||
/** Taint sink for the URL-part of an outgoing http request */
|
||||
class HttpRequestUrlTaintSink extends TaintSink {
|
||||
HttpRequestUrlTaintSink() { this = any(HttpRequest r).getAUrlPart() }
|
||||
|
||||
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
/** Gets an HTTP verb, in upper case */
|
||||
deprecated string httpVerb() {
|
||||
result in ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"]
|
||||
}
|
||||
|
||||
/** Gets an HTTP verb, in lower case */
|
||||
deprecated string httpVerbLower() { result = httpVerb().toLowerCase() }
|
||||
@@ -1,10 +0,0 @@
|
||||
import semmle.python.web.django.Request
|
||||
import semmle.python.web.flask.Request
|
||||
import semmle.python.web.tornado.Request
|
||||
import semmle.python.web.pyramid.Request
|
||||
import semmle.python.web.twisted.Request
|
||||
import semmle.python.web.bottle.Request
|
||||
import semmle.python.web.turbogears.Request
|
||||
import semmle.python.web.falcon.Request
|
||||
import semmle.python.web.cherrypy.Request
|
||||
import semmle.python.web.stdlib.Request
|
||||
@@ -1,46 +0,0 @@
|
||||
import python
|
||||
import semmle.python.web.Http
|
||||
import semmle.python.types.Extensions
|
||||
|
||||
/** Gets the bottle module */
|
||||
deprecated ModuleValue theBottleModule() { result = Module::named("bottle") }
|
||||
|
||||
/** Gets the bottle.Bottle class */
|
||||
deprecated ClassValue theBottleClass() { result = theBottleModule().attr("Bottle") }
|
||||
|
||||
/**
|
||||
* Holds if `route` is routed to `func`
|
||||
* by decorating `func` with `app.route(route)` or `route(route)`
|
||||
*/
|
||||
deprecated predicate bottle_route(CallNode route_call, ControlFlowNode route, Function func) {
|
||||
exists(CallNode decorator_call, string name |
|
||||
route_call.getFunction().(AttrNode).getObject(name).pointsTo().getClass() = theBottleClass() or
|
||||
route_call.getFunction().pointsTo(theBottleModule().attr(name))
|
||||
|
|
||||
(name = "route" or name = httpVerbLower()) and
|
||||
decorator_call.getFunction() = route_call and
|
||||
route_call.getArg(0) = route and
|
||||
decorator_call.getArg(0).getNode().(FunctionExpr).getInnerScope() = func
|
||||
)
|
||||
}
|
||||
|
||||
deprecated class BottleRoute extends ControlFlowNode {
|
||||
BottleRoute() { bottle_route(this, _, _) }
|
||||
|
||||
string getUrl() {
|
||||
exists(StrConst url |
|
||||
bottle_route(this, url.getAFlowNode(), _) and
|
||||
result = url.getText()
|
||||
)
|
||||
}
|
||||
|
||||
Function getFunction() { bottle_route(this, _, result) }
|
||||
|
||||
Parameter getANamedArgument() {
|
||||
exists(string name, Function func |
|
||||
func = this.getFunction() and
|
||||
func.getArgByName(name) = result and
|
||||
this.getUrl().matches("%<" + name + ">%")
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
import python
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
import semmle.python.security.strings.External
|
||||
import semmle.python.web.Http
|
||||
import semmle.python.web.bottle.General
|
||||
|
||||
deprecated private Value theBottleRequestObject() { result = theBottleModule().attr("request") }
|
||||
|
||||
deprecated class BottleRequestKind extends TaintKind {
|
||||
BottleRequestKind() { this = "bottle.request" }
|
||||
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
result instanceof BottleFormsDict and
|
||||
(name = "cookies" or name = "query" or name = "form")
|
||||
or
|
||||
result instanceof ExternalStringKind and
|
||||
(name = "query_string" or name = "url_args")
|
||||
or
|
||||
result.(DictKind).getValue() instanceof FileUpload and
|
||||
name = "files"
|
||||
}
|
||||
}
|
||||
|
||||
deprecated private class RequestSource extends HttpRequestTaintSource {
|
||||
RequestSource() { this.(ControlFlowNode).pointsTo(theBottleRequestObject()) }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof BottleRequestKind }
|
||||
}
|
||||
|
||||
deprecated class BottleFormsDict extends TaintKind {
|
||||
BottleFormsDict() { this = "bottle.FormsDict" }
|
||||
|
||||
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
/* Cannot use `getTaintOfAttribute(name)` as it wouldn't bind `name` */
|
||||
exists(string name |
|
||||
fromnode = tonode.(AttrNode).getObject(name) and
|
||||
result instanceof ExternalStringKind
|
||||
|
|
||||
name != "get" and name != "getunicode" and name != "getall"
|
||||
)
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
(name = "get" or name = "getunicode") and
|
||||
result instanceof ExternalStringKind
|
||||
or
|
||||
name = "getall" and result.(SequenceKind).getItem() instanceof ExternalStringKind
|
||||
}
|
||||
}
|
||||
|
||||
deprecated class FileUpload extends TaintKind {
|
||||
FileUpload() { this = "bottle.FileUpload" }
|
||||
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
name = "filename" and result instanceof ExternalStringKind
|
||||
or
|
||||
name = "raw_filename" and result instanceof ExternalStringKind
|
||||
or
|
||||
name = "file" and result instanceof UntrustedFile
|
||||
}
|
||||
}
|
||||
|
||||
deprecated class UntrustedFile extends TaintKind {
|
||||
UntrustedFile() { this = "Untrusted file" }
|
||||
}
|
||||
|
||||
//
|
||||
// TO DO.. File uploads -- Should check about file uploads for other frameworks as well.
|
||||
// Move UntrustedFile to shared location
|
||||
//
|
||||
/** A parameter to a bottle request handler function */
|
||||
deprecated class BottleRequestParameter extends HttpRequestTaintSource {
|
||||
BottleRequestParameter() {
|
||||
exists(BottleRoute route | route.getANamedArgument() = this.(ControlFlowNode).getNode())
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind }
|
||||
|
||||
override string toString() { result = "bottle handler function argument" }
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
import python
|
||||
import semmle.python.web.Http
|
||||
|
||||
deprecated module CherryPy {
|
||||
FunctionValue expose() { result = Value::named("cherrypy.expose") }
|
||||
}
|
||||
|
||||
deprecated class CherryPyExposedFunction extends Function {
|
||||
CherryPyExposedFunction() {
|
||||
this.getADecorator().pointsTo(CherryPy::expose())
|
||||
or
|
||||
this.getADecorator().(Call).getFunc().pointsTo(CherryPy::expose())
|
||||
}
|
||||
}
|
||||
|
||||
deprecated class CherryPyRoute extends CallNode {
|
||||
CherryPyRoute() {
|
||||
/* cherrypy.quickstart(root, script_name, config) */
|
||||
Value::named("cherrypy.quickstart").(FunctionValue).getACall() = this
|
||||
or
|
||||
/* cherrypy.tree.mount(root, script_name, config) */
|
||||
this.getFunction().(AttrNode).getObject("mount").pointsTo(Value::named("cherrypy.tree"))
|
||||
}
|
||||
|
||||
ClassValue getAppClass() {
|
||||
this.getArg(0).pointsTo().getClass() = result
|
||||
or
|
||||
this.getArgByName("root").pointsTo().getClass() = result
|
||||
}
|
||||
|
||||
string getPath() {
|
||||
exists(Value path | path = Value::forString(result) |
|
||||
this.getArg(1).pointsTo(path)
|
||||
or
|
||||
this.getArgByName("script_name").pointsTo(path)
|
||||
)
|
||||
}
|
||||
|
||||
ClassValue getConfig() {
|
||||
this.getArg(2).pointsTo().getClass() = result
|
||||
or
|
||||
this.getArgByName("config").pointsTo().getClass() = result
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
import python
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
import semmle.python.security.strings.Basic
|
||||
import semmle.python.web.Http
|
||||
import semmle.python.web.cherrypy.General
|
||||
|
||||
/** The cherrypy.request local-proxy object */
|
||||
deprecated class CherryPyRequest extends TaintKind {
|
||||
CherryPyRequest() { this = "cherrypy.request" }
|
||||
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
name = "params" and result instanceof ExternalStringDictKind
|
||||
or
|
||||
name = "cookie" and result instanceof UntrustedCookie
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
name in ["getHeader", "getCookie", "getUser", "getPassword"] and
|
||||
result instanceof ExternalStringKind
|
||||
}
|
||||
}
|
||||
|
||||
deprecated class CherryPyExposedFunctionParameter extends HttpRequestTaintSource {
|
||||
CherryPyExposedFunctionParameter() {
|
||||
exists(Parameter p |
|
||||
p = any(CherryPyExposedFunction f).getAnArg() and
|
||||
not p.isSelf() and
|
||||
p.asName().getAFlowNode() = this
|
||||
)
|
||||
}
|
||||
|
||||
override string toString() { result = "CherryPy handler function parameter" }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind }
|
||||
}
|
||||
|
||||
deprecated class CherryPyRequestSource extends HttpRequestTaintSource {
|
||||
CherryPyRequestSource() { this.(ControlFlowNode).pointsTo(Value::named("cherrypy.request")) }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof CherryPyRequest }
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
import python
|
||||
import semmle.python.regex
|
||||
import semmle.python.web.Http
|
||||
|
||||
// TODO: Since django uses `path = partial(...)`, our analysis doesn't understand this is
|
||||
// a FunctionValue, so we can't use `FunctionValue.getArgumentForCall`
|
||||
// https://github.com/django/django/blob/master/django/urls/conf.py#L76
|
||||
abstract deprecated class DjangoRoute extends CallNode {
|
||||
DjangoViewHandler getViewHandler() {
|
||||
result = view_handler_from_view_arg(this.getArg(1))
|
||||
or
|
||||
result = view_handler_from_view_arg(this.getArgByName("view"))
|
||||
}
|
||||
|
||||
abstract string getANamedArgument();
|
||||
|
||||
/**
|
||||
* Get the number of positional arguments that will be passed to the view.
|
||||
* Will only return a result if there are no named arguments.
|
||||
*/
|
||||
abstract int getNumPositionalArguments();
|
||||
}
|
||||
|
||||
/**
|
||||
* For function based views -- also see `DjangoClassBasedViewHandler`
|
||||
* https://docs.djangoproject.com/en/1.11/topics/http/views/
|
||||
* https://docs.djangoproject.com/en/3.0/topics/http/views/
|
||||
*/
|
||||
deprecated class DjangoViewHandler extends PythonFunctionValue {
|
||||
/** Gets the index of the 'request' argument */
|
||||
int getRequestArgIndex() { result = 0 }
|
||||
}
|
||||
|
||||
/**
|
||||
* Class based views
|
||||
* https://docs.djangoproject.com/en/1.11/topics/class-based-views/
|
||||
* https://docs.djangoproject.com/en/3.0/topics/class-based-views/
|
||||
*/
|
||||
deprecated private class DjangoViewClass extends ClassValue {
|
||||
DjangoViewClass() {
|
||||
Value::named("django.views.generic.View") = this.getASuperType()
|
||||
or
|
||||
Value::named("django.views.View") = this.getASuperType()
|
||||
}
|
||||
}
|
||||
|
||||
deprecated class DjangoClassBasedViewHandler extends DjangoViewHandler {
|
||||
DjangoClassBasedViewHandler() { exists(DjangoViewClass cls | cls.lookup(httpVerbLower()) = this) }
|
||||
|
||||
override int getRequestArgIndex() {
|
||||
// due to `self` being the first parameter
|
||||
result = 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the function that will handle requests when `view_arg` is used as the view argument to a
|
||||
* django route. That is, this methods handles Class-based Views and its `as_view()` function.
|
||||
*/
|
||||
deprecated private DjangoViewHandler view_handler_from_view_arg(ControlFlowNode view_arg) {
|
||||
// Function-based view
|
||||
result = view_arg.pointsTo()
|
||||
or
|
||||
// Class-based view
|
||||
exists(ClassValue cls |
|
||||
cls = view_arg.(CallNode).getFunction().(AttrNode).getObject("as_view").pointsTo() and
|
||||
result = cls.lookup(httpVerbLower())
|
||||
)
|
||||
}
|
||||
|
||||
// We need this "dummy" class, since otherwise the regex argument would not be considered
|
||||
// a regex (RegexString is abstract)
|
||||
deprecated class DjangoRouteRegex extends RegexString {
|
||||
DjangoRouteRegex() { exists(DjangoRegexRoute route | route.getRouteArg() = this.getAFlowNode()) }
|
||||
}
|
||||
|
||||
deprecated class DjangoRegexRoute extends DjangoRoute {
|
||||
ControlFlowNode route;
|
||||
|
||||
DjangoRegexRoute() {
|
||||
exists(FunctionValue route_maker |
|
||||
// Django 1.x: https://docs.djangoproject.com/en/1.11/ref/urls/#django.conf.urls.url
|
||||
Value::named("django.conf.urls.url") = route_maker and
|
||||
route_maker.getArgumentForCall(this, 0) = route
|
||||
)
|
||||
or
|
||||
// Django 2.x and 3.x: https://docs.djangoproject.com/en/3.0/ref/urls/#re-path
|
||||
this = Value::named("django.urls.re_path").getACall() and
|
||||
(
|
||||
route = this.getArg(0)
|
||||
or
|
||||
route = this.getArgByName("route")
|
||||
)
|
||||
}
|
||||
|
||||
ControlFlowNode getRouteArg() { result = route }
|
||||
|
||||
override string getANamedArgument() {
|
||||
exists(DjangoRouteRegex regex | regex.getAFlowNode() = route |
|
||||
result = regex.getGroupName(_, _)
|
||||
)
|
||||
}
|
||||
|
||||
override int getNumPositionalArguments() {
|
||||
not exists(this.getANamedArgument()) and
|
||||
exists(DjangoRouteRegex regex | regex.getAFlowNode() = route |
|
||||
result = count(regex.getGroupNumber(_, _))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
deprecated class DjangoPathRoute extends DjangoRoute {
|
||||
ControlFlowNode route;
|
||||
|
||||
DjangoPathRoute() {
|
||||
// Django 2.x and 3.x: https://docs.djangoproject.com/en/3.0/ref/urls/#path
|
||||
this = Value::named("django.urls.path").getACall() and
|
||||
(
|
||||
route = this.getArg(0)
|
||||
or
|
||||
route = this.getArgByName("route")
|
||||
)
|
||||
}
|
||||
|
||||
override string getANamedArgument() {
|
||||
// regexp taken from django:
|
||||
// https://github.com/django/django/blob/7d1bf29977bb368d7c28e7c6eb146db3b3009ae7/django/urls/resolvers.py#L199
|
||||
exists(StrConst route_str, string match |
|
||||
route_str = route.getNode() and
|
||||
match = route_str.getText().regexpFind("<(?:(?<converter>[^>:]+):)?(?<parameter>\\w+)>", _, _) and
|
||||
result = match.regexpCapture("<(?:(?<converter>[^>:]+):)?(?<parameter>\\w+)>", 2)
|
||||
)
|
||||
}
|
||||
|
||||
override int getNumPositionalArguments() { none() }
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
import python
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
import semmle.python.web.Http
|
||||
import semmle.python.web.django.General
|
||||
|
||||
/** A django.request.HttpRequest object */
|
||||
deprecated class DjangoRequest extends TaintKind {
|
||||
DjangoRequest() { this = "django.request.HttpRequest" }
|
||||
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
(name = "GET" or name = "POST") and
|
||||
result instanceof DjangoQueryDict
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
(name = "body" or name = "path") and
|
||||
result instanceof ExternalStringKind
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper for getTaintForStep() */
|
||||
pragma[noinline]
|
||||
deprecated private predicate subscript_taint(SubscriptNode sub, ControlFlowNode obj, TaintKind kind) {
|
||||
sub.getObject() = obj and
|
||||
kind instanceof ExternalStringKind
|
||||
}
|
||||
|
||||
/** A django.request.QueryDict object */
|
||||
deprecated class DjangoQueryDict extends TaintKind {
|
||||
DjangoQueryDict() { this = "django.http.request.QueryDict" }
|
||||
|
||||
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
this.taints(fromnode) and
|
||||
subscript_taint(tonode, fromnode, result)
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
name = "get" and result instanceof ExternalStringKind
|
||||
}
|
||||
}
|
||||
|
||||
/** A Django request parameter */
|
||||
deprecated class DjangoRequestSource extends HttpRequestTaintSource {
|
||||
DjangoRequestSource() {
|
||||
exists(DjangoRoute route, DjangoViewHandler view, int request_arg_index |
|
||||
route.getViewHandler() = view and
|
||||
request_arg_index = view.getRequestArgIndex() and
|
||||
this = view.getScope().getArg(request_arg_index).asName().getAFlowNode()
|
||||
)
|
||||
}
|
||||
|
||||
override string toString() { result = "Django request source" }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoRequest }
|
||||
}
|
||||
|
||||
/** An argument specified in a url routing table */
|
||||
deprecated class DjangoRequestParameter extends HttpRequestTaintSource {
|
||||
DjangoRequestParameter() {
|
||||
exists(DjangoRoute route, Function f, DjangoViewHandler view, int request_arg_index |
|
||||
route.getViewHandler() = view and
|
||||
request_arg_index = view.getRequestArgIndex() and
|
||||
f = view.getScope()
|
||||
|
|
||||
this.(ControlFlowNode).getNode() = f.getArgByName(route.getANamedArgument())
|
||||
or
|
||||
exists(int i | i >= 0 |
|
||||
i < route.getNumPositionalArguments() and
|
||||
// +1 because first argument is always the request
|
||||
this.(ControlFlowNode).getNode() = f.getArg(request_arg_index + 1 + i)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind }
|
||||
|
||||
override string toString() { result = "django.http.request.parameter" }
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import python
|
||||
import semmle.python.web.Http
|
||||
|
||||
/** Gets the falcon API class */
|
||||
deprecated ClassValue theFalconAPIClass() { result = Value::named("falcon.API") }
|
||||
|
||||
/** Holds if `route` is routed to `resource` */
|
||||
deprecated private predicate api_route(
|
||||
CallNode route_call, ControlFlowNode route, ClassValue resource
|
||||
) {
|
||||
route_call.getFunction().(AttrNode).getObject("add_route").pointsTo().getClass() =
|
||||
theFalconAPIClass() and
|
||||
route_call.getArg(0) = route and
|
||||
route_call.getArg(1).pointsTo().getClass() = resource
|
||||
}
|
||||
|
||||
deprecated private predicate route(FalconRoute route, Function target, string funcname) {
|
||||
route.getResourceClass().lookup("on_" + funcname).(FunctionValue).getScope() = target
|
||||
}
|
||||
|
||||
deprecated class FalconRoute extends ControlFlowNode {
|
||||
FalconRoute() { api_route(this, _, _) }
|
||||
|
||||
string getUrl() {
|
||||
exists(StrConst url |
|
||||
api_route(this, url.getAFlowNode(), _) and
|
||||
result = url.getText()
|
||||
)
|
||||
}
|
||||
|
||||
ClassValue getResourceClass() { api_route(this, _, result) }
|
||||
|
||||
FalconHandlerFunction getHandlerFunction(string method) { route(this, result, method) }
|
||||
}
|
||||
|
||||
deprecated class FalconHandlerFunction extends Function {
|
||||
FalconHandlerFunction() { route(_, this, _) }
|
||||
|
||||
private string methodName() { route(_, this, result) }
|
||||
|
||||
string getMethod() { result = this.methodName().toUpperCase() }
|
||||
|
||||
Parameter getRequest() { result = this.getArg(1) }
|
||||
|
||||
Parameter getResponse() { result = this.getArg(2) }
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
import python
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
import semmle.python.web.Http
|
||||
import semmle.python.web.falcon.General
|
||||
|
||||
/** https://falcon.readthedocs.io/en/stable/api/request_and_response.html */
|
||||
deprecated class FalconRequest extends TaintKind {
|
||||
FalconRequest() { this = "falcon.request" }
|
||||
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
name = "env" and result instanceof WsgiEnvironment
|
||||
or
|
||||
result instanceof ExternalStringKind and
|
||||
name in ["uri", "url", "forwarded_uri", "relative_uri", "query_string"]
|
||||
or
|
||||
result instanceof ExternalStringDictKind and
|
||||
(name = "cookies" or name = "params")
|
||||
or
|
||||
name = "stream" and result instanceof ExternalFileObject
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
name = "get_param" and result instanceof ExternalStringKind
|
||||
or
|
||||
name = "get_param_as_json" and result instanceof ExternalJsonKind
|
||||
or
|
||||
name = "get_param_as_list" and result instanceof ExternalStringSequenceKind
|
||||
}
|
||||
}
|
||||
|
||||
deprecated class FalconRequestParameter extends HttpRequestTaintSource {
|
||||
FalconRequestParameter() {
|
||||
exists(FalconHandlerFunction f | f.getRequest() = this.(ControlFlowNode).getNode())
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind k) { k instanceof FalconRequest }
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
import python
|
||||
import semmle.python.web.Http
|
||||
import semmle.python.web.flask.Response
|
||||
|
||||
/** Gets the flask app class */
|
||||
deprecated ClassValue theFlaskClass() { result = Value::named("flask.Flask") }
|
||||
|
||||
/** Gets the flask MethodView class */
|
||||
deprecated ClassValue theFlaskMethodViewClass() { result = Value::named("flask.views.MethodView") }
|
||||
|
||||
deprecated ClassValue theFlaskReponseClass() { result = Value::named("flask.Response") }
|
||||
|
||||
/**
|
||||
* Holds if `route` is routed to `func`
|
||||
* by decorating `func` with `app.route(route)`
|
||||
*/
|
||||
deprecated predicate app_route(ControlFlowNode route, Function func) {
|
||||
exists(CallNode route_call, CallNode decorator_call |
|
||||
route_call.getFunction().(AttrNode).getObject("route").pointsTo().getClass() = theFlaskClass() and
|
||||
decorator_call.getFunction() = route_call and
|
||||
route_call.getArg(0) = route and
|
||||
decorator_call.getArg(0).getNode().(FunctionExpr).getInnerScope() = func
|
||||
)
|
||||
}
|
||||
|
||||
/* Helper for add_url_rule */
|
||||
deprecated private predicate add_url_rule_call(ControlFlowNode regex, ControlFlowNode callable) {
|
||||
exists(CallNode call |
|
||||
call.getFunction().(AttrNode).getObject("add_url_rule").pointsTo().getClass() = theFlaskClass() and
|
||||
regex = call.getArg(0)
|
||||
|
|
||||
callable = call.getArg(2) or
|
||||
callable = call.getArgByName("view_func")
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if urls matching `regex` are routed to `func` */
|
||||
deprecated predicate add_url_rule(ControlFlowNode regex, Function func) {
|
||||
exists(ControlFlowNode callable | add_url_rule_call(regex, callable) |
|
||||
exists(PythonFunctionValue f | f.getScope() = func and callable.pointsTo(f))
|
||||
or
|
||||
/* MethodView.as_view() */
|
||||
exists(MethodViewClass view_cls | view_cls.asTaint().taints(callable) |
|
||||
func = view_cls.lookup(httpVerbLower()).(FunctionValue).getScope()
|
||||
)
|
||||
/* TODO: -- Handle Views that aren't MethodViews */
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if urls matching `regex` are routed to `func` using
|
||||
* any of flask's routing mechanisms.
|
||||
*/
|
||||
deprecated predicate flask_routing(ControlFlowNode regex, Function func) {
|
||||
app_route(regex, func)
|
||||
or
|
||||
add_url_rule(regex, func)
|
||||
}
|
||||
|
||||
/** A class that extends flask.views.MethodView */
|
||||
deprecated private class MethodViewClass extends ClassValue {
|
||||
MethodViewClass() { this.getASuperType() = theFlaskMethodViewClass() }
|
||||
|
||||
/* As we are restricted to strings for taint kinds, we need to map these classes to strings. */
|
||||
string taintString() { result = "flask/" + this.getQualifiedName() + ".as.view" }
|
||||
|
||||
/* As we are restricted to strings for taint kinds, we need to map these classes to strings. */
|
||||
TaintKind asTaint() { result = this.taintString() }
|
||||
}
|
||||
|
||||
deprecated private class MethodViewTaint extends TaintKind {
|
||||
MethodViewTaint() { any(MethodViewClass cls).taintString() = this }
|
||||
}
|
||||
|
||||
/** A source of method view "taint"s. */
|
||||
deprecated private class AsView extends TaintSource {
|
||||
AsView() {
|
||||
exists(ClassValue view_class |
|
||||
view_class.getASuperType() = theFlaskMethodViewClass() and
|
||||
this.(CallNode).getFunction().(AttrNode).getObject("as_view").pointsTo(view_class)
|
||||
)
|
||||
}
|
||||
|
||||
override string toString() { result = "flask.MethodView.as_view()" }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) {
|
||||
exists(MethodViewClass view_class |
|
||||
kind = view_class.asTaint() and
|
||||
this.(CallNode).getFunction().(AttrNode).getObject("as_view").pointsTo(view_class)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
deprecated class FlaskCookieSet extends CookieSet, CallNode {
|
||||
FlaskCookieSet() {
|
||||
any(FlaskResponseTaintKind t).taints(this.getFunction().(AttrNode).getObject("set_cookie"))
|
||||
}
|
||||
|
||||
override string toString() { result = CallNode.super.toString() }
|
||||
|
||||
override ControlFlowNode getKey() { result = this.getArg(0) }
|
||||
|
||||
override ControlFlowNode getValue() { result = this.getArg(1) }
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
import python
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
import semmle.python.web.Http
|
||||
import semmle.python.web.flask.General
|
||||
|
||||
deprecated private Value theFlaskRequestObject() { result = Value::named("flask.request") }
|
||||
|
||||
/** Holds if `attr` is an access of attribute `name` of the flask request object */
|
||||
deprecated private predicate flask_request_attr(AttrNode attr, string name) {
|
||||
attr.isLoad() and
|
||||
attr.getObject(name).pointsTo(theFlaskRequestObject())
|
||||
}
|
||||
|
||||
/** Source of external data from a flask request */
|
||||
deprecated class FlaskRequestData extends HttpRequestTaintSource {
|
||||
FlaskRequestData() {
|
||||
not this instanceof FlaskRequestArgs and
|
||||
exists(string name | flask_request_attr(this, name) |
|
||||
name in ["path", "full_path", "base_url", "url"]
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind }
|
||||
|
||||
override string toString() { result = "flask.request" }
|
||||
}
|
||||
|
||||
/** Source of dictionary whose values are externally controlled */
|
||||
deprecated class FlaskRequestArgs extends HttpRequestTaintSource {
|
||||
FlaskRequestArgs() {
|
||||
exists(string attr | flask_request_attr(this, attr) |
|
||||
attr in ["args", "form", "values", "files", "headers", "json"]
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind }
|
||||
|
||||
override string toString() { result = "flask.request.args" }
|
||||
}
|
||||
|
||||
/** Source of dictionary whose values are externally controlled */
|
||||
deprecated class FlaskRequestJson extends HttpRequestTaintSource {
|
||||
FlaskRequestJson() { flask_request_attr(this, "json") }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalJsonKind }
|
||||
|
||||
override string toString() { result = "flask.request.json" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A parameter to a flask request handler, that can capture a part of the URL (as specified in
|
||||
* the url-pattern of a route).
|
||||
*
|
||||
* For example, the `name` parameter in:
|
||||
* ```
|
||||
* @app.route('/hello/<name>')
|
||||
* def hello(name):
|
||||
* ```
|
||||
*/
|
||||
deprecated class FlaskRoutedParameter extends HttpRequestTaintSource {
|
||||
FlaskRoutedParameter() {
|
||||
exists(string name, Function func, StrConst url_pattern |
|
||||
this.(ControlFlowNode).getNode() = func.getArgByName(name) and
|
||||
flask_routing(url_pattern.getAFlowNode(), func) and
|
||||
exists(string match |
|
||||
match = url_pattern.getS().regexpFind(werkzeug_rule_re(), _, _) and
|
||||
name = match.regexpCapture(werkzeug_rule_re(), 4)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind }
|
||||
}
|
||||
|
||||
deprecated private string werkzeug_rule_re() {
|
||||
// since flask uses werkzeug internally, we are using its routing rules from
|
||||
// https://github.com/pallets/werkzeug/blob/4dc8d6ab840d4b78cbd5789cef91b01e3bde01d5/src/werkzeug/routing.py#L138-L151
|
||||
result =
|
||||
"(?<static>[^<]*)<(?:(?<converter>[a-zA-Z_][a-zA-Z0-9_]*)(?:\\((?<args>.*?)\\))?\\:)?(?<variable>[a-zA-Z_][a-zA-Z0-9_]*)>"
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
import python
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
import semmle.python.security.strings.Basic
|
||||
import semmle.python.web.flask.General
|
||||
|
||||
/**
|
||||
* A flask response, which is vulnerable to any sort of
|
||||
* http response malice.
|
||||
*/
|
||||
deprecated class FlaskRoutedResponse extends HttpResponseTaintSink {
|
||||
FlaskRoutedResponse() {
|
||||
exists(PythonFunctionValue response |
|
||||
flask_routing(_, response.getScope()) and
|
||||
this = response.getAReturnedNode()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) { kind instanceof StringKind }
|
||||
|
||||
override string toString() { result = "flask.routed.response" }
|
||||
}
|
||||
|
||||
deprecated class FlaskResponseArgument extends HttpResponseTaintSink {
|
||||
FlaskResponseArgument() {
|
||||
exists(CallNode call |
|
||||
(
|
||||
call.getFunction().pointsTo(theFlaskReponseClass())
|
||||
or
|
||||
call.getFunction().pointsTo(Value::named("flask.make_response"))
|
||||
) and
|
||||
call.getArg(0) = this
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) { kind instanceof StringKind }
|
||||
|
||||
override string toString() { result = "flask.response.argument" }
|
||||
}
|
||||
|
||||
deprecated class FlaskResponseTaintKind extends TaintKind {
|
||||
FlaskResponseTaintKind() { this = "flask.Response" }
|
||||
}
|
||||
|
||||
deprecated class FlaskResponseConfiguration extends TaintTracking::Configuration {
|
||||
FlaskResponseConfiguration() { this = "Flask response configuration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node, TaintKind kind) {
|
||||
kind instanceof FlaskResponseTaintKind and
|
||||
(
|
||||
node.asCfgNode().(CallNode).getFunction().pointsTo(theFlaskReponseClass())
|
||||
or
|
||||
node.asCfgNode().(CallNode).getFunction().pointsTo(Value::named("flask.make_response"))
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import python
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
import semmle.python.web.Http
|
||||
private import semmle.python.web.webob.Request
|
||||
private import semmle.python.web.pyramid.View
|
||||
|
||||
deprecated class PyramidRequest extends BaseWebobRequest {
|
||||
PyramidRequest() { this = "pyramid.request" }
|
||||
|
||||
override ClassValue getType() { result = Value::named("pyramid.request.Request") }
|
||||
}
|
||||
|
||||
/** Source of pyramid request objects */
|
||||
deprecated class PyramidViewArgument extends HttpRequestTaintSource {
|
||||
PyramidViewArgument() {
|
||||
exists(Function view_func |
|
||||
is_pyramid_view_function(view_func) and
|
||||
this.(ControlFlowNode).getNode() = view_func.getArg(0)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof PyramidRequest }
|
||||
|
||||
override string toString() { result = "pyramid.view.argument" }
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import python
|
||||
|
||||
deprecated ModuleValue thePyramidViewModule() { result.getName() = "pyramid.view" }
|
||||
|
||||
deprecated Value thePyramidViewConfig() { result = thePyramidViewModule().attr("view_config") }
|
||||
|
||||
deprecated predicate is_pyramid_view_function(Function func) {
|
||||
func.getADecorator().pointsTo().getClass() = thePyramidViewConfig()
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
/**
|
||||
* Provides the sources and taint-flow for HTTP servers defined using the standard library (stdlib).
|
||||
* Specifically, we model `HttpRequestTaintSource`s from instances of `BaseHTTPRequestHandler`
|
||||
* (or subclasses) and form parsing using `cgi.FieldStorage`.
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
import semmle.python.web.Http
|
||||
|
||||
/** Source of BaseHttpRequestHandler instances. */
|
||||
deprecated class StdLibRequestSource extends HttpRequestTaintSource {
|
||||
StdLibRequestSource() {
|
||||
exists(ClassValue cls |
|
||||
cls.getABaseType+() = Value::named("BaseHTTPServer.BaseHTTPRequestHandler")
|
||||
or
|
||||
cls.getABaseType+() = Value::named("http.server.BaseHTTPRequestHandler")
|
||||
|
|
||||
this.(ControlFlowNode).pointsTo().getClass() = cls
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof BaseHTTPRequestHandlerKind }
|
||||
}
|
||||
|
||||
/** TaintKind for an instance of BaseHttpRequestHandler. */
|
||||
deprecated class BaseHTTPRequestHandlerKind extends TaintKind {
|
||||
BaseHTTPRequestHandlerKind() { this = "BaseHTTPRequestHandlerKind" }
|
||||
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
name in ["requestline", "path"] and
|
||||
result instanceof ExternalStringKind
|
||||
or
|
||||
name = "headers" and
|
||||
result instanceof HTTPMessageKind
|
||||
or
|
||||
name = "rfile" and
|
||||
result instanceof ExternalFileObject
|
||||
}
|
||||
}
|
||||
|
||||
/** TaintKind for headers (instance of HttpMessage). */
|
||||
deprecated class HTTPMessageKind extends ExternalStringDictKind {
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
result = super.getTaintOfMethodResult(name)
|
||||
or
|
||||
name = "get_all" and
|
||||
result.(SequenceKind).getItem() = this.getValue()
|
||||
or
|
||||
name in ["as_bytes", "as_string"] and
|
||||
result instanceof ExternalStringKind
|
||||
}
|
||||
|
||||
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
result = super.getTaintForFlowStep(fromnode, tonode)
|
||||
or
|
||||
exists(ClassValue cls | cls = ClassValue::unicode() or cls = ClassValue::bytes() |
|
||||
tonode = cls.getACall() and
|
||||
tonode.(CallNode).getArg(0) = fromnode and
|
||||
result instanceof ExternalStringKind
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Source of parsed HTTP forms (by using the `cgi` module). */
|
||||
deprecated class CgiFieldStorageSource extends HttpRequestTaintSource {
|
||||
CgiFieldStorageSource() { this = Value::named("cgi.FieldStorage").getACall() }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof CgiFieldStorageFormKind }
|
||||
}
|
||||
|
||||
/** TaintKind for a parsed HTTP form. */
|
||||
deprecated class CgiFieldStorageFormKind extends TaintKind {
|
||||
/*
|
||||
* There is a slight difference between how we model form/fields and how it is handled by the code.
|
||||
* In the code
|
||||
* ```
|
||||
* form = cgi.FieldStorage()
|
||||
* field = form['myfield']
|
||||
* ```
|
||||
* both `form` and `field` have the type `cgi.FieldStorage`. This allows the code to represent
|
||||
* nested forms as `form['nested_form']['myfield']`. However, since HTML forms can't be nested
|
||||
* we ignore that detail since it allows for a more clean modeling.
|
||||
*/
|
||||
|
||||
CgiFieldStorageFormKind() { this = "CgiFieldStorageFormKind" }
|
||||
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
name = "value" and result.(SequenceKind).getItem() instanceof CgiFieldStorageFieldKind
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
name = "getvalue" and
|
||||
(
|
||||
result instanceof ExternalStringKind
|
||||
or
|
||||
result.(SequenceKind).getItem() instanceof ExternalStringKind
|
||||
)
|
||||
or
|
||||
name = "getfirst" and
|
||||
result instanceof ExternalStringKind
|
||||
or
|
||||
name = "getlist" and
|
||||
result.(SequenceKind).getItem() instanceof ExternalStringKind
|
||||
}
|
||||
|
||||
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
tonode.(SubscriptNode).getObject() = fromnode and
|
||||
(
|
||||
result instanceof CgiFieldStorageFieldKind
|
||||
or
|
||||
result.(SequenceKind).getItem() instanceof CgiFieldStorageFieldKind
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** TaintKind for the field of a parsed HTTP form. */
|
||||
deprecated class CgiFieldStorageFieldKind extends TaintKind {
|
||||
CgiFieldStorageFieldKind() { this = "CgiFieldStorageFieldKind" }
|
||||
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
name in ["filename", "value"] and result instanceof ExternalStringKind
|
||||
or
|
||||
name = "file" and result instanceof ExternalFileObject
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
import python
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
import semmle.python.web.Http
|
||||
import Tornado
|
||||
|
||||
/** A tornado.request.HttpRequest object */
|
||||
deprecated class TornadoRequest extends TaintKind {
|
||||
TornadoRequest() { this = "tornado.request.HttpRequest" }
|
||||
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
result instanceof ExternalStringDictKind and
|
||||
(
|
||||
name = "headers" or
|
||||
name = "cookies"
|
||||
)
|
||||
or
|
||||
result instanceof ExternalStringKind and
|
||||
(
|
||||
name = "uri" or
|
||||
name = "query" or
|
||||
name = "body"
|
||||
)
|
||||
or
|
||||
result instanceof ExternalStringSequenceDictKind and
|
||||
(
|
||||
name = "arguments" or
|
||||
name = "query_arguments" or
|
||||
name = "body_arguments"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
deprecated class TornadoRequestSource extends HttpRequestTaintSource {
|
||||
TornadoRequestSource() { isTornadoRequestHandlerInstance(this.(AttrNode).getObject("request")) }
|
||||
|
||||
override string toString() { result = "Tornado request source" }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof TornadoRequest }
|
||||
}
|
||||
|
||||
deprecated class TornadoExternalInputSource extends HttpRequestTaintSource {
|
||||
TornadoExternalInputSource() {
|
||||
exists(string name |
|
||||
name in ["get_argument", "get_query_argument", "get_body_argument", "decode_argument"]
|
||||
|
|
||||
this = callToNamedTornadoRequestHandlerMethod(name)
|
||||
)
|
||||
}
|
||||
|
||||
override string toString() { result = "Tornado request method" }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind }
|
||||
}
|
||||
|
||||
deprecated class TornadoExternalInputListSource extends HttpRequestTaintSource {
|
||||
TornadoExternalInputListSource() {
|
||||
exists(string name |
|
||||
name = "get_arguments" or
|
||||
name = "get_query_arguments" or
|
||||
name = "get_body_arguments"
|
||||
|
|
||||
this = callToNamedTornadoRequestHandlerMethod(name)
|
||||
)
|
||||
}
|
||||
|
||||
override string toString() { result = "Tornado request method" }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind }
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
import python
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
import semmle.python.web.Http
|
||||
|
||||
deprecated private ClassValue theTornadoRequestHandlerClass() {
|
||||
result = Value::named("tornado.web.RequestHandler")
|
||||
}
|
||||
|
||||
deprecated ClassValue aTornadoRequestHandlerClass() {
|
||||
result.getABaseType+() = theTornadoRequestHandlerClass()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is likely to refer to an instance of a tornado
|
||||
* `RequestHandler` class.
|
||||
*/
|
||||
deprecated predicate isTornadoRequestHandlerInstance(ControlFlowNode node) {
|
||||
node.pointsTo().getClass() = aTornadoRequestHandlerClass()
|
||||
or
|
||||
/*
|
||||
* In some cases, the points-to analysis won't capture all instances we care
|
||||
* about. For these, we use the following syntactic check. First, that
|
||||
* `node` appears inside a method of a subclass of
|
||||
* `tornado.web.RequestHandler`:
|
||||
*/
|
||||
|
||||
node.getScope().getEnclosingScope() = aTornadoRequestHandlerClass().getScope() and
|
||||
/* Secondly, that `node` refers to the `self` argument: */
|
||||
node.isLoad() and
|
||||
node.(NameNode).isSelf()
|
||||
}
|
||||
|
||||
deprecated CallNode callToNamedTornadoRequestHandlerMethod(string name) {
|
||||
isTornadoRequestHandlerInstance(result.getFunction().(AttrNode).getObject(name))
|
||||
}
|
||||
|
||||
deprecated class TornadoCookieSet extends CookieSet, CallNode {
|
||||
TornadoCookieSet() {
|
||||
exists(ControlFlowNode f |
|
||||
f = this.getFunction().(AttrNode).getObject("set_cookie") and
|
||||
isTornadoRequestHandlerInstance(f)
|
||||
)
|
||||
}
|
||||
|
||||
override string toString() { result = CallNode.super.toString() }
|
||||
|
||||
override ControlFlowNode getKey() { result = this.getArg(0) }
|
||||
|
||||
override ControlFlowNode getValue() { result = this.getArg(1) }
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import python
|
||||
import semmle.python.security.strings.External
|
||||
import semmle.python.web.Http
|
||||
import TurboGears
|
||||
|
||||
deprecated private class ValidatedMethodParameter extends Parameter {
|
||||
ValidatedMethodParameter() {
|
||||
exists(string name, TurboGearsControllerMethod method |
|
||||
method.getArgByName(name) = this and
|
||||
method.getValidationDict().getItem(_).(KeyValuePair).getKey().(StrConst).getText() = name
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
deprecated class UnvalidatedControllerMethodParameter extends HttpRequestTaintSource {
|
||||
UnvalidatedControllerMethodParameter() {
|
||||
exists(Parameter p |
|
||||
any(TurboGearsControllerMethod m | not m.getName() = "onerror").getAnArg() = p and
|
||||
not p instanceof ValidatedMethodParameter and
|
||||
not p.isSelf() and
|
||||
p.(Name).getAFlowNode() = this
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind }
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
import python
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
|
||||
deprecated private ClassValue theTurboGearsControllerClass() {
|
||||
result = Value::named("tg.TGController")
|
||||
}
|
||||
|
||||
deprecated ClassValue aTurboGearsControllerClass() {
|
||||
result.getABaseType+() = theTurboGearsControllerClass()
|
||||
}
|
||||
|
||||
deprecated class TurboGearsControllerMethod extends Function {
|
||||
ControlFlowNode decorator;
|
||||
|
||||
TurboGearsControllerMethod() {
|
||||
aTurboGearsControllerClass().getScope() = this.getScope() and
|
||||
decorator = this.getADecorator().getAFlowNode() and
|
||||
/* Is decorated with @expose() or @expose(path) */
|
||||
(
|
||||
decorator.(CallNode).getFunction().(NameNode).getId() = "expose"
|
||||
or
|
||||
decorator.pointsTo().getClass() = Value::named("tg.expose")
|
||||
)
|
||||
}
|
||||
|
||||
private ControlFlowNode templateName() { result = decorator.(CallNode).getArg(0) }
|
||||
|
||||
predicate isTemplated() { exists(this.templateName()) }
|
||||
|
||||
Dict getValidationDict() {
|
||||
exists(Call call |
|
||||
call = this.getADecorator() and
|
||||
call.getFunc().(Name).getId() = "validate" and
|
||||
call.getArg(0).pointsTo(_, result)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import python
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
import semmle.python.web.Http
|
||||
import Twisted
|
||||
|
||||
/** A twisted.web.http.Request object */
|
||||
deprecated class TwistedRequest extends TaintKind {
|
||||
TwistedRequest() { this = "twisted.request.http.Request" }
|
||||
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
result instanceof ExternalStringSequenceDictKind and
|
||||
name = "args"
|
||||
or
|
||||
result instanceof ExternalStringKind and
|
||||
name = "uri"
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
name in ["getHeader", "getCookie", "getUser", "getPassword"] and
|
||||
result instanceof ExternalStringKind
|
||||
}
|
||||
}
|
||||
|
||||
deprecated class TwistedRequestSource extends HttpRequestTaintSource {
|
||||
TwistedRequestSource() { isTwistedRequestInstance(this) }
|
||||
|
||||
override string toString() { result = "Twisted request source" }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof TwistedRequest }
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
import python
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
|
||||
deprecated private ClassValue theTwistedHttpRequestClass() {
|
||||
result = Value::named("twisted.web.http.Request")
|
||||
}
|
||||
|
||||
deprecated private ClassValue theTwistedHttpResourceClass() {
|
||||
result = Value::named("twisted.web.resource.Resource")
|
||||
}
|
||||
|
||||
deprecated ClassValue aTwistedRequestHandlerClass() {
|
||||
result.getABaseType+() = theTwistedHttpResourceClass()
|
||||
}
|
||||
|
||||
deprecated FunctionValue getTwistedRequestHandlerMethod(string name) {
|
||||
result = aTwistedRequestHandlerClass().declaredAttribute(name)
|
||||
}
|
||||
|
||||
bindingset[name]
|
||||
deprecated predicate isKnownRequestHandlerMethodName(string name) {
|
||||
name = "render" or
|
||||
name.matches("render_%")
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is likely to refer to an instance of the twisted
|
||||
* `Request` class.
|
||||
*/
|
||||
deprecated predicate isTwistedRequestInstance(NameNode node) {
|
||||
node.pointsTo().getClass() = theTwistedHttpRequestClass()
|
||||
or
|
||||
/*
|
||||
* In points-to analysis cannot infer that a given object is an instance of
|
||||
* the `twisted.web.http.Request` class, we also include any parameter
|
||||
* called `request` that appears inside a subclass of a request handler
|
||||
* class, and the appropriate arguments of known request handler methods.
|
||||
*/
|
||||
|
||||
exists(Function func |
|
||||
func = node.getScope() and
|
||||
func.getEnclosingScope() = aTwistedRequestHandlerClass().getScope()
|
||||
|
|
||||
/* Any parameter called `request` */
|
||||
node.getId() = "request" and
|
||||
node.isParameter()
|
||||
or
|
||||
/* Any request parameter of a known request handler method */
|
||||
isKnownRequestHandlerMethodName(func.getName()) and
|
||||
node.getNode() = func.getArg(1)
|
||||
)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user