Skip to content

Commit 7069f45

Browse files
committed
Polish documentation
1 parent 350cbb4 commit 7069f45

File tree

5 files changed

+104
-9
lines changed

5 files changed

+104
-9
lines changed

python/ql/src/experimental/Security/CWE-347/JWTEmptyKeyOrAlgorithm.qhelp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
<qhelp>
55
<overview>
66
<p>Applications encoding a JSON Web Token (JWT) may be vulnerable when the applied key or algorithm
7-
is empty or None.</p>
7+
is empty or <code>None</code>.</p>
88
</overview>
99

1010
<recommendation>
11-
<p>Use non-empty nor None values while encoding JWT payloads.</p>
11+
<p>Use non-empty nor <code>None</code> values while encoding JWT payloads.</p>
1212
</recommendation>
1313

1414
<example>

python/ql/src/experimental/semmle/python/frameworks/JWT.qll

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
11
private import python
22
private import semmle.python.ApiGraphs
33

4+
/** Checks if the argument is empty or none. */
45
predicate isEmptyOrNone(DataFlow::Node arg) { isEmpty(arg) or isNone(arg) }
56

7+
/** Checks if an empty string `""` flows to `arg` */
68
predicate isEmpty(DataFlow::Node arg) {
79
exists(StrConst emptyString |
810
emptyString.getText() = "" and
911
DataFlow::exprNode(emptyString).(DataFlow::LocalSourceNode).flowsTo(arg)
1012
)
1113
}
1214

15+
/** Checks if `None` flows to `arg` */
1316
predicate isNone(DataFlow::Node arg) {
1417
exists( | DataFlow::exprNode(any(None no)).(DataFlow::LocalSourceNode).flowsTo(arg))
1518
}
1619

20+
/** Checks if `False` flows to `arg` */
1721
predicate isFalse(DataFlow::Node arg) {
1822
exists( | DataFlow::exprNode(any(False falseExpr)).(DataFlow::LocalSourceNode).flowsTo(arg))
1923
}
20-

