mirror of
https://github.com/github/codeql.git
synced 2025-12-20 10:46:30 +01:00
Query upload
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* @name JWT encoding using empty key or algorithm
|
||||
* @description The application uses an empty secret or algorithm while encoding a JWT Token.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @id py/jwt-empty-secret-or-algorithm
|
||||
* @tags security
|
||||
*/
|
||||
|
||||
// determine precision above
|
||||
import python
|
||||
import experimental.semmle.python.Concepts
|
||||
import experimental.semmle.python.security.JWT
|
||||
|
||||
from JWTEncoding jwtEncoding, string affectedComponent
|
||||
where
|
||||
exists( |
|
||||
affectedComponent = "algorithm" and
|
||||
isEmptyOrNone(jwtEncoding.getAlgorithmNode())
|
||||
or
|
||||
affectedComponent = "key" and
|
||||
isEmptyOrNone(jwtEncoding.getKeyNode())
|
||||
)
|
||||
select jwtEncoding, affectedComponent, "is empty."
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @name JWT missing secret or public key verification
|
||||
* @description The application does not verify the JWT payload with a cryptographic secret or public key.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @id py/jwt-missing-verification
|
||||
* @tags security
|
||||
* external/cwe/cwe-347
|
||||
*/
|
||||
|
||||
// determine precision above
|
||||
import python
|
||||
import experimental.semmle.python.Concepts
|
||||
|
||||
from JWTDecoding jwtDecoding
|
||||
where not jwtDecoding.verifiesSignature()
|
||||
select jwtDecoding, "does not verify the JWT payload with a cryptographic secret or public key."
|
||||
@@ -0,0 +1,23 @@
|
||||
import jwt
|
||||
|
||||
# Encoding
|
||||
|
||||
# good - key and algorithm
|
||||
jwt.encode({"foo": "bar"}, "key", "HS256")
|
||||
jwt.encode({"foo": "bar"}, key="key", algorithm="HS256")
|
||||
|
||||
# bad - both key and algorithm set to None
|
||||
jwt.encode({"foo": "bar"}, None, None)
|
||||
|
||||
# bad - empty key
|
||||
jwt.encode({"foo": "bar"}, "", algorithm="HS256")
|
||||
jwt.encode({"foo": "bar"}, key="", algorithm="HS256")
|
||||
|
||||
# Decoding
|
||||
|
||||
# good
|
||||
jwt.decode(token, "key", "HS256")
|
||||
|
||||
# bad - unverified decoding
|
||||
jwt.decode(token, verify=False)
|
||||
jwt.decode(token, key, options={"verify_signature": False})
|
||||
@@ -13,3 +13,77 @@ private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import experimental.semmle.python.Frameworks
|
||||
|
||||
/** Provides classes for modeling JWT-related APIs. */
|
||||
module JWTEncoding {
|
||||
/**
|
||||
* A data-flow node that collects methods encoding a JWT token.
|
||||
*
|
||||
* Extend this class to model new APIs. If you want to refine existing API models,
|
||||
* extend `JWTEncoding` instead.
|
||||
*/
|
||||
abstract class Range extends DataFlow::Node {
|
||||
/**
|
||||
* Gets the argument containing the encoding key.
|
||||
*/
|
||||
abstract DataFlow::Node getKeyNode();
|
||||
|
||||
/**
|
||||
* Gets the algorithm Node used in the encoding.
|
||||
*/
|
||||
abstract DataFlow::Node getAlgorithmNode();
|
||||
|
||||
/**
|
||||
* Tries to get the algorithm used in the encoding.
|
||||
*/
|
||||
abstract string getAlgorithm();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data-flow node that collect methods immediately executing an expression.
|
||||
*
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `JWTEncoding::Range` instead.
|
||||
*/
|
||||
class JWTEncoding extends DataFlow::Node {
|
||||
JWTEncoding::Range range;
|
||||
|
||||
JWTEncoding() { this = range }
|
||||
|
||||
DataFlow::Node getKeyNode() { result = range.getKeyNode() }
|
||||
|
||||
DataFlow::Node getAlgorithmNode() { result = range.getAlgorithmNode() }
|
||||
|
||||
string getAlgorithm() { result = range.getAlgorithm() }
|
||||
}
|
||||
|
||||
/** Provides classes for modeling Regular Expression escape-related APIs. */
|
||||
module JWTDecoding {
|
||||
/**
|
||||
* A data-flow node that collects functions escaping regular expressions.
|
||||
*
|
||||
* Extend this class to model new APIs. If you want to refine existing API models,
|
||||
* extend `JWTDecoding` instead.
|
||||
*/
|
||||
abstract class Range extends DataFlow::Node {
|
||||
/**
|
||||
* Gets the argument containing the escaped expression.
|
||||
*/
|
||||
abstract predicate verifiesSignature();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data-flow node that collects functions escaping regular expressions.
|
||||
*
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `JWTDecoding::Range` instead.
|
||||
*/
|
||||
class JWTDecoding extends DataFlow::Node {
|
||||
JWTDecoding::Range range;
|
||||
|
||||
JWTDecoding() { this = range }
|
||||
|
||||
predicate verifiesSignature() { range.verifiesSignature() }
|
||||
}
|
||||
|
||||
@@ -9,3 +9,37 @@ private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import experimental.semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
private module JWT {
|
||||
private class PyJWTEncodeCall extends DataFlow::CallCfgNode, JWTEncoding::Range {
|
||||
PyJWTEncodeCall() { this = API::moduleImport("jwt").getMember("encode").getACall() }
|
||||
|
||||
override DataFlow::Node getKeyNode() {
|
||||
result = this.getArg(1) or result = this.getArgByName("key")
|
||||
}
|
||||
|
||||
override DataFlow::Node getAlgorithmNode() {
|
||||
result = this.getArg(2) or
|
||||
result = this.getArgByName("algorithm")
|
||||
}
|
||||
|
||||
override string getAlgorithm() { result = getAlgorithmNode().asExpr().(Str_).getS() }
|
||||
}
|
||||
|
||||
private class PyJWTDecodeCall extends DataFlow::CallCfgNode, JWTDecoding::Range {
|
||||
PyJWTDecodeCall() { this = API::moduleImport("jwt").getMember("decode").getACall() }
|
||||
|
||||
override predicate verifiesSignature() {
|
||||
not exists(NameConstant falseName |
|
||||
falseName.getId() = "False" and
|
||||
exists( | falseName = this.getArgByName("verify").asExpr())
|
||||
or
|
||||
exists(KeyValuePair optionsDict |
|
||||
optionsDict = this.getArgByName("options").asExpr().(Dict).getItems().getAnItem() and
|
||||
optionsDict.getKey().(Str_).getS().matches("verify_signature") and
|
||||
falseName = optionsDict.getValue()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
11
python/ql/src/experimental/semmle/python/security/JWT.qll
Normal file
11
python/ql/src/experimental/semmle/python/security/JWT.qll
Normal file
@@ -0,0 +1,11 @@
|
||||
import python
|
||||
import experimental.semmle.python.Concepts
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
|
||||
predicate isEmptyOrNone(DataFlow::Node arg) { isEmpty(arg) or isNone(arg) }
|
||||
|
||||
predicate isEmpty(DataFlow::Node arg) { arg.asExpr().(Str_).getS() = "" }
|
||||
|
||||
predicate isNone(DataFlow::Node arg) {
|
||||
exists(NameConstant noneName | noneName.getId() = "None" and arg.asExpr() = noneName)
|
||||
}
|
||||
Reference in New Issue
Block a user