python/ql/src/experimental/semmle/python/libraries/Authlib.qll

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,23 @@ private module Authlib {
1818
/** Gets a reference to `jwt.decode` */
1919
private API::Node authlibJWTDecode() { result = authlibJWT().getMember("decode") }
2020

21-
// def encode(self, header, payload, key, check=True):
21+
/**
22+
* Gets a call to `authlib.jose.(jwt|JsonWebToken).encode`.
23+
*
24+
* Given the following example:
25+
*
26+
* ```py
27+
* jwt.encode({"alg": "HS256"}, token, "key")
28+
* ```
29+
*
30+
* * `this` would be `jwt.encode({"alg": "HS256"}, token, "key")`.
31+
* * `getPayload()`'s result would be `token`.
32+
* * `getKey()`'s result would be `"key"`.
33+
* * `getAlgorithm()`'s result would be `"HS256"`.
34+
* * `getAlgorithmstring()`'s result would be `HS256`.
35+
*/
2236
private class AuthlibJWTEncodeCall extends DataFlow::CallCfgNode, JWTEncoding::Range {
37+
// def encode(self, header, payload, key, check=True):
2338
AuthlibJWTEncodeCall() { this = authlibJWTEncode().getACall() }
2439

2540
override DataFlow::Node getPayload() { result = this.getArg(1) }
@@ -42,8 +57,21 @@ private module Authlib {
4257
}
4358
}
4459

45-
// def decode(self, s, key, claims_cls=None, claims_options=None, claims_params=None):
60+
/**
61+
* Gets a call to `authlib.jose.(jwt|JsonWebToken).decode`
62+
*
63+
* Given the following example:
64+
*
65+
* ```py
66+
* jwt.decode(token, key)
67+
* ```
68+
*
69+
* * `this` would be `jwt.decode(token, key)`.
70+
* * `getPayload()`'s result would be `token`.
71+
* * `getKey()`'s result would be `key`.
72+
*/
4673
private class AuthlibJWTDecodeCall extends DataFlow::CallCfgNode, JWTDecoding::Range {
74+
// def decode(self, s, key, claims_cls=None, claims_options=None, claims_params=None):
4775
AuthlibJWTDecodeCall() { this = authlibJWTDecode().getACall() }
4876

4977
override DataFlow::Node getPayload() { result = this.getArg(0) }

python/ql/src/experimental/semmle/python/libraries/PyJWT.qll

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,23 @@ private module PyJWT {
1010
/** Gets a reference to `jwt.decode` */
1111
private API::Node pyjwtDecode() { result = API::moduleImport("jwt").getMember("decode") }
1212

13-
// def encode(self, payload, key, algorithm="HS256", headers=None, json_encoder=None)
13+
/**
14+
* Gets a call to `jwt.encode`.
15+
*
16+
* Given the following example:
17+
*
18+
* ```py
19+
* jwt.encode(token, "key", "HS256")
20+
* ```
21+
*
22+
* * `this` would be `jwt.encode(token, "key", "HS256")`.
23+
* * `getPayload()`'s result would be `token`.
24+
* * `getKey()`'s result would be `"key"`.
25+
* * `getAlgorithm()`'s result would be `"HS256"`.
26+
* * `getAlgorithmstring()`'s result would be `HS256`.
27+
*/
1428
private class PyJWTEncodeCall extends DataFlow::CallCfgNode, JWTEncoding::Range {
29+
// def encode(self, payload, key, algorithm="HS256", headers=None, json_encoder=None)
1530
PyJWTEncodeCall() { this = pyjwtEncode().getACall() }
1631

1732
override DataFlow::Node getPayload() {
@@ -32,8 +47,25 @@ private module PyJWT {
3247
}
3348
}
3449

35-
// def decode(self, jwt, key="", algorithms=None, options=None)
50+
/**
51+
* Gets a call to `jwt.decode`.
52+
*
53+
* Given the following example:
54+
*
55+
* ```py
56+
* jwt.decode(token, key, "HS256", options={"verify_signature": True})
57+
* ```
58+
*
59+
* * `this` would be `jwt.decode(token, key, options={"verify_signature": True})`.
60+
* * `getPayload()`'s result would be `token`.
61+
* * `getKey()`'s result would be `key`.
62+
* * `getAlgorithm()`'s result would be `"HS256"`.
63+
* * `getAlgorithmstring()`'s result would be `HS256`.
64+
* * `getOptions()`'s result would be `{"verify_signature": True}`.
65+
* * `verifiesSignature()` predicate would succeed.
66+
*/
3667
private class PyJWTDecodeCall extends DataFlow::CallCfgNode, JWTDecoding::Range {
68+
// def decode(self, jwt, key="", algorithms=None, options=None)
3769
PyJWTDecodeCall() { this = pyjwtDecode().getACall() }
3870

3971
override DataFlow::Node getPayload() { result in [this.getArg(0), this.getArgByName("jwt")] }

python/ql/src/experimental/semmle/python/libraries/PythonJose.qll

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,23 @@ private module PythonJose {
1313
/** Gets a reference to `jwt.decode` */
1414
private API::Node joseJWTDecode() { result = joseJWT().getMember("decode") }
1515

16-
// def encode(claims, key, algorithm=ALGORITHMS.HS256, headers=None, access_token=None):
16+
/**
17+
* Gets a call to `jwt.encode`.
18+
*
19+
* Given the following example:
20+
*
21+
* ```py
22+
* jwt.encode(token, key="key", algorithm="HS256")
23+
* ```
24+
*
25+
* * `this` would be `jwt.encode(token, key="key", algorithm="HS256")`.
26+
* * `getPayload()`'s result would be `token`.
27+
* * `getKey()`'s result would be `"key"`.
28+
* * `getAlgorithm()`'s result would be `"HS256"`.
29+
* * `getAlgorithmstring()`'s result would be `HS256`.
30+
*/
1731
private class JoseJWTEncodeCall extends DataFlow::CallCfgNode, JWTEncoding::Range {
32+
// def encode(claims, key, algorithm=ALGORITHMS.HS256, headers=None, access_token=None):
1833
JoseJWTEncodeCall() { this = joseJWTEncode().getACall() }
1934

2035
override DataFlow::Node getPayload() { result = this.getArg(0) }
@@ -33,8 +48,25 @@ private module PythonJose {
3348
}
3449
}
3550

36-
// def decode(token, key, algorithms=None, options=None, audience=None, issuer=None, subject=None, access_token=None):
51+
/**
52+
* Gets a call to `jwt.decode`.
53+
*
54+
* Given the following example:
55+
*
56+
* ```py
57+
* jwt.decode(token, "key", "HS256")
58+
* ```
59+
*
60+
* * `this` would be `jwt.decode(token, "key", "HS256")`.
61+
* * `getPayload()`'s result would be `token`.
62+
* * `getKey()`'s result would be `"key"`.
63+
* * `getAlgorithm()`'s result would be `"HS256"`.
64+
* * `getAlgorithmstring()`'s result would be `HS256`.
65+
* * `getOptions()`'s result would be none.
66+
* * `verifiesSignature()` predicate would succeed.
67+
*/
3768
private class JoseJWTDecodeCall extends DataFlow::CallCfgNode, JWTDecoding::Range {
69+
// def decode(token, key, algorithms=None, options=None, audience=None, issuer=None, subject=None, access_token=None):
3870
JoseJWTDecodeCall() { this = joseJWTDecode().getACall() }
3971

4072
override DataFlow::Node getPayload() { result = this.getArg(0) }

0 commit comments

Comments
 (0)