Date: Wed, 17 Aug 2022 12:19:02 +0100
Subject: [PATCH 119/631] fix qhelp
---
.../TimingAttackAgainstHash/TimingAttackAgainstHash.qhelp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.qhelp b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.qhelp
index 2602a5a0cca..5f1b1feb8e3 100644
--- a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.qhelp
+++ b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.qhelp
@@ -28,7 +28,7 @@ and does not depend on the contents of the arrays.
The following example uses == which is a fail fast check for validating a Hash.
-
+
The next example use a safe constant-time algorithm for validating a Hash:
From 01828936e21de6f20766735291eb13bf1299e4fa Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Wed, 17 Aug 2022 12:19:44 +0100
Subject: [PATCH 120/631] fix qhelp
---
.../TimingAttackAgainstSensitiveInfo.qhelp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstSensitiveInfo/TimingAttackAgainstSensitiveInfo.qhelp b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstSensitiveInfo/TimingAttackAgainstSensitiveInfo.qhelp
index 683af727470..dd6d2de2573 100644
--- a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstSensitiveInfo/TimingAttackAgainstSensitiveInfo.qhelp
+++ b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstSensitiveInfo/TimingAttackAgainstSensitiveInfo.qhelp
@@ -29,7 +29,7 @@ and does not depend on the contents of the arrays.
The following example uses == which is a fail fast check for validating a secret.
-
+
The next example use a safe constant-time algorithm for validating a secret:
From 5daeea7aebe2e1abf7085a073b2efe5709a328cc Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Wed, 17 Aug 2022 12:24:40 +0100
Subject: [PATCH 121/631] Adjust the @id property
---
.../TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql
index 6c757a91ec6..6659d562633 100644
--- a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql
+++ b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql
@@ -7,7 +7,7 @@
* @kind path-problem
* @problem.severity error
* @precision medium
- * @id py/timing-attack-against-Hash
+ * @id py/timing-attack-against-hash
* @tags security
* external/cwe/cwe-208
*/
From b29ca57ce19a1cbaa825ecd2dbc3cf7d9e266af3 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Wed, 17 Aug 2022 12:46:57 +0100
Subject: [PATCH 122/631] Autoformat TimingAttack.qll
---
.../src/experimental/semmle/python/security/TimingAttack.qll | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
index 9f08e873e78..981dc46dcc6 100644
--- a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
+++ b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
@@ -51,9 +51,7 @@ private predicate existsFailFastCheck(Expr firstInput, Expr secondInput) {
class NonConstantTimeComparisonSink extends DataFlow::Node {
Expr anotherParameter;
- NonConstantTimeComparisonSink() {
- existsFailFastCheck(this.asExpr(), anotherParameter)
- }
+ NonConstantTimeComparisonSink() { existsFailFastCheck(this.asExpr(), anotherParameter) }
/** Holds if remote user input was used in the comparison. */
predicate includesUserInput() {
@@ -195,3 +193,4 @@ class UserInputInComparisonConfig extends TaintTracking2::Configuration {
)
}
}
+
From 9b4d1789b1e3f3696c7ccf409a78e67c2c8503bb Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Wed, 17 Aug 2022 12:49:58 +0100
Subject: [PATCH 123/631] Autoformat TimingAttackAgainstHash.ql
---
.../TimingAttackAgainstHash/TimingAttackAgainstHash.ql | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.ql b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.ql
index b2266f21095..e4db280b581 100644
--- a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.ql
+++ b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.ql
@@ -31,6 +31,9 @@ class TimingAttackAgainsthash extends TaintTracking::Configuration {
}
from TimingAttackAgainsthash config, DataFlow::PathNode source, DataFlow::PathNode sink
-where config.hasFlowPath(source, sink) and sink.getNode().(NonConstantTimeComparisonSink).includesUserInput()
+where
+ config.hasFlowPath(source, sink) and
+ sink.getNode().(NonConstantTimeComparisonSink).includesUserInput()
select sink.getNode(), source, sink, "Timing attack against $@ validation.", source,
source.getNode()
+
From a7dcf96f553c7719d7c80ae3894bec18eea9b758 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Wed, 17 Aug 2022 12:53:07 +0100
Subject: [PATCH 124/631] Autoformat TimingAttackAgainstSensitiveInfo.ql
---
.../TimingAttackAgainstSensitiveInfo.ql | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstSensitiveInfo/TimingAttackAgainstSensitiveInfo.ql b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstSensitiveInfo/TimingAttackAgainstSensitiveInfo.ql
index 9e7b0a82be5..ca24a35fed2 100644
--- a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstSensitiveInfo/TimingAttackAgainstSensitiveInfo.ql
+++ b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstSensitiveInfo/TimingAttackAgainstSensitiveInfo.ql
@@ -28,7 +28,7 @@ class ClientSuppliedSecretConfig extends TaintTracking::Configuration {
}
from ClientSuppliedSecretConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
-where
+where
config.hasFlowPath(source, sink) and
(
source.getNode().(SecretSource).includesUserInput() or
@@ -36,3 +36,4 @@ where
)
select sink.getNode(), source, sink, "Timing attack against $@ validation.", source.getNode(),
"client-supplied token"
+
From cb5331bdd8c62ffa05a4e82a9c83ba77a5024421 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Wed, 17 Aug 2022 12:54:34 +0100
Subject: [PATCH 125/631] Autoformat TimingAttackAgainstHeaderValue.ql
---
.../TimingAttackAgainstHeaderValue.ql | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHeaderValue/TimingAttackAgainstHeaderValue.ql b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHeaderValue/TimingAttackAgainstHeaderValue.ql
index 00a64744aef..bcf9369ee37 100644
--- a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHeaderValue/TimingAttackAgainstHeaderValue.ql
+++ b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHeaderValue/TimingAttackAgainstHeaderValue.ql
@@ -24,12 +24,12 @@ class ClientSuppliedSecretConfig extends TaintTracking::Configuration {
override predicate isSource(DataFlow::Node source) { source instanceof ClientSuppliedsecret }
- override predicate isSink(DataFlow::Node sink) {
+ override predicate isSink(DataFlow::Node sink) {
exists(Compare cmp, Expr left, Expr right, Cmpop cmpop |
cmpop.getSymbol() = ["==", "in", "is not", "!="] and
cmp.compares(left, cmpop, right) and
sink.asExpr() = [left, right]
- )
+ )
}
}
@@ -37,3 +37,4 @@ from ClientSuppliedSecretConfig config, DataFlow::PathNode source, DataFlow::Pat
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "Timing attack against $@ validation.", source.getNode(),
"client-supplied token"
+
From 9f3de035c7e3ecefc14dd4d3fcdcef7951365de7 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Wed, 17 Aug 2022 12:57:57 +0100
Subject: [PATCH 126/631] Autoformat PossibleTimingAttackAgainstHash.ql
---
.../TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql
index 6659d562633..8c8c9938e15 100644
--- a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql
+++ b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql
@@ -15,7 +15,6 @@
import python
import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.TaintTracking
-import semmle.python.ApiGraphs
import experimental.semmle.python.security.TimingAttack
import DataFlow::PathGraph
@@ -35,3 +34,4 @@ from PossibleTimingAttackAgainstHash config, DataFlow::PathNode source, DataFlow
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "Possible Timing attack against $@ validation.", source,
source.getNode()
+
From 313dbc9120a702b19db4cfafb4279e4951f56030 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Wed, 17 Aug 2022 12:59:09 +0100
Subject: [PATCH 127/631] Autoformat
PossibleTimingAttackAgainstSensitiveInfo.ql
---
.../PossibleTimingAttackAgainstSensitiveInfo.ql | 1 +
1 file changed, 1 insertion(+)
diff --git a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstSensitiveInfo/PossibleTimingAttackAgainstSensitiveInfo.ql b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstSensitiveInfo/PossibleTimingAttackAgainstSensitiveInfo.ql
index e4dcda45093..43b49d205be 100644
--- a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstSensitiveInfo/PossibleTimingAttackAgainstSensitiveInfo.ql
+++ b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstSensitiveInfo/PossibleTimingAttackAgainstSensitiveInfo.ql
@@ -31,3 +31,4 @@ from ClientSuppliedSecretConfig config, DataFlow::PathNode source, DataFlow::Pat
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "Timing attack against $@ validation.", source.getNode(),
"client-supplied token"
+
From 76de2f42037bc632a96c4820ff5ebf1dbd1a6ccb Mon Sep 17 00:00:00 2001
From: Taus
Date: Wed, 17 Aug 2022 15:12:51 +0000
Subject: [PATCH 128/631] Python: Remove trailing newlines
These were causing the autoformatting check to fail. I'm not really sure
how these newlines got introduced. Possibly some editor option or
`git-commit` hook?
---
.../TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql | 1 -
.../CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.ql | 1 -
.../TimingAttackAgainstHeaderValue.ql | 1 -
.../PossibleTimingAttackAgainstSensitiveInfo.ql | 1 -
.../TimingAttackAgainstSensitiveInfo.ql | 1 -
.../ql/src/experimental/semmle/python/security/TimingAttack.qll | 1 -
6 files changed, 6 deletions(-)
diff --git a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql
index 8c8c9938e15..3abd61836a8 100644
--- a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql
+++ b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql
@@ -34,4 +34,3 @@ from PossibleTimingAttackAgainstHash config, DataFlow::PathNode source, DataFlow
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "Possible Timing attack against $@ validation.", source,
source.getNode()
-
diff --git a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.ql b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.ql
index e4db280b581..8f098cec409 100644
--- a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.ql
+++ b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.ql
@@ -36,4 +36,3 @@ where
sink.getNode().(NonConstantTimeComparisonSink).includesUserInput()
select sink.getNode(), source, sink, "Timing attack against $@ validation.", source,
source.getNode()
-
diff --git a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHeaderValue/TimingAttackAgainstHeaderValue.ql b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHeaderValue/TimingAttackAgainstHeaderValue.ql
index bcf9369ee37..e2986100bbe 100644
--- a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHeaderValue/TimingAttackAgainstHeaderValue.ql
+++ b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHeaderValue/TimingAttackAgainstHeaderValue.ql
@@ -37,4 +37,3 @@ from ClientSuppliedSecretConfig config, DataFlow::PathNode source, DataFlow::Pat
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "Timing attack against $@ validation.", source.getNode(),
"client-supplied token"
-
diff --git a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstSensitiveInfo/PossibleTimingAttackAgainstSensitiveInfo.ql b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstSensitiveInfo/PossibleTimingAttackAgainstSensitiveInfo.ql
index 43b49d205be..e4dcda45093 100644
--- a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstSensitiveInfo/PossibleTimingAttackAgainstSensitiveInfo.ql
+++ b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstSensitiveInfo/PossibleTimingAttackAgainstSensitiveInfo.ql
@@ -31,4 +31,3 @@ from ClientSuppliedSecretConfig config, DataFlow::PathNode source, DataFlow::Pat
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "Timing attack against $@ validation.", source.getNode(),
"client-supplied token"
-
diff --git a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstSensitiveInfo/TimingAttackAgainstSensitiveInfo.ql b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstSensitiveInfo/TimingAttackAgainstSensitiveInfo.ql
index ca24a35fed2..2f6165753fc 100644
--- a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstSensitiveInfo/TimingAttackAgainstSensitiveInfo.ql
+++ b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstSensitiveInfo/TimingAttackAgainstSensitiveInfo.ql
@@ -36,4 +36,3 @@ where
)
select sink.getNode(), source, sink, "Timing attack against $@ validation.", source.getNode(),
"client-supplied token"
-
diff --git a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
index 981dc46dcc6..df313b712e3 100644
--- a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
+++ b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
@@ -193,4 +193,3 @@ class UserInputInComparisonConfig extends TaintTracking2::Configuration {
)
}
}
-
From 3d17989107f44958d60b52413b3f966bba20e5eb Mon Sep 17 00:00:00 2001
From: Taus
Date: Wed, 17 Aug 2022 17:09:18 +0000
Subject: [PATCH 129/631] Python: Fix broken `select` statement
Based on the alert description, "message" seemed like a suitable word to
use here.
---
.../PossibleTimingAttackAgainstHash.ql | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql
index 3abd61836a8..f8e791c96ba 100644
--- a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql
+++ b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql
@@ -32,5 +32,5 @@ class PossibleTimingAttackAgainstHash extends TaintTracking::Configuration {
from PossibleTimingAttackAgainstHash config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
-select sink.getNode(), source, sink, "Possible Timing attack against $@ validation.", source,
- source.getNode()
+select sink.getNode(), source, sink, "Possible Timing attack against $@ validation.",
+ source.getNode(), "message"
From 687cd929032cf66c7ec05f970b2e5ced284af237 Mon Sep 17 00:00:00 2001
From: Taus
Date: Fri, 19 Aug 2022 11:43:57 +0000
Subject: [PATCH 130/631] Python: Update `.expected` file
---
.../PossibleTimingAttackAgainstHash.expected | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/ql/test/experimental/query-tests/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.expected b/python/ql/test/experimental/query-tests/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.expected
index 8fdcb13b9b7..dabc3f76a56 100644
--- a/python/ql/test/experimental/query-tests/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.expected
+++ b/python/ql/test/experimental/query-tests/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.expected
@@ -5,4 +5,4 @@ nodes
| TimingAttackAgainstHash.py:18:19:18:48 | ControlFlowNode for sign() | semmle.label | ControlFlowNode for sign() |
subpaths
#select
-| TimingAttackAgainstHash.py:18:19:18:48 | ControlFlowNode for sign() | TimingAttackAgainstHash.py:12:12:12:47 | ControlFlowNode for Attribute() | TimingAttackAgainstHash.py:18:19:18:48 | ControlFlowNode for sign() | Possible Timing attack against $@ validation. | TimingAttackAgainstHash.py:12:12:12:47 | ControlFlowNode for Attribute() | TimingAttackAgainstHash.py:12:12:12:47 | ControlFlowNode for Attribute() |
+| TimingAttackAgainstHash.py:18:19:18:48 | ControlFlowNode for sign() | TimingAttackAgainstHash.py:12:12:12:47 | ControlFlowNode for Attribute() | TimingAttackAgainstHash.py:18:19:18:48 | ControlFlowNode for sign() | Possible Timing attack against $@ validation. | TimingAttackAgainstHash.py:12:12:12:47 | ControlFlowNode for Attribute() | message |
From bd45ea94d0b3bf3659e63578135f00f7905aadfe Mon Sep 17 00:00:00 2001
From: Taus
Date: Fri, 19 Aug 2022 12:31:12 +0000
Subject: [PATCH 131/631] Python: Fix `TimingAttackAgainstHash.ql` select
---
.../TimingAttackAgainstHash/TimingAttackAgainstHash.ql | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.ql b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.ql
index 8f098cec409..c0ebdc285e8 100644
--- a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.ql
+++ b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.ql
@@ -34,5 +34,5 @@ from TimingAttackAgainsthash config, DataFlow::PathNode source, DataFlow::PathNo
where
config.hasFlowPath(source, sink) and
sink.getNode().(NonConstantTimeComparisonSink).includesUserInput()
-select sink.getNode(), source, sink, "Timing attack against $@ validation.", source,
- source.getNode()
+select sink.getNode(), source, sink, "Timing attack against $@ validation.", source.getNode(),
+ "message"
From ee05e2ca761286e12a08ff718dfaee88a9c01b60 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Tue, 23 Aug 2022 12:27:20 +0100
Subject: [PATCH 132/631] add `x-gitlab-token` to sensitive headers
---
.../ql/src/experimental/semmle/python/security/TimingAttack.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
index df313b712e3..04687d3caa1 100644
--- a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
+++ b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
@@ -162,7 +162,7 @@ private string sensitiveheaders() {
result =
[
"x-auth-token", "x-csrf-token", "http_x_csrf_token", "x-csrf-param", "x-csrf-header",
- "http_x_csrf_token", "x-api-key", "authorization", "proxy-authorization"
+ "http_x_csrf_token", "x-api-key", "authorization", "proxy-authorization", "x-gitlab-token"
]
}
From 93257be9134cf938c1871c8cb902be71845c72eb Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Tue, 23 Aug 2022 12:51:48 +0100
Subject: [PATCH 133/631] Add Werkzeug source
---
.../semmle/python/security/TimingAttack.qll | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
index 04687d3caa1..4b64bd12f5b 100644
--- a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
+++ b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
@@ -157,6 +157,25 @@ private class TornadoClientSuppliedsecret extends ClientSuppliedsecret {
}
}
+private class WerkzeugClientSuppliedsecret extends ClientSuppliedsecret {
+ WerkzeugClientSuppliedsecret() {
+ exists(RemoteFlowSource rfs, DataFlow::AttrRead get |
+ rfs.getSourceType() = "werkzeug.datastructures" and this.getFunction() = get
+ |
+ // `get` is a call to datastructures.headers.get or datastructures.headers.get_all or datastructures.headers.getlist
+ // datastructures.headers
+ get.getObject()
+ .(DataFlow::AttrRead)
+ // request
+ .getObject()
+ .getALocalSource() = rfs and
+ get.getAttributeName() in ["get", "get_all", "getlist"] and
+ get.getObject().(DataFlow::AttrRead).getAttributeName() = "Headers" and
+ this.getArg(0).asExpr().(StrConst).getText().toLowerCase() = sensitiveheaders()
+ )
+ }
+}
+
/** A string for `match` that identifies strings that look like they represent Sensitive Headers. */
private string sensitiveheaders() {
result =
From 66fb420d00af685e9933d17275276122873ae630 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Mon, 29 Aug 2022 18:08:09 +0100
Subject: [PATCH 134/631] Update PossibleTimingAttackAgainstHash.ql
---
.../TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql
index f8e791c96ba..71e82bd715f 100644
--- a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql
+++ b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql
@@ -7,7 +7,7 @@
* @kind path-problem
* @problem.severity error
* @precision medium
- * @id py/timing-attack-against-hash
+ * @id py/possible-timing-attack-against-hash
* @tags security
* external/cwe/cwe-208
*/
From 199e3d94620270d625aecf3b37c42c7f7db8ba6b Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Mon, 29 Aug 2022 18:13:45 +0100
Subject: [PATCH 135/631] Rename the query ID
---
.../PossibleTimingAttackAgainstSensitiveInfo.ql | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstSensitiveInfo/PossibleTimingAttackAgainstSensitiveInfo.ql b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstSensitiveInfo/PossibleTimingAttackAgainstSensitiveInfo.ql
index e4dcda45093..47173999a91 100644
--- a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstSensitiveInfo/PossibleTimingAttackAgainstSensitiveInfo.ql
+++ b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstSensitiveInfo/PossibleTimingAttackAgainstSensitiveInfo.ql
@@ -5,7 +5,7 @@
* @kind path-problem
* @problem.severity error
* @precision medium
- * @id py/timing-attack-sensitive-info
+ * @id py/possible-timing-attack-sensitive-info
* @tags security
* external/cwe/cwe-208
*/
From 141b65fea85c3d03ea3e60affeb3d6955c6229cf Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Mon, 29 Aug 2022 18:18:19 +0100
Subject: [PATCH 136/631] Fix typo
---
.../TimingAttackAgainstHash/TimingAttackAgainstHash.qhelp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.qhelp b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.qhelp
index 5f1b1feb8e3..2dfe5296167 100644
--- a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.qhelp
+++ b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.qhelp
@@ -6,7 +6,7 @@
Timing Attack is based on the leakage of information by studying how long it takes the system to respond to different inputs.
it can be circumvented by using a constant-time algorithm for checking the value of Hash,
more precisely, the comparison time should not depend on the content of the input. Otherwise the attacker gains
-information that is indirectly leaked by the application. This information is then used for malicious purposes.
+information that is indirectly leaked by the application. This information may then be used for malicious purposes.
From baa0fd41480fc47c8965270fc3ae889fc2004960 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Mon, 29 Aug 2022 18:25:26 +0100
Subject: [PATCH 137/631] Convert `%UserPass%` word to lowercase
---
.../ql/src/experimental/semmle/python/security/TimingAttack.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
index 4b64bd12f5b..8c49392bf1f 100644
--- a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
+++ b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
@@ -78,7 +78,7 @@ private string suspicious() {
result =
[
"%password%", "%passwd%", "%pwd%", "%refresh%token%", "%secret%token", "%secret%key",
- "%passcode%", "%passphrase%", "%token%", "%secret%", "%credential%", "%UserPass%"
+ "%passcode%", "%passphrase%", "%token%", "%secret%", "%credential%", "%userpass%"
]
}
From b2551a5581db5286ac6512ee3eb325c0a3151613 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Mon, 29 Aug 2022 18:30:43 +0100
Subject: [PATCH 138/631] Update the name of the class (and its subclasses)
---
.../semmle/python/security/TimingAttack.qll | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
index 8c49392bf1f..3c76edd1482 100644
--- a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
+++ b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
@@ -95,9 +95,9 @@ class CredentialExpr extends Expr {
*
* For example: `request.headers.get("X-Auth-Token")`.
*/
-abstract class ClientSuppliedsecret extends DataFlow::CallCfgNode { }
+abstract class ClientSuppliedSecret extends DataFlow::CallCfgNode { }
-private class FlaskClientSuppliedsecret extends ClientSuppliedsecret {
+private class FlaskClientSuppliedSecret extends ClientSuppliedSecret {
FlaskClientSuppliedsecret() {
exists(RemoteFlowSource rfs, DataFlow::AttrRead get |
rfs.getSourceType() = "flask.request" and this.getFunction() = get
@@ -116,7 +116,7 @@ private class FlaskClientSuppliedsecret extends ClientSuppliedsecret {
}
}
-private class DjangoClientSuppliedsecret extends ClientSuppliedsecret {
+private class DjangoClientSuppliedSecret extends ClientSuppliedSecret {
DjangoClientSuppliedsecret() {
exists(RemoteFlowSource rfs, DataFlow::AttrRead get |
rfs.getSourceType() = "django.http.request.HttpRequest" and this.getFunction() = get
@@ -135,7 +135,7 @@ private class DjangoClientSuppliedsecret extends ClientSuppliedsecret {
}
}
-private class TornadoClientSuppliedsecret extends ClientSuppliedsecret {
+private class TornadoClientSuppliedSecret extends ClientSuppliedSecret {
TornadoClientSuppliedsecret() {
exists(RemoteFlowSource rfs, DataFlow::AttrRead get |
rfs.getSourceType() = "tornado.web.RequestHandler" and this.getFunction() = get
@@ -157,7 +157,7 @@ private class TornadoClientSuppliedsecret extends ClientSuppliedsecret {
}
}
-private class WerkzeugClientSuppliedsecret extends ClientSuppliedsecret {
+private class WerkzeugClientSuppliedSecret extends ClientSuppliedSecret {
WerkzeugClientSuppliedsecret() {
exists(RemoteFlowSource rfs, DataFlow::AttrRead get |
rfs.getSourceType() = "werkzeug.datastructures" and this.getFunction() = get
From 9995e91bb716868d49b2956edc1aa97579508880 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Mon, 29 Aug 2022 18:57:56 +0100
Subject: [PATCH 139/631] Update the name of the class (and its subclasses)
---
.../experimental/semmle/python/security/TimingAttack.qll | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
index 3c76edd1482..3500974aeaa 100644
--- a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
+++ b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
@@ -98,7 +98,7 @@ class CredentialExpr extends Expr {
abstract class ClientSuppliedSecret extends DataFlow::CallCfgNode { }
private class FlaskClientSuppliedSecret extends ClientSuppliedSecret {
- FlaskClientSuppliedsecret() {
+ FlaskClientSuppliedSecret() {
exists(RemoteFlowSource rfs, DataFlow::AttrRead get |
rfs.getSourceType() = "flask.request" and this.getFunction() = get
|
@@ -117,7 +117,7 @@ private class FlaskClientSuppliedSecret extends ClientSuppliedSecret {
}
private class DjangoClientSuppliedSecret extends ClientSuppliedSecret {
- DjangoClientSuppliedsecret() {
+ DjangoClientSuppliedSecret() {
exists(RemoteFlowSource rfs, DataFlow::AttrRead get |
rfs.getSourceType() = "django.http.request.HttpRequest" and this.getFunction() = get
|
@@ -136,7 +136,7 @@ private class DjangoClientSuppliedSecret extends ClientSuppliedSecret {
}
private class TornadoClientSuppliedSecret extends ClientSuppliedSecret {
- TornadoClientSuppliedsecret() {
+ TornadoClientSuppliedSecret() {
exists(RemoteFlowSource rfs, DataFlow::AttrRead get |
rfs.getSourceType() = "tornado.web.RequestHandler" and this.getFunction() = get
|
@@ -158,7 +158,7 @@ private class TornadoClientSuppliedSecret extends ClientSuppliedSecret {
}
private class WerkzeugClientSuppliedSecret extends ClientSuppliedSecret {
- WerkzeugClientSuppliedsecret() {
+ WerkzeugClientSuppliedSecret() {
exists(RemoteFlowSource rfs, DataFlow::AttrRead get |
rfs.getSourceType() = "werkzeug.datastructures" and this.getFunction() = get
|
From 0177cd810eb70de8b28f965bceda4b91bf6db7f1 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Tue, 30 Aug 2022 13:58:54 +0100
Subject: [PATCH 140/631] Update `suspicious()`
---
.../src/experimental/semmle/python/security/TimingAttack.qll | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
index 3500974aeaa..467873cb1e7 100644
--- a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
+++ b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
@@ -78,7 +78,8 @@ private string suspicious() {
result =
[
"%password%", "%passwd%", "%pwd%", "%refresh%token%", "%secret%token", "%secret%key",
- "%passcode%", "%passphrase%", "%token%", "%secret%", "%credential%", "%userpass%"
+ "%passcode%", "%passphrase%", "%token%", "%secret%", "%credential%", "%userpass%",
+ "%digest%", "%signature%", "%mac%"
]
}
From de58d0f0247f01fcafc9328afe0b7e92276f4479 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Tue, 30 Aug 2022 16:34:43 +0100
Subject: [PATCH 141/631] Update the subclasses of `ClientSuppliedSecret` class
---
.../semmle/python/security/TimingAttack.qll | 84 ++++++-------------
1 file changed, 24 insertions(+), 60 deletions(-)
diff --git a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
index 467873cb1e7..1065e27915c 100644
--- a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
+++ b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
@@ -96,84 +96,48 @@ class CredentialExpr extends Expr {
*
* For example: `request.headers.get("X-Auth-Token")`.
*/
-abstract class ClientSuppliedSecret extends DataFlow::CallCfgNode { }
+abstract class ClientSuppliedSecret extends API::CallNode { }
private class FlaskClientSuppliedSecret extends ClientSuppliedSecret {
FlaskClientSuppliedSecret() {
- exists(RemoteFlowSource rfs, DataFlow::AttrRead get |
- rfs.getSourceType() = "flask.request" and this.getFunction() = get
- |
- // `get` is a call to request.headers.get or request.headers.get_all or request.headers.getlist
- // request.headers
- get.getObject()
- .(DataFlow::AttrRead)
- // request
- .getObject()
- .getALocalSource() = rfs and
- get.getAttributeName() in ["get", "get_all", "getlist"] and
- get.getObject().(DataFlow::AttrRead).getAttributeName() = "headers" and
- this.getArg(0).asExpr().(StrConst).getText().toLowerCase() = sensitiveheaders()
- )
+ this = Flask::request().getMember("headers").getMember(["get", "get_all", "getlist"]).getACall() and
+ this.getParameter(0, ["key", "name"]).toString().toLowerCase() = sensitiveheaders()
}
}
private class DjangoClientSuppliedSecret extends ClientSuppliedSecret {
DjangoClientSuppliedSecret() {
- exists(RemoteFlowSource rfs, DataFlow::AttrRead get |
- rfs.getSourceType() = "django.http.request.HttpRequest" and this.getFunction() = get
- |
- // `get` is a call to request.headers.get or request.META.get
- // request.headers
- get.getObject()
- .(DataFlow::AttrRead)
- // request
- .getObject()
- .getALocalSource() = rfs and
- get.getAttributeName() = "get" and
- get.getObject().(DataFlow::AttrRead).getAttributeName() in ["headers", "META"] and
- this.getArg(0).asExpr().(StrConst).getText().toLowerCase() = sensitiveheaders()
- )
+ this =
+ PrivateDjango::DjangoImpl::Http::Request::HttpRequest::classRef()
+ .getMember(["headers", "META"])
+ .getMember("get")
+ .getACall() and
+ this.getParameter(0, "key").toString().toLowerCase() = sensitiveheaders()
}
}
+/** Gets a reference to the `tornado.web.RequestHandler` module. */
+API::Node requesthandler() {
+ result = API::moduleImport("tornado").getMember("web").getMember("RequestHandler")
+}
+
private class TornadoClientSuppliedSecret extends ClientSuppliedSecret {
TornadoClientSuppliedSecret() {
- exists(RemoteFlowSource rfs, DataFlow::AttrRead get |
- rfs.getSourceType() = "tornado.web.RequestHandler" and this.getFunction() = get
- |
- // `get` is a call to `rfs`.request.headers.get
- // `rfs`.request.headers
- get.getObject()
- .(DataFlow::AttrRead)
- // `rfs`.request
- .getObject()
- .(DataFlow::AttrRead)
- // `rfs`
- .getObject()
- .getALocalSource() = rfs and
- get.getAttributeName() in ["get", "get_list"] and
- get.getObject().(DataFlow::AttrRead).getAttributeName() = "headers" and
- this.getArg(0).asExpr().(StrConst).getText().toLowerCase() = sensitiveheaders()
- )
+ this = requesthandler().getMember(["headers", "META"]).getMember("get").getACall() and
+ this.getParameter(0, "key").toString().toLowerCase() = sensitiveheaders()
}
}
+/** Gets a reference to the `werkzeug.datastructures.Headers` module. */
+API::Node headers() {
+ result = API::moduleImport("werkzeug").getMember("datastructures").getMember("Headers")
+}
+
private class WerkzeugClientSuppliedSecret extends ClientSuppliedSecret {
WerkzeugClientSuppliedSecret() {
- exists(RemoteFlowSource rfs, DataFlow::AttrRead get |
- rfs.getSourceType() = "werkzeug.datastructures" and this.getFunction() = get
- |
- // `get` is a call to datastructures.headers.get or datastructures.headers.get_all or datastructures.headers.getlist
- // datastructures.headers
- get.getObject()
- .(DataFlow::AttrRead)
- // request
- .getObject()
- .getALocalSource() = rfs and
- get.getAttributeName() in ["get", "get_all", "getlist"] and
- get.getObject().(DataFlow::AttrRead).getAttributeName() = "Headers" and
- this.getArg(0).asExpr().(StrConst).getText().toLowerCase() = sensitiveheaders()
- )
+ this =
+ headers().getMember(["headers", "META"]).getMember(["get", "get_all", "getlist"]).getACall() and
+ this.getParameter(0, ["key", "name"]).toString().toLowerCase() = sensitiveheaders()
}
}
From 23f268f3b9285b1a29b8050e7471293d0a864d6d Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Tue, 30 Aug 2022 16:39:40 +0100
Subject: [PATCH 142/631] Import Django and Flask model
---
.../ql/src/experimental/semmle/python/security/TimingAttack.qll | 2 ++
1 file changed, 2 insertions(+)
diff --git a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
index 1065e27915c..8a006d981fc 100644
--- a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
+++ b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
@@ -6,6 +6,8 @@ private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.DataFlow2
private import semmle.python.ApiGraphs
private import semmle.python.dataflow.new.RemoteFlowSources
+private import semmle.python.frameworks.Flask
+private import semmle.python.frameworks.Django
/** A data flow source of the hash obtained */
class ProduceHashCall extends DataFlow::CallCfgNode {
From 133a3c19f04e37cff7c312d828f961b5fcc73631 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Wed, 31 Aug 2022 11:09:24 +0100
Subject: [PATCH 143/631] Add more source of crypto call
---
.../semmle/python/security/TimingAttack.qll | 96 +++++++++++++++++--
1 file changed, 90 insertions(+), 6 deletions(-)
diff --git a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
index 8a006d981fc..75535c8e270 100644
--- a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
+++ b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
@@ -9,9 +9,23 @@ private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.frameworks.Flask
private import semmle.python.frameworks.Django
-/** A data flow source of the hash obtained */
-class ProduceHashCall extends DataFlow::CallCfgNode {
- ProduceHashCall() {
+/** A method call that produces cryptographic result. */
+abstract private class ProduceCryptoCall extends API::CallNode {
+ /** Gets a type of cryptographic operation such as HMAC, signature or ciphertext. */
+ abstract string getResultType();
+}
+
+/** Gets a reference to the `cryptography.hazmat.primitives` module. */
+API::Node cryptographylib() {
+ result = API::moduleImport("cryptography").getMember("hazmat").getMember("primitives")
+}
+
+/** Gets a reference to the `Crypto` module. */
+API::Node cryptodome() { result = API::moduleImport(["Crypto", "Cryptodome"]) }
+
+/** A method call that produces a MAC. */
+class ProduceMacCall extends ProduceCryptoCall {
+ ProduceMacCall() {
this = API::moduleImport("hmac").getMember("digest").getACall() or
this =
API::moduleImport("hmac")
@@ -19,15 +33,85 @@ class ProduceHashCall extends DataFlow::CallCfgNode {
.getReturn()
.getMember(["digest", "hexdigest"])
.getACall() or
+ this =
+ cryptodome()
+ .getMember("Hash")
+ .getMember("HMAC")
+ .getMember(["new", "HMAC"])
+ .getMember(["digest", "hexdigest"])
+ .getACall() or
+ this =
+ cryptographylib()
+ .getMember("hmac")
+ .getMember("HMAC")
+ .getReturn()
+ .getMember("finalize")
+ .getACall() or
+ this =
+ cryptographylib()
+ .getMember("cmac")
+ .getMember("CMAC")
+ .getReturn()
+ .getMember("finalize")
+ .getACall() or
+ this =
+ cryptodome()
+ .getMember("Hash")
+ .getMember("CMAC")
+ .getMember(["new", "CMAC"])
+ .getMember(["digest", "hexdigest"])
+ .getACall()
+ }
+
+ override string getResultType() { result = "MAC" }
+}
+
+/** A method call that produces a signature. */
+private class ProduceSignatureCall extends ProduceCryptoCall {
+ ProduceSignatureCall() {
+ this =
+ cryptodome()
+ .getMember("Signature")
+ .getMember(["DSS", "pkcs1_15", "pss", "eddsa"])
+ .getMember("new")
+ .getReturn()
+ .getMember("sign")
+ .getACall()
+ }
+
+ override string getResultType() { result = "signature" }
+}
+
+private string hashalgo() {
+ result = ["sha1", "sha224", "sha256", "sha384", "sha512", "blake2b", "blake2s", "md5"]
+}
+
+/** A method call that produces a Hash. */
+private class ProduceHashCall extends ProduceCryptoCall {
+ ProduceHashCall() {
+ this =
+ cryptographylib()
+ .getMember("hashes")
+ .getMember("Hash")
+ .getReturn()
+ .getMember("finalize")
+ .getACall() or
this =
API::moduleImport("hashlib")
- .getMember([
- "new", "sha1", "sha224", "sha256", "sha384", "sha512", "blake2b", "blake2s", "md5"
- ])
+ .getMember(["new", hashalgo()])
+ .getReturn()
+ .getMember(["digest", "hexdigest"])
+ .getACall() or
+ this =
+ cryptodome()
+ .getMember(hashalgo())
+ .getMember("new")
.getReturn()
.getMember(["digest", "hexdigest"])
.getACall()
}
+
+ override string getResultType() { result = "Hash" }
}
/** A data flow sink for comparison. */
From ca28d79541b04a0493af3bb2f073912343e56146 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Wed, 31 Aug 2022 11:15:39 +0100
Subject: [PATCH 144/631] Prevent crosstalk between the configurations
---
.../src/experimental/semmle/python/security/TimingAttack.qll | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
index 75535c8e270..3f55e3b5c5a 100644
--- a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
+++ b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
@@ -1,7 +1,6 @@
private import python
-private import semmle.python.dataflow.new.TaintTracking
private import semmle.python.dataflow.new.TaintTracking2
-private import semmle.python.dataflow.new.TaintTracking3
+private import semmle.python.dataflow.new.TaintTracking
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.DataFlow2
private import semmle.python.ApiGraphs
@@ -239,7 +238,7 @@ private string sensitiveheaders() {
/**
* A config that tracks data flow from remote user input to Variable that hold sensitive info
*/
-class UserInputSecretConfig extends TaintTracking2::Configuration {
+class UserInputSecretConfig extends TaintTracking::Configuration {
UserInputSecretConfig() { this = "UserInputSecretConfig" }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
From 740bf716cb2654caf43948914efd94355333ddc1 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Wed, 31 Aug 2022 12:22:01 +0100
Subject: [PATCH 145/631] Update TimingAttack.qll
---
.../ql/src/experimental/semmle/python/security/TimingAttack.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
index 3f55e3b5c5a..fb512405a10 100644
--- a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
+++ b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
@@ -10,7 +10,7 @@ private import semmle.python.frameworks.Django
/** A method call that produces cryptographic result. */
abstract private class ProduceCryptoCall extends API::CallNode {
- /** Gets a type of cryptographic operation such as HMAC, signature or ciphertext. */
+ /** Gets a type of cryptographic operation such as HMAC, signature or Hash. */
abstract string getResultType();
}
From 275ed0d6e57f51208d6f592584c00247aee9b025 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Wed, 31 Aug 2022 12:37:36 +0100
Subject: [PATCH 146/631] Update `select` statement
---
.../TimingAttackAgainstHash/TimingAttackAgainstHash.ql | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.ql b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.ql
index c0ebdc285e8..f60e712cb4f 100644
--- a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.ql
+++ b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.ql
@@ -25,7 +25,7 @@ import DataFlow::PathGraph
class TimingAttackAgainsthash extends TaintTracking::Configuration {
TimingAttackAgainsthash() { this = "TimingAttackAgainsthash" }
- override predicate isSource(DataFlow::Node source) { source instanceof ProduceHashCall }
+ override predicate isSource(DataFlow::Node source) { source instanceof ProduceCryptoCall }
override predicate isSink(DataFlow::Node sink) { sink instanceof NonConstantTimeComparisonSink }
}
@@ -34,5 +34,5 @@ from TimingAttackAgainsthash config, DataFlow::PathNode source, DataFlow::PathNo
where
config.hasFlowPath(source, sink) and
sink.getNode().(NonConstantTimeComparisonSink).includesUserInput()
-select sink.getNode(), source, sink, "Timing attack against $@ validation.", source.getNode(),
- "message"
+select sink.getNode(), source, sink, "Timing attack against $@ validation.",
+ source.getNode().(ProduceCryptoCall).getResultType(), "message"
From f2688c4a021182394748046bbdf51ec9aa205005 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Wed, 31 Aug 2022 12:39:00 +0100
Subject: [PATCH 147/631] Update `select` statement
---
.../PossibleTimingAttackAgainstHash.ql | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql
index 71e82bd715f..0d5809694b3 100644
--- a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql
+++ b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.ql
@@ -25,7 +25,7 @@ import DataFlow::PathGraph
class PossibleTimingAttackAgainstHash extends TaintTracking::Configuration {
PossibleTimingAttackAgainstHash() { this = "PossibleTimingAttackAgainstHash" }
- override predicate isSource(DataFlow::Node source) { source instanceof ProduceHashCall }
+ override predicate isSource(DataFlow::Node source) { source instanceof ProduceCryptoCall }
override predicate isSink(DataFlow::Node sink) { sink instanceof NonConstantTimeComparisonSink }
}
@@ -33,4 +33,4 @@ class PossibleTimingAttackAgainstHash extends TaintTracking::Configuration {
from PossibleTimingAttackAgainstHash config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "Possible Timing attack against $@ validation.",
- source.getNode(), "message"
+ source.getNode().(ProduceCryptoCall).getResultType(), "message"
From 12960fd00f66a59e5e15decfce8492b177b564ec Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Wed, 31 Aug 2022 12:39:46 +0100
Subject: [PATCH 148/631] Update TimingAttack.qll
---
.../ql/src/experimental/semmle/python/security/TimingAttack.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
index fb512405a10..4906b9b22b3 100644
--- a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
+++ b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
@@ -9,7 +9,7 @@ private import semmle.python.frameworks.Flask
private import semmle.python.frameworks.Django
/** A method call that produces cryptographic result. */
-abstract private class ProduceCryptoCall extends API::CallNode {
+abstract class ProduceCryptoCall extends API::CallNode {
/** Gets a type of cryptographic operation such as HMAC, signature or Hash. */
abstract string getResultType();
}
From 13d1a4fdc146cbdbe43397808ae2745da9560bcf Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Wed, 31 Aug 2022 12:46:17 +0100
Subject: [PATCH 149/631] Update TimingAttackAgainstHeaderValue.ql
---
.../TimingAttackAgainstHeaderValue.ql | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHeaderValue/TimingAttackAgainstHeaderValue.ql b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHeaderValue/TimingAttackAgainstHeaderValue.ql
index e2986100bbe..99d870afc83 100644
--- a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHeaderValue/TimingAttackAgainstHeaderValue.ql
+++ b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHeaderValue/TimingAttackAgainstHeaderValue.ql
@@ -22,7 +22,7 @@ import DataFlow::PathGraph
class ClientSuppliedSecretConfig extends TaintTracking::Configuration {
ClientSuppliedSecretConfig() { this = "ClientSuppliedSecretConfig" }
- override predicate isSource(DataFlow::Node source) { source instanceof ClientSuppliedsecret }
+ override predicate isSource(DataFlow::Node source) { source instanceof ClientSuppliedSecret }
override predicate isSink(DataFlow::Node sink) {
exists(Compare cmp, Expr left, Expr right, Cmpop cmpop |
From 56d48e6264796a47bc6d83b742db9228864921d0 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Wed, 31 Aug 2022 15:59:51 +0100
Subject: [PATCH 150/631] Add more tests
---
.../TimingAttackAgainstHash.py | 21 ++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/python/ql/test/experimental/query-tests/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.py b/python/ql/test/experimental/query-tests/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.py
index c7730ef69b2..1d312f028eb 100644
--- a/python/ql/test/experimental/query-tests/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.py
+++ b/python/ql/test/experimental/query-tests/Security/CWE-208/TimingAttackAgainstHash/TimingAttackAgainstHash.py
@@ -5,8 +5,26 @@
"""
import hashlib
import hmac
+from cryptography.hazmat.primitives import cmac
+from cryptography.hazmat.primitives.ciphers import algorithms
+from Crypto.Signature import pkcs1_15
+from Crypto.Hash import SHA256
+from Crypto.PublicKey import RSA
-key = "e179017a-62b0-4996-8a38-e91aa9f1"
+
+def UnsafeCmacCheck(actualCmac):
+ expected = cmac.CMAC(algorithms.AES(key))
+ expected.update(b"message to authenticate")
+ expected.finalize()
+ return actualCmac == expected
+
+
+def UnsafeCheckSignature(expected):
+ message = b'To be signed'
+ key = RSA.import_key(open('private_key.der').read())
+ h = SHA256.new(message)
+ signature = pkcs1_15.new(key).sign(h)
+ return expected == signature
def sign(pre_key, msg, alg):
return hmac.new(pre_key, msg, alg).digest()
@@ -15,6 +33,7 @@ def verifyGood(msg, sig):
return constant_time_string_compare(sig, sign(key, msg, hashlib.sha256)) #good
def verifyBad(msg, sig):
+ key = "e179017a-62b0-4996-8a38-e91aa9f1"
return sig == sign(key, msg, hashlib.sha256) #bad
def constant_time_string_compare(a, b):
From 8153b790ad2ed2228bdd65a19fb1a80bb9647c26 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Wed, 31 Aug 2022 16:01:09 +0100
Subject: [PATCH 151/631] Update test result
---
.../PossibleTimingAttackAgainstHash.expected | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/python/ql/test/experimental/query-tests/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.expected b/python/ql/test/experimental/query-tests/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.expected
index dabc3f76a56..ece6b8d6897 100644
--- a/python/ql/test/experimental/query-tests/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.expected
+++ b/python/ql/test/experimental/query-tests/Security/CWE-208/TimingAttackAgainstHash/PossibleTimingAttackAgainstHash.expected
@@ -1,8 +1,12 @@
edges
-| TimingAttackAgainstHash.py:12:12:12:47 | ControlFlowNode for Attribute() | TimingAttackAgainstHash.py:18:19:18:48 | ControlFlowNode for sign() |
+| TimingAttackAgainstHash.py:26:17:26:41 | ControlFlowNode for Attribute() | TimingAttackAgainstHash.py:27:24:27:32 | ControlFlowNode for signature |
+| TimingAttackAgainstHash.py:30:12:30:47 | ControlFlowNode for Attribute() | TimingAttackAgainstHash.py:37:19:37:48 | ControlFlowNode for sign() |
nodes
-| TimingAttackAgainstHash.py:12:12:12:47 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
-| TimingAttackAgainstHash.py:18:19:18:48 | ControlFlowNode for sign() | semmle.label | ControlFlowNode for sign() |
+| TimingAttackAgainstHash.py:26:17:26:41 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| TimingAttackAgainstHash.py:27:24:27:32 | ControlFlowNode for signature | semmle.label | ControlFlowNode for signature |
+| TimingAttackAgainstHash.py:30:12:30:47 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| TimingAttackAgainstHash.py:37:19:37:48 | ControlFlowNode for sign() | semmle.label | ControlFlowNode for sign() |
subpaths
#select
-| TimingAttackAgainstHash.py:18:19:18:48 | ControlFlowNode for sign() | TimingAttackAgainstHash.py:12:12:12:47 | ControlFlowNode for Attribute() | TimingAttackAgainstHash.py:18:19:18:48 | ControlFlowNode for sign() | Possible Timing attack against $@ validation. | TimingAttackAgainstHash.py:12:12:12:47 | ControlFlowNode for Attribute() | message |
+| TimingAttackAgainstHash.py:27:24:27:32 | ControlFlowNode for signature | TimingAttackAgainstHash.py:26:17:26:41 | ControlFlowNode for Attribute() | TimingAttackAgainstHash.py:27:24:27:32 | ControlFlowNode for signature | Possible Timing attack against $@ validation. | signature | message |
+| TimingAttackAgainstHash.py:37:19:37:48 | ControlFlowNode for sign() | TimingAttackAgainstHash.py:30:12:30:47 | ControlFlowNode for Attribute() | TimingAttackAgainstHash.py:37:19:37:48 | ControlFlowNode for sign() | Possible Timing attack against $@ validation. | MAC | message |
From a42cb20b862b04af6356c758c7b590c231e44d7a Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Wed, 31 Aug 2022 16:07:58 +0100
Subject: [PATCH 152/631] Update TimingAttack.qll
---
.../experimental/semmle/python/security/TimingAttack.qll | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
index 4906b9b22b3..a02583ca52d 100644
--- a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
+++ b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
@@ -186,7 +186,7 @@ abstract class ClientSuppliedSecret extends API::CallNode { }
private class FlaskClientSuppliedSecret extends ClientSuppliedSecret {
FlaskClientSuppliedSecret() {
this = Flask::request().getMember("headers").getMember(["get", "get_all", "getlist"]).getACall() and
- this.getParameter(0, ["key", "name"]).toString().toLowerCase() = sensitiveheaders()
+ this.getParameter(0, "key").asSink().asExpr().(StrConst).getText().toLowerCase() = sensitiveheaders()
}
}
@@ -197,7 +197,7 @@ private class DjangoClientSuppliedSecret extends ClientSuppliedSecret {
.getMember(["headers", "META"])
.getMember("get")
.getACall() and
- this.getParameter(0, "key").toString().toLowerCase() = sensitiveheaders()
+ this.getParameter(0, "key").asSink().asExpr().(StrConst).getText().toLowerCase() = sensitiveheaders()
}
}
@@ -209,7 +209,7 @@ API::Node requesthandler() {
private class TornadoClientSuppliedSecret extends ClientSuppliedSecret {
TornadoClientSuppliedSecret() {
this = requesthandler().getMember(["headers", "META"]).getMember("get").getACall() and
- this.getParameter(0, "key").toString().toLowerCase() = sensitiveheaders()
+ this.getParameter(0, "key").asSink().asExpr().(StrConst).getText().toLowerCase() = sensitiveheaders()
}
}
@@ -222,7 +222,7 @@ private class WerkzeugClientSuppliedSecret extends ClientSuppliedSecret {
WerkzeugClientSuppliedSecret() {
this =
headers().getMember(["headers", "META"]).getMember(["get", "get_all", "getlist"]).getACall() and
- this.getParameter(0, ["key", "name"]).toString().toLowerCase() = sensitiveheaders()
+ this.getParameter(0, "key").asSink().asExpr().(StrConst).getText().toLowerCase() = sensitiveheaders()
}
}
From daff7775ca38f947bf9848a90eb4a305e4a9f9e7 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Wed, 31 Aug 2022 16:09:22 +0100
Subject: [PATCH 153/631] Update TimingAttack.qll
---
.../src/experimental/semmle/python/security/TimingAttack.qll | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
index a02583ca52d..1fe382459d7 100644
--- a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
+++ b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
@@ -186,7 +186,7 @@ abstract class ClientSuppliedSecret extends API::CallNode { }
private class FlaskClientSuppliedSecret extends ClientSuppliedSecret {
FlaskClientSuppliedSecret() {
this = Flask::request().getMember("headers").getMember(["get", "get_all", "getlist"]).getACall() and
- this.getParameter(0, "key").asSink().asExpr().(StrConst).getText().toLowerCase() = sensitiveheaders()
+ this.getParameter(0, ["key", "name"]).asSink().asExpr().(StrConst).getText().toLowerCase() = sensitiveheaders()
}
}
@@ -222,7 +222,7 @@ private class WerkzeugClientSuppliedSecret extends ClientSuppliedSecret {
WerkzeugClientSuppliedSecret() {
this =
headers().getMember(["headers", "META"]).getMember(["get", "get_all", "getlist"]).getACall() and
- this.getParameter(0, "key").asSink().asExpr().(StrConst).getText().toLowerCase() = sensitiveheaders()
+ this.getParameter(0, ["key", "name"]).asSink().asExpr().(StrConst).getText().toLowerCase() = sensitiveheaders()
}
}
From cf83b07aae34d43ebf186bca4205694af14ddfea Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Wed, 31 Aug 2022 17:04:02 +0100
Subject: [PATCH 154/631] Add more source of crypto call
---
.../semmle/python/security/TimingAttack.qll | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
index 1fe382459d7..2b8ec13f301 100644
--- a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
+++ b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
@@ -10,7 +10,7 @@ private import semmle.python.frameworks.Django
/** A method call that produces cryptographic result. */
abstract class ProduceCryptoCall extends API::CallNode {
- /** Gets a type of cryptographic operation such as HMAC, signature or Hash. */
+ /** Gets a type of cryptographic operation such as MAC, signature, Hash or ciphertext. */
abstract string getResultType();
}
@@ -113,6 +113,21 @@ private class ProduceHashCall extends ProduceCryptoCall {
override string getResultType() { result = "Hash" }
}
+/** A method call that produces a ciphertext. */
+private class ProduceCiphertextCall extends ProduceCryptoCall {
+ ProduceCiphertextCall() {
+ this =
+ cryptodome()
+ .getMember("Cipher")
+ .getMember(["DES", "DES3", "ARC2", "ARC4", "Blowfish", "PKCS1_v1_5"])
+ .getMember(["ARC4Cipher", "new", "PKCS115_Cipher"])
+ .getMember("encrypt")
+ .getACall()
+ }
+
+ override string getResultType() { result = "ciphertext" }
+}
+
/** A data flow sink for comparison. */
private predicate existsFailFastCheck(Expr firstInput, Expr secondInput) {
exists(Compare compare |
From 0fd684cde83f81d3c7373551f520a5c9f2c41666 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Wed, 31 Aug 2022 17:13:43 +0100
Subject: [PATCH 155/631] Add more source of crypto call
---
.../experimental/semmle/python/security/TimingAttack.qll | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
index 2b8ec13f301..5db95bf4948 100644
--- a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
+++ b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
@@ -122,6 +122,13 @@ private class ProduceCiphertextCall extends ProduceCryptoCall {
.getMember(["DES", "DES3", "ARC2", "ARC4", "Blowfish", "PKCS1_v1_5"])
.getMember(["ARC4Cipher", "new", "PKCS115_Cipher"])
.getMember("encrypt")
+ .getACall() or
+ this =
+ cryptographylib()
+ .getMember("ciphers")
+ .getMember("Cipher")
+ .getReturn()
+ .getMember("finalize")
.getACall()
}
From a50c226ca94a3c3033e2d7e39859ef53d579278f Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Sat, 3 Sep 2022 12:10:55 +0100
Subject: [PATCH 156/631] Autoformat
---
.../semmle/python/security/TimingAttack.qll | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
index 5db95bf4948..ad739fc42a4 100644
--- a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
+++ b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
@@ -185,8 +185,8 @@ private string suspicious() {
result =
[
"%password%", "%passwd%", "%pwd%", "%refresh%token%", "%secret%token", "%secret%key",
- "%passcode%", "%passphrase%", "%token%", "%secret%", "%credential%", "%userpass%",
- "%digest%", "%signature%", "%mac%"
+ "%passcode%", "%passphrase%", "%token%", "%secret%", "%credential%", "%userpass%", "%digest%",
+ "%signature%", "%mac%"
]
}
@@ -208,7 +208,8 @@ abstract class ClientSuppliedSecret extends API::CallNode { }
private class FlaskClientSuppliedSecret extends ClientSuppliedSecret {
FlaskClientSuppliedSecret() {
this = Flask::request().getMember("headers").getMember(["get", "get_all", "getlist"]).getACall() and
- this.getParameter(0, ["key", "name"]).asSink().asExpr().(StrConst).getText().toLowerCase() = sensitiveheaders()
+ this.getParameter(0, ["key", "name"]).asSink().asExpr().(StrConst).getText().toLowerCase() =
+ sensitiveheaders()
}
}
@@ -219,7 +220,8 @@ private class DjangoClientSuppliedSecret extends ClientSuppliedSecret {
.getMember(["headers", "META"])
.getMember("get")
.getACall() and
- this.getParameter(0, "key").asSink().asExpr().(StrConst).getText().toLowerCase() = sensitiveheaders()
+ this.getParameter(0, "key").asSink().asExpr().(StrConst).getText().toLowerCase() =
+ sensitiveheaders()
}
}
@@ -231,7 +233,8 @@ API::Node requesthandler() {
private class TornadoClientSuppliedSecret extends ClientSuppliedSecret {
TornadoClientSuppliedSecret() {
this = requesthandler().getMember(["headers", "META"]).getMember("get").getACall() and
- this.getParameter(0, "key").asSink().asExpr().(StrConst).getText().toLowerCase() = sensitiveheaders()
+ this.getParameter(0, "key").asSink().asExpr().(StrConst).getText().toLowerCase() =
+ sensitiveheaders()
}
}
@@ -244,7 +247,8 @@ private class WerkzeugClientSuppliedSecret extends ClientSuppliedSecret {
WerkzeugClientSuppliedSecret() {
this =
headers().getMember(["headers", "META"]).getMember(["get", "get_all", "getlist"]).getACall() and
- this.getParameter(0, ["key", "name"]).asSink().asExpr().(StrConst).getText().toLowerCase() = sensitiveheaders()
+ this.getParameter(0, ["key", "name"]).asSink().asExpr().(StrConst).getText().toLowerCase() =
+ sensitiveheaders()
}
}
From 94b91536f9742f4cc14c13a5826d418c679eb8a2 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Sat, 3 Sep 2022 14:05:07 +0100
Subject: [PATCH 157/631] Replacing `getParameter` by `getArg` and
`getArgByName`
---
.../semmle/python/security/TimingAttack.qll | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
index ad739fc42a4..8507b9ebfb3 100644
--- a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
+++ b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
@@ -203,12 +203,12 @@ class CredentialExpr extends Expr {
*
* For example: `request.headers.get("X-Auth-Token")`.
*/
-abstract class ClientSuppliedSecret extends API::CallNode { }
+abstract class ClientSuppliedSecret extends DataFlow::CallCfgNode { }
private class FlaskClientSuppliedSecret extends ClientSuppliedSecret {
FlaskClientSuppliedSecret() {
this = Flask::request().getMember("headers").getMember(["get", "get_all", "getlist"]).getACall() and
- this.getParameter(0, ["key", "name"]).asSink().asExpr().(StrConst).getText().toLowerCase() =
+ [this.getArg(0), this.getArgByName(["key", "name"])].asExpr().(StrConst).getText().toLowerCase() =
sensitiveheaders()
}
}
@@ -220,7 +220,7 @@ private class DjangoClientSuppliedSecret extends ClientSuppliedSecret {
.getMember(["headers", "META"])
.getMember("get")
.getACall() and
- this.getParameter(0, "key").asSink().asExpr().(StrConst).getText().toLowerCase() =
+ [this.getArg(0), this.getArgByName("key")].asExpr().(StrConst).getText().toLowerCase() =
sensitiveheaders()
}
}
@@ -233,7 +233,7 @@ API::Node requesthandler() {
private class TornadoClientSuppliedSecret extends ClientSuppliedSecret {
TornadoClientSuppliedSecret() {
this = requesthandler().getMember(["headers", "META"]).getMember("get").getACall() and
- this.getParameter(0, "key").asSink().asExpr().(StrConst).getText().toLowerCase() =
+ [this.getArg(0), this.getArgByName("key")].asExpr().(StrConst).getText().toLowerCase() =
sensitiveheaders()
}
}
@@ -247,7 +247,7 @@ private class WerkzeugClientSuppliedSecret extends ClientSuppliedSecret {
WerkzeugClientSuppliedSecret() {
this =
headers().getMember(["headers", "META"]).getMember(["get", "get_all", "getlist"]).getACall() and
- this.getParameter(0, ["key", "name"]).asSink().asExpr().(StrConst).getText().toLowerCase() =
+ [this.getArg(0), this.getArgByName(["key", "name"])].asExpr().(StrConst).getText().toLowerCase() =
sensitiveheaders()
}
}
From f84331f5a53d1b2c43e5022f952bfa6722c799f3 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Mon, 5 Sep 2022 00:53:10 +0100
Subject: [PATCH 158/631] Provides classes for modeling HTTP Header APIs
---
.../experimental/semmle/python/Concepts.qll | 33 +++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll
index 433d89dbad1..1347949af93 100644
--- a/python/ql/src/experimental/semmle/python/Concepts.qll
+++ b/python/ql/src/experimental/semmle/python/Concepts.qll
@@ -371,6 +371,39 @@ class HeaderDeclaration extends DataFlow::Node {
DataFlow::Node getValueArg() { result = range.getValueArg() }
}
+/** Provides classes for modeling HTTP Header APIs. */
+module HeaderCollection {
+ /**
+ * A data-flow node that collects functions getting HTTP Headers.
+ *
+ * Extend this class to model new APIs. If you want to refine existing API models,
+ * extend `HeaderCollection` instead.
+ */
+ abstract class Range extends DataFlow::Node {
+ /**
+ * Gets the argument containing the header name.
+ */
+ abstract DataFlow::Node getNameArg();
+ }
+}
+
+/**
+ * A data-flow node that collects functions getting HTTP Headers.
+ *
+ * Extend this class to refine existing API models. If you want to model new APIs,
+ * extend `HeaderCollection::Range` instead.
+ */
+class HeaderCollection extends DataFlow::Node {
+ HeaderCollection::Range range;
+
+ HeaderCollection() { this = range }
+
+ /**
+ * Gets the argument containing the header name.
+ */
+ DataFlow::Node getNameArg() { result = range.getNameArg() }
+}
+
/** Provides classes for modeling Csv writer APIs. */
module CsvWriter {
/**
From 23871b3f5a43af571a46df81126acfb82183bbf5 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Mon, 5 Sep 2022 18:26:56 +0100
Subject: [PATCH 159/631] Update Concepts.qll
---
.../experimental/semmle/python/Concepts.qll | 33 -------------------
1 file changed, 33 deletions(-)
diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll
index 1347949af93..433d89dbad1 100644
--- a/python/ql/src/experimental/semmle/python/Concepts.qll
+++ b/python/ql/src/experimental/semmle/python/Concepts.qll
@@ -371,39 +371,6 @@ class HeaderDeclaration extends DataFlow::Node {
DataFlow::Node getValueArg() { result = range.getValueArg() }
}
-/** Provides classes for modeling HTTP Header APIs. */
-module HeaderCollection {
- /**
- * A data-flow node that collects functions getting HTTP Headers.
- *
- * Extend this class to model new APIs. If you want to refine existing API models,
- * extend `HeaderCollection` instead.
- */
- abstract class Range extends DataFlow::Node {
- /**
- * Gets the argument containing the header name.
- */
- abstract DataFlow::Node getNameArg();
- }
-}
-
-/**
- * A data-flow node that collects functions getting HTTP Headers.
- *
- * Extend this class to refine existing API models. If you want to model new APIs,
- * extend `HeaderCollection::Range` instead.
- */
-class HeaderCollection extends DataFlow::Node {
- HeaderCollection::Range range;
-
- HeaderCollection() { this = range }
-
- /**
- * Gets the argument containing the header name.
- */
- DataFlow::Node getNameArg() { result = range.getNameArg() }
-}
-
/** Provides classes for modeling Csv writer APIs. */
module CsvWriter {
/**
From 0b8bdc0f85aab2f4d1557f887f50af23f1adcc12 Mon Sep 17 00:00:00 2001
From: Taus
Date: Tue, 6 Sep 2022 16:37:43 +0000
Subject: [PATCH 160/631] Python: Fix broken test
---
...leTimingAttackAgainstSensitiveInfo.expected | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/python/ql/test/experimental/query-tests/Security/CWE-208/TimingAttackAgainstSensitiveInfo/PossibleTimingAttackAgainstSensitiveInfo.expected b/python/ql/test/experimental/query-tests/Security/CWE-208/TimingAttackAgainstSensitiveInfo/PossibleTimingAttackAgainstSensitiveInfo.expected
index 63d5e8ad821..4bde57c0b96 100644
--- a/python/ql/test/experimental/query-tests/Security/CWE-208/TimingAttackAgainstSensitiveInfo/PossibleTimingAttackAgainstSensitiveInfo.expected
+++ b/python/ql/test/experimental/query-tests/Security/CWE-208/TimingAttackAgainstSensitiveInfo/PossibleTimingAttackAgainstSensitiveInfo.expected
@@ -1,6 +1,24 @@
edges
+| TimingAttackAgainstSensitiveInfo.py:14:8:14:14 | ControlFlowNode for request | TimingAttackAgainstSensitiveInfo.py:15:20:15:31 | ControlFlowNode for Attribute |
+| TimingAttackAgainstSensitiveInfo.py:15:20:15:26 | ControlFlowNode for request | TimingAttackAgainstSensitiveInfo.py:15:20:15:31 | ControlFlowNode for Attribute |
+| TimingAttackAgainstSensitiveInfo.py:15:20:15:31 | ControlFlowNode for Attribute | TimingAttackAgainstSensitiveInfo.py:15:20:15:38 | ControlFlowNode for Subscript |
+| TimingAttackAgainstSensitiveInfo.py:15:20:15:38 | ControlFlowNode for Subscript | TimingAttackAgainstSensitiveInfo.py:16:16:16:23 | ControlFlowNode for password |
+| TimingAttackAgainstSensitiveInfo.py:20:8:20:14 | ControlFlowNode for request | TimingAttackAgainstSensitiveInfo.py:21:20:21:31 | ControlFlowNode for Attribute |
+| TimingAttackAgainstSensitiveInfo.py:21:20:21:26 | ControlFlowNode for request | TimingAttackAgainstSensitiveInfo.py:21:20:21:31 | ControlFlowNode for Attribute |
+| TimingAttackAgainstSensitiveInfo.py:21:20:21:31 | ControlFlowNode for Attribute | TimingAttackAgainstSensitiveInfo.py:21:20:21:38 | ControlFlowNode for Subscript |
+| TimingAttackAgainstSensitiveInfo.py:21:20:21:38 | ControlFlowNode for Subscript | TimingAttackAgainstSensitiveInfo.py:22:38:22:45 | ControlFlowNode for password |
nodes
+| TimingAttackAgainstSensitiveInfo.py:14:8:14:14 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| TimingAttackAgainstSensitiveInfo.py:15:20:15:26 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| TimingAttackAgainstSensitiveInfo.py:15:20:15:31 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| TimingAttackAgainstSensitiveInfo.py:15:20:15:38 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
| TimingAttackAgainstSensitiveInfo.py:16:16:16:23 | ControlFlowNode for password | semmle.label | ControlFlowNode for password |
+| TimingAttackAgainstSensitiveInfo.py:16:16:16:23 | ControlFlowNode for password | semmle.label | ControlFlowNode for password |
+| TimingAttackAgainstSensitiveInfo.py:20:8:20:14 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| TimingAttackAgainstSensitiveInfo.py:21:20:21:26 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
+| TimingAttackAgainstSensitiveInfo.py:21:20:21:31 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| TimingAttackAgainstSensitiveInfo.py:21:20:21:38 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
+| TimingAttackAgainstSensitiveInfo.py:22:38:22:45 | ControlFlowNode for password | semmle.label | ControlFlowNode for password |
subpaths
#select
| TimingAttackAgainstSensitiveInfo.py:16:16:16:23 | ControlFlowNode for password | TimingAttackAgainstSensitiveInfo.py:16:16:16:23 | ControlFlowNode for password | TimingAttackAgainstSensitiveInfo.py:16:16:16:23 | ControlFlowNode for password | Timing attack against $@ validation. | TimingAttackAgainstSensitiveInfo.py:16:16:16:23 | ControlFlowNode for password | client-supplied token |
From 64bb022adfd0a58c683b440c2c017469f43fa8d4 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Wed, 7 Sep 2022 11:12:53 +0100
Subject: [PATCH 161/631] Add `www-authenticate` to sensitiveheaders()
---
.../src/experimental/semmle/python/security/TimingAttack.qll | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
index 8507b9ebfb3..ee6be383415 100644
--- a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
+++ b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
@@ -257,7 +257,8 @@ private string sensitiveheaders() {
result =
[
"x-auth-token", "x-csrf-token", "http_x_csrf_token", "x-csrf-param", "x-csrf-header",
- "http_x_csrf_token", "x-api-key", "authorization", "proxy-authorization", "x-gitlab-token"
+ "http_x_csrf_token", "x-api-key", "authorization", "proxy-authorization", "x-gitlab-token",
+ "www-authenticate"
]
}
From cd51b4991dc4999bb7d9232a6481755e20f30c52 Mon Sep 17 00:00:00 2001
From: Raul Garcia
Date: Tue, 18 Oct 2022 11:48:13 -0700
Subject: [PATCH 162/631] Initial CHeckin - CNG basic case
---
.../QuantumVulnerableDiscovery/WinCng.ql | 39 +++++++++
.../QuantumVulnerableDiscovery/WindowsCng.qll | 7 ++
.../WindowsCngPQVAsymmetricKeyUsage.qll | 81 +++++++++++++++++++
.../QuantumVulnerableDiscovery/WinCng.qlref | 1 +
4 files changed, 128 insertions(+)
create mode 100644 cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng.ql
create mode 100644 cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCng.qll
create mode 100644 cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCngPQVAsymmetricKeyUsage.qll
create mode 100644 cpp/ql/test/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng.qlref
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng.ql b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng.ql
new file mode 100644
index 00000000000..15400fb7f60
--- /dev/null
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng.ql
@@ -0,0 +1,39 @@
+/**
+ * @id cpp/nist-pqc/pqc-vulnerable-algorithms-cng
+ * @name Usage of PQC vulnerable algorithms
+ * @description Usage of PQC vulnerable algorithms.
+ * @microsoft.severity important
+ * @kind path-problem
+ * @problem.severity warning
+ * @precision high
+ * @tags security
+ * pqc
+ * nist
+ */
+
+import cpp
+import DataFlow::PathGraph
+import WindowsCng
+import WindowsCngPQVAsymmetricKeyUsage
+
+// CNG-specific DataFlow configuration
+class BCryptConfiguration extends TaintTracking::Configuration {
+ BCryptConfiguration() {
+ this = "BCryptConfiguration"
+ }
+ override predicate isSource(DataFlow::Node source) {
+ source instanceof BCryptOpenAlgorithmProviderSource
+ }
+
+ override predicate isSink(DataFlow::Node sink) {
+ sink instanceof BCryptOpenAlgorithmProviderSink
+ }
+
+ override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
+ isWindowsCngAsymmetricKeyAdditionalTaintStep( node1, node2)
+ }
+}
+
+from BCryptConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
+where config.hasFlowPath(source, sink)
+select sink.getNode(), source, sink, "PQC vulnerable algorithm in use has been detected."
\ No newline at end of file
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCng.qll b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCng.qll
new file mode 100644
index 00000000000..92f54b7ed0e
--- /dev/null
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCng.qll
@@ -0,0 +1,7 @@
+import cpp
+import DataFlow::PathGraph
+import semmle.code.cpp.dataflow.TaintTracking
+
+abstract class BCryptOpenAlgorithmProviderSink extends DataFlow::Node {}
+abstract class BCryptOpenAlgorithmProviderSource extends DataFlow::Node {}
+
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCngPQVAsymmetricKeyUsage.qll b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCngPQVAsymmetricKeyUsage.qll
new file mode 100644
index 00000000000..1e354fb4abd
--- /dev/null
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCngPQVAsymmetricKeyUsage.qll
@@ -0,0 +1,81 @@
+import cpp
+import WindowsCng
+
+//TODO: Verify NCrypt calls (parameters) & find all other APIs that should be included (i.e. decrypt, etc.)
+
+predicate isExprAlgIdForBCryptOpenAlgorithmProvider(Expr e){
+ exists( FunctionCall call |
+ // BCryptOpenAlgorithmProvider 2nd argument specifies the algorithm to be used
+ e = call.getArgument(1)
+ and
+ call.getTarget().hasGlobalName("BCryptOpenAlgorithmProvider")
+ )
+}
+
+predicate isExprAlgHandleForBCryptGenerateOrImportKeyPair(Expr e){
+ exists( FunctionCall call |
+ e = call.getArgument(0)
+ and
+ ( call.getTarget().hasGlobalName("BCryptImportKeyPair") or
+ call.getTarget().hasGlobalName("BCryptGenerateKeyPair"))
+ )
+}
+
+predicate isExprOutKeyHandleForBCryptGenerateOrImportKeyPair(Expr e){
+ exists( FunctionCall call |
+ ( e = call.getArgument(3) and call.getTarget().hasGlobalName("BCryptImportKeyPair") )or
+ ( e = call.getArgument(1) and call.getTarget().hasGlobalName("BCryptGenerateKeyPair") )
+ )
+}
+
+predicate isExprKeyHandleForBCryptSignHash(Expr e){
+ exists( FunctionCall call |
+ e = call.getArgument(0)
+ and
+ call.getTarget().hasGlobalName("BCryptSignHash")
+ )
+}
+
+class BCryptSignHashArgumentSink extends BCryptOpenAlgorithmProviderSink {
+ BCryptSignHashArgumentSink() {
+ isExprKeyHandleForBCryptSignHash(this.asExpr())
+ }
+}
+
+class BCryptOpenAlgorithmProviderPqcVulnerableAlgorithmsSource extends BCryptOpenAlgorithmProviderSource {
+ BCryptOpenAlgorithmProviderPqcVulnerableAlgorithmsSource() {
+ this.asExpr() instanceof StringLiteral and
+ (
+ this.asExpr().getValue() in ["DH", "DSA", "ECDSA", "ECDH"]
+ or this.asExpr().getValue().matches("ECDH%")
+ or this.asExpr().getValue().matches("RSA%")
+ )
+ }
+
+}
+
+predicate isAdditionalTaintStepOpenAlgolrithmProviderToGenerateOrImportKeyPair(DataFlow::Node node1, DataFlow::Node node2)
+{
+ isExprAlgIdForBCryptOpenAlgorithmProvider(node1.asExpr()) and
+ isExprAlgHandleForBCryptGenerateOrImportKeyPair(node2.asExpr())
+}
+
+
+predicate isAdditionalTaintStepWithinGenerateOrImportKeyPair(DataFlow::Node node1, DataFlow::Node node2)
+{
+ isExprAlgHandleForBCryptGenerateOrImportKeyPair(node1.asExpr()) and
+ isExprOutKeyHandleForBCryptGenerateOrImportKeyPair(node2.asExpr())
+}
+
+predicate isAdditionalTaintStepGenerateOrImportKeyPairToSignHash(DataFlow::Node node1, DataFlow::Node node2)
+{
+ isExprOutKeyHandleForBCryptGenerateOrImportKeyPair(node1.asExpr()) and
+ isExprKeyHandleForBCryptSignHash(node2.asExpr())
+}
+
+predicate isWindowsCngAsymmetricKeyAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
+ isAdditionalTaintStepOpenAlgolrithmProviderToGenerateOrImportKeyPair(node1, node2)
+ or isAdditionalTaintStepWithinGenerateOrImportKeyPair(node1, node2)
+ or isAdditionalTaintStepGenerateOrImportKeyPairToSignHash(node1, node2)
+}
+
diff --git a/cpp/ql/test/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng.qlref b/cpp/ql/test/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng.qlref
new file mode 100644
index 00000000000..6e6f369e60a
--- /dev/null
+++ b/cpp/ql/test/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng.qlref
@@ -0,0 +1 @@
+experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng.ql
\ No newline at end of file
From d4ba46ce82a19c4cf8e8eac4fbeb4d07d1091a5f Mon Sep 17 00:00:00 2001
From: Raul Garcia
Date: Mon, 7 Nov 2022 15:05:28 -0800
Subject: [PATCH 163/631] Initial PR - Proof of Concept using CNG
---
.../QuantumVulnerableDiscovery/WindowsCng.qll | 7 ++
.../WindowsCngPQCVAsymmetricKeyUsage.ql | 40 +++++++++
.../WindowsCngPQCVAsymmetricKeyUsage.qll | 81 +++++++++++++++++++
3 files changed, 128 insertions(+)
create mode 100644 cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCng.qll
create mode 100644 cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCngPQCVAsymmetricKeyUsage.ql
create mode 100644 cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCngPQCVAsymmetricKeyUsage.qll
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCng.qll b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCng.qll
new file mode 100644
index 00000000000..92f54b7ed0e
--- /dev/null
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCng.qll
@@ -0,0 +1,7 @@
+import cpp
+import DataFlow::PathGraph
+import semmle.code.cpp.dataflow.TaintTracking
+
+abstract class BCryptOpenAlgorithmProviderSink extends DataFlow::Node {}
+abstract class BCryptOpenAlgorithmProviderSource extends DataFlow::Node {}
+
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCngPQCVAsymmetricKeyUsage.ql b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCngPQCVAsymmetricKeyUsage.ql
new file mode 100644
index 00000000000..86fe64528ee
--- /dev/null
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCngPQCVAsymmetricKeyUsage.ql
@@ -0,0 +1,40 @@
+/**
+ * @id cpp/nist-pqc/pqc-vulnerable-algorithms-cng
+ * @name Usage of PQC vulnerable algorithms
+ * @description Usage of PQC vulnerable algorithms.
+ * @microsoft.severity important
+ * @kind path-problem
+ * @problem.severity warning
+ * @precision high
+ * @tags security
+ * pqc
+ * nist
+ */
+
+import cpp
+import DataFlow::PathGraph
+import WindowsCng
+import WindowsCngPQCVAsymmetricKeyUsage
+
+// CNG-specific DataFlow configuration
+class BCryptConfiguration extends DataFlow::Configuration {
+ BCryptConfiguration() {
+ this = "BCryptConfiguration"
+ }
+ override predicate isSource(DataFlow::Node source) {
+ source instanceof BCryptOpenAlgorithmProviderSource
+ }
+
+ override predicate isSink(DataFlow::Node sink) {
+ sink instanceof BCryptOpenAlgorithmProviderSink
+ }
+
+ override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
+ isWindowsCngAsymmetricKeyAdditionalTaintStep( node1, node2)
+ }
+}
+
+from BCryptConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
+where config.hasFlowPath(source, sink)
+select sink.getNode(), source, sink, "PQC vulnerable algorithm $@ in use has been detected.",
+ source.getNode().asExpr(), source.getNode().asExpr().toString()
\ No newline at end of file
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCngPQCVAsymmetricKeyUsage.qll b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCngPQCVAsymmetricKeyUsage.qll
new file mode 100644
index 00000000000..1e354fb4abd
--- /dev/null
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCngPQCVAsymmetricKeyUsage.qll
@@ -0,0 +1,81 @@
+import cpp
+import WindowsCng
+
+//TODO: Verify NCrypt calls (parameters) & find all other APIs that should be included (i.e. decrypt, etc.)
+
+predicate isExprAlgIdForBCryptOpenAlgorithmProvider(Expr e){
+ exists( FunctionCall call |
+ // BCryptOpenAlgorithmProvider 2nd argument specifies the algorithm to be used
+ e = call.getArgument(1)
+ and
+ call.getTarget().hasGlobalName("BCryptOpenAlgorithmProvider")
+ )
+}
+
+predicate isExprAlgHandleForBCryptGenerateOrImportKeyPair(Expr e){
+ exists( FunctionCall call |
+ e = call.getArgument(0)
+ and
+ ( call.getTarget().hasGlobalName("BCryptImportKeyPair") or
+ call.getTarget().hasGlobalName("BCryptGenerateKeyPair"))
+ )
+}
+
+predicate isExprOutKeyHandleForBCryptGenerateOrImportKeyPair(Expr e){
+ exists( FunctionCall call |
+ ( e = call.getArgument(3) and call.getTarget().hasGlobalName("BCryptImportKeyPair") )or
+ ( e = call.getArgument(1) and call.getTarget().hasGlobalName("BCryptGenerateKeyPair") )
+ )
+}
+
+predicate isExprKeyHandleForBCryptSignHash(Expr e){
+ exists( FunctionCall call |
+ e = call.getArgument(0)
+ and
+ call.getTarget().hasGlobalName("BCryptSignHash")
+ )
+}
+
+class BCryptSignHashArgumentSink extends BCryptOpenAlgorithmProviderSink {
+ BCryptSignHashArgumentSink() {
+ isExprKeyHandleForBCryptSignHash(this.asExpr())
+ }
+}
+
+class BCryptOpenAlgorithmProviderPqcVulnerableAlgorithmsSource extends BCryptOpenAlgorithmProviderSource {
+ BCryptOpenAlgorithmProviderPqcVulnerableAlgorithmsSource() {
+ this.asExpr() instanceof StringLiteral and
+ (
+ this.asExpr().getValue() in ["DH", "DSA", "ECDSA", "ECDH"]
+ or this.asExpr().getValue().matches("ECDH%")
+ or this.asExpr().getValue().matches("RSA%")
+ )
+ }
+
+}
+
+predicate isAdditionalTaintStepOpenAlgolrithmProviderToGenerateOrImportKeyPair(DataFlow::Node node1, DataFlow::Node node2)
+{
+ isExprAlgIdForBCryptOpenAlgorithmProvider(node1.asExpr()) and
+ isExprAlgHandleForBCryptGenerateOrImportKeyPair(node2.asExpr())
+}
+
+
+predicate isAdditionalTaintStepWithinGenerateOrImportKeyPair(DataFlow::Node node1, DataFlow::Node node2)
+{
+ isExprAlgHandleForBCryptGenerateOrImportKeyPair(node1.asExpr()) and
+ isExprOutKeyHandleForBCryptGenerateOrImportKeyPair(node2.asExpr())
+}
+
+predicate isAdditionalTaintStepGenerateOrImportKeyPairToSignHash(DataFlow::Node node1, DataFlow::Node node2)
+{
+ isExprOutKeyHandleForBCryptGenerateOrImportKeyPair(node1.asExpr()) and
+ isExprKeyHandleForBCryptSignHash(node2.asExpr())
+}
+
+predicate isWindowsCngAsymmetricKeyAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
+ isAdditionalTaintStepOpenAlgolrithmProviderToGenerateOrImportKeyPair(node1, node2)
+ or isAdditionalTaintStepWithinGenerateOrImportKeyPair(node1, node2)
+ or isAdditionalTaintStepGenerateOrImportKeyPairToSignHash(node1, node2)
+}
+
From 96116c2d2d38b4a9719beaf5efe69ae4421ee5fc Mon Sep 17 00:00:00 2001
From: "REDMOND\\brodes"
Date: Tue, 8 Nov 2022 10:59:13 -0500
Subject: [PATCH 164/631] Suggested query changes.
---
.../QuantumVulnerableDiscovery/WinCng.ql | 2 +-
.../WindowsCngPQCVAsymmetricKeyUsage.qll | 57 +++++++++++++
.../WindowsCngPQVAsymmetricKeyUsage.qll | 81 -------------------
3 files changed, 58 insertions(+), 82 deletions(-)
create mode 100644 cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCngPQCVAsymmetricKeyUsage.qll
delete mode 100644 cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCngPQVAsymmetricKeyUsage.qll
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng.ql b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng.ql
index 15400fb7f60..195a8ee85dc 100644
--- a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng.ql
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng.ql
@@ -14,7 +14,7 @@
import cpp
import DataFlow::PathGraph
import WindowsCng
-import WindowsCngPQVAsymmetricKeyUsage
+import WindowsCngPQCVAsymmetricKeyUsage
// CNG-specific DataFlow configuration
class BCryptConfiguration extends TaintTracking::Configuration {
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCngPQCVAsymmetricKeyUsage.qll b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCngPQCVAsymmetricKeyUsage.qll
new file mode 100644
index 00000000000..14dd5e4beda
--- /dev/null
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCngPQCVAsymmetricKeyUsage.qll
@@ -0,0 +1,57 @@
+import cpp
+import WindowsCng
+//TODO: Verify NCrypt calls (parameters) & find all other APIs that should be included (i.e. decrypt, etc.)
+predicate isExprKeyHandleForBCryptSignHash(Expr e){
+ exists( FunctionCall call |
+ e = call.getArgument(0)
+ and
+ call.getTarget().hasGlobalName("BCryptSignHash")
+ )
+}
+
+class BCryptSignHashArgumentSink extends BCryptOpenAlgorithmProviderSink {
+ BCryptSignHashArgumentSink() {
+ isExprKeyHandleForBCryptSignHash(this.asExpr())
+ }
+}
+
+class BCryptOpenAlgorithmProviderPqcVulnerableAlgorithmsSource extends BCryptOpenAlgorithmProviderSource {
+ BCryptOpenAlgorithmProviderPqcVulnerableAlgorithmsSource() {
+ this.asExpr() instanceof StringLiteral and
+ (
+ this.asExpr().getValue() in ["DH", "DSA", "ECDSA", "ECDH"]
+ or this.asExpr().getValue().matches("ECDH%")
+ or this.asExpr().getValue().matches("RSA%")
+ )
+ }
+}
+
+predicate stepOpenAlgorithmProvider(DataFlow::Node node1, DataFlow::Node node2)
+{
+ exists( FunctionCall call |
+ // BCryptOpenAlgorithmProvider 2nd argument specifies the algorithm to be used
+ node1.asExpr() = call.getArgument(1)
+ and
+ call.getTarget().hasGlobalName("BCryptOpenAlgorithmProvider")
+ and
+ node2.asDefiningArgument() = call.getArgument(0)
+ )
+}
+
+predicate stepImportGenerateKeyPair(DataFlow::Node node1, DataFlow::Node node2)
+{
+ exists( FunctionCall call |
+ node1.asExpr() = call.getArgument(0)
+ and
+ ( call.getTarget().hasGlobalName("BCryptImportKeyPair") or
+ call.getTarget().hasGlobalName("BCryptGenerateKeyPair"))
+ and
+ node2.asDefiningArgument() = call.getArgument(1)
+ )
+}
+
+predicate isWindowsCngAsymmetricKeyAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
+ stepOpenAlgorithmProvider(node1, node2)
+ or
+ stepImportGenerateKeyPair(node1, node2)
+}
\ No newline at end of file
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCngPQVAsymmetricKeyUsage.qll b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCngPQVAsymmetricKeyUsage.qll
deleted file mode 100644
index 1e354fb4abd..00000000000
--- a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCngPQVAsymmetricKeyUsage.qll
+++ /dev/null
@@ -1,81 +0,0 @@
-import cpp
-import WindowsCng
-
-//TODO: Verify NCrypt calls (parameters) & find all other APIs that should be included (i.e. decrypt, etc.)
-
-predicate isExprAlgIdForBCryptOpenAlgorithmProvider(Expr e){
- exists( FunctionCall call |
- // BCryptOpenAlgorithmProvider 2nd argument specifies the algorithm to be used
- e = call.getArgument(1)
- and
- call.getTarget().hasGlobalName("BCryptOpenAlgorithmProvider")
- )
-}
-
-predicate isExprAlgHandleForBCryptGenerateOrImportKeyPair(Expr e){
- exists( FunctionCall call |
- e = call.getArgument(0)
- and
- ( call.getTarget().hasGlobalName("BCryptImportKeyPair") or
- call.getTarget().hasGlobalName("BCryptGenerateKeyPair"))
- )
-}
-
-predicate isExprOutKeyHandleForBCryptGenerateOrImportKeyPair(Expr e){
- exists( FunctionCall call |
- ( e = call.getArgument(3) and call.getTarget().hasGlobalName("BCryptImportKeyPair") )or
- ( e = call.getArgument(1) and call.getTarget().hasGlobalName("BCryptGenerateKeyPair") )
- )
-}
-
-predicate isExprKeyHandleForBCryptSignHash(Expr e){
- exists( FunctionCall call |
- e = call.getArgument(0)
- and
- call.getTarget().hasGlobalName("BCryptSignHash")
- )
-}
-
-class BCryptSignHashArgumentSink extends BCryptOpenAlgorithmProviderSink {
- BCryptSignHashArgumentSink() {
- isExprKeyHandleForBCryptSignHash(this.asExpr())
- }
-}
-
-class BCryptOpenAlgorithmProviderPqcVulnerableAlgorithmsSource extends BCryptOpenAlgorithmProviderSource {
- BCryptOpenAlgorithmProviderPqcVulnerableAlgorithmsSource() {
- this.asExpr() instanceof StringLiteral and
- (
- this.asExpr().getValue() in ["DH", "DSA", "ECDSA", "ECDH"]
- or this.asExpr().getValue().matches("ECDH%")
- or this.asExpr().getValue().matches("RSA%")
- )
- }
-
-}
-
-predicate isAdditionalTaintStepOpenAlgolrithmProviderToGenerateOrImportKeyPair(DataFlow::Node node1, DataFlow::Node node2)
-{
- isExprAlgIdForBCryptOpenAlgorithmProvider(node1.asExpr()) and
- isExprAlgHandleForBCryptGenerateOrImportKeyPair(node2.asExpr())
-}
-
-
-predicate isAdditionalTaintStepWithinGenerateOrImportKeyPair(DataFlow::Node node1, DataFlow::Node node2)
-{
- isExprAlgHandleForBCryptGenerateOrImportKeyPair(node1.asExpr()) and
- isExprOutKeyHandleForBCryptGenerateOrImportKeyPair(node2.asExpr())
-}
-
-predicate isAdditionalTaintStepGenerateOrImportKeyPairToSignHash(DataFlow::Node node1, DataFlow::Node node2)
-{
- isExprOutKeyHandleForBCryptGenerateOrImportKeyPair(node1.asExpr()) and
- isExprKeyHandleForBCryptSignHash(node2.asExpr())
-}
-
-predicate isWindowsCngAsymmetricKeyAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
- isAdditionalTaintStepOpenAlgolrithmProviderToGenerateOrImportKeyPair(node1, node2)
- or isAdditionalTaintStepWithinGenerateOrImportKeyPair(node1, node2)
- or isAdditionalTaintStepGenerateOrImportKeyPairToSignHash(node1, node2)
-}
-
From 7a27635a11eb27bcfa00e87d777b027667a5866d Mon Sep 17 00:00:00 2001
From: "REDMOND\\brodes"
Date: Tue, 8 Nov 2022 12:11:43 -0500
Subject: [PATCH 165/631] Moving queries and modifying query to include
BCryptEncrypt
---
.../{ => WinCng}/WinCng.ql | 2 +-
.../{ => WinCng}/WindowsCng.qll | 0
.../WindowsCngPQCVulnerableUsage.ql} | 0
.../WindowsCngPQCVulnerableUsage.qll} | 14 ++++++++++++--
4 files changed, 13 insertions(+), 3 deletions(-)
rename cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/{ => WinCng}/WinCng.ql (94%)
rename cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/{ => WinCng}/WindowsCng.qll (100%)
rename cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/{WindowsCngPQCVAsymmetricKeyUsage.ql => WinCng/WindowsCngPQCVulnerableUsage.ql} (100%)
rename cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/{WindowsCngPQCVAsymmetricKeyUsage.qll => WinCng/WindowsCngPQCVulnerableUsage.qll} (86%)
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng.ql b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WinCng.ql
similarity index 94%
rename from cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng.ql
rename to cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WinCng.ql
index 195a8ee85dc..7f19a74aa70 100644
--- a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng.ql
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WinCng.ql
@@ -14,7 +14,7 @@
import cpp
import DataFlow::PathGraph
import WindowsCng
-import WindowsCngPQCVAsymmetricKeyUsage
+import WindowsCngPQCVulnerableUsage
// CNG-specific DataFlow configuration
class BCryptConfiguration extends TaintTracking::Configuration {
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCng.qll b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
similarity index 100%
rename from cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCng.qll
rename to cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCngPQCVAsymmetricKeyUsage.ql b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.ql
similarity index 100%
rename from cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCngPQCVAsymmetricKeyUsage.ql
rename to cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.ql
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCngPQCVAsymmetricKeyUsage.qll b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
similarity index 86%
rename from cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCngPQCVAsymmetricKeyUsage.qll
rename to cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
index 14dd5e4beda..48e9d1326fc 100644
--- a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WindowsCngPQCVAsymmetricKeyUsage.qll
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
@@ -1,11 +1,21 @@
import cpp
import WindowsCng
+
+predicate vulnerableCngFunctionName(string name) {
+ name in ["BCryptSignHash", "BCryptEncrypt"]
+}
+
+predicate vulnerableCngFunction(Function f)
+{
+ exists(string name | f.hasGlobalName(name) and vulnerableCngFunctionName(name))
+}
+
//TODO: Verify NCrypt calls (parameters) & find all other APIs that should be included (i.e. decrypt, etc.)
predicate isExprKeyHandleForBCryptSignHash(Expr e){
exists( FunctionCall call |
e = call.getArgument(0)
- and
- call.getTarget().hasGlobalName("BCryptSignHash")
+ and
+ vulnerableCngFunction(call.getTarget())
)
}
From 1e9c2b3335bdea69e9f88e976eed7dbb985cde0f Mon Sep 17 00:00:00 2001
From: "REDMOND\\brodes"
Date: Tue, 8 Nov 2022 12:13:05 -0500
Subject: [PATCH 166/631] Applied formatting.
---
.../WinCng/WindowsCngPQCVulnerableUsage.qll | 84 ++++++++-----------
1 file changed, 37 insertions(+), 47 deletions(-)
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
index 48e9d1326fc..9af62fdfa97 100644
--- a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
@@ -1,67 +1,57 @@
import cpp
import WindowsCng
-predicate vulnerableCngFunctionName(string name) {
- name in ["BCryptSignHash", "BCryptEncrypt"]
-}
+predicate vulnerableCngFunctionName(string name) { name in ["BCryptSignHash", "BCryptEncrypt"] }
-predicate vulnerableCngFunction(Function f)
-{
- exists(string name | f.hasGlobalName(name) and vulnerableCngFunctionName(name))
+predicate vulnerableCngFunction(Function f) {
+ exists(string name | f.hasGlobalName(name) and vulnerableCngFunctionName(name))
}
//TODO: Verify NCrypt calls (parameters) & find all other APIs that should be included (i.e. decrypt, etc.)
-predicate isExprKeyHandleForBCryptSignHash(Expr e){
- exists( FunctionCall call |
- e = call.getArgument(0)
- and
- vulnerableCngFunction(call.getTarget())
- )
+predicate isExprKeyHandleForBCryptSignHash(Expr e) {
+ exists(FunctionCall call |
+ e = call.getArgument(0) and
+ vulnerableCngFunction(call.getTarget())
+ )
}
class BCryptSignHashArgumentSink extends BCryptOpenAlgorithmProviderSink {
- BCryptSignHashArgumentSink() {
- isExprKeyHandleForBCryptSignHash(this.asExpr())
- }
+ BCryptSignHashArgumentSink() { isExprKeyHandleForBCryptSignHash(this.asExpr()) }
}
class BCryptOpenAlgorithmProviderPqcVulnerableAlgorithmsSource extends BCryptOpenAlgorithmProviderSource {
- BCryptOpenAlgorithmProviderPqcVulnerableAlgorithmsSource() {
- this.asExpr() instanceof StringLiteral and
- (
- this.asExpr().getValue() in ["DH", "DSA", "ECDSA", "ECDH"]
- or this.asExpr().getValue().matches("ECDH%")
- or this.asExpr().getValue().matches("RSA%")
- )
- }
+ BCryptOpenAlgorithmProviderPqcVulnerableAlgorithmsSource() {
+ this.asExpr() instanceof StringLiteral and
+ (
+ this.asExpr().getValue() in ["DH", "DSA", "ECDSA", "ECDH"] or
+ this.asExpr().getValue().matches("ECDH%") or
+ this.asExpr().getValue().matches("RSA%")
+ )
+ }
}
-predicate stepOpenAlgorithmProvider(DataFlow::Node node1, DataFlow::Node node2)
-{
- exists( FunctionCall call |
- // BCryptOpenAlgorithmProvider 2nd argument specifies the algorithm to be used
- node1.asExpr() = call.getArgument(1)
- and
- call.getTarget().hasGlobalName("BCryptOpenAlgorithmProvider")
- and
- node2.asDefiningArgument() = call.getArgument(0)
- )
+predicate stepOpenAlgorithmProvider(DataFlow::Node node1, DataFlow::Node node2) {
+ exists(FunctionCall call |
+ // BCryptOpenAlgorithmProvider 2nd argument specifies the algorithm to be used
+ node1.asExpr() = call.getArgument(1) and
+ call.getTarget().hasGlobalName("BCryptOpenAlgorithmProvider") and
+ node2.asDefiningArgument() = call.getArgument(0)
+ )
}
-predicate stepImportGenerateKeyPair(DataFlow::Node node1, DataFlow::Node node2)
-{
- exists( FunctionCall call |
- node1.asExpr() = call.getArgument(0)
- and
- ( call.getTarget().hasGlobalName("BCryptImportKeyPair") or
- call.getTarget().hasGlobalName("BCryptGenerateKeyPair"))
- and
- node2.asDefiningArgument() = call.getArgument(1)
- )
+predicate stepImportGenerateKeyPair(DataFlow::Node node1, DataFlow::Node node2) {
+ exists(FunctionCall call |
+ node1.asExpr() = call.getArgument(0) and
+ (
+ call.getTarget().hasGlobalName("BCryptImportKeyPair") or
+ call.getTarget().hasGlobalName("BCryptGenerateKeyPair")
+ ) and
+ node2.asDefiningArgument() = call.getArgument(1)
+ )
}
predicate isWindowsCngAsymmetricKeyAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
- stepOpenAlgorithmProvider(node1, node2)
- or
- stepImportGenerateKeyPair(node1, node2)
-}
\ No newline at end of file
+ stepOpenAlgorithmProvider(node1, node2)
+ or
+ stepImportGenerateKeyPair(node1, node2)
+}
From 43760b6bb125e91a454b9a6d4be1232e0e857e51 Mon Sep 17 00:00:00 2001
From: "REDMOND\\brodes"
Date: Tue, 8 Nov 2022 12:20:15 -0500
Subject: [PATCH 167/631] Misc. compartmentalization and naming changes.
---
.../WinCng/WindowsCngPQCVulnerableUsage.ql | 4 ++--
.../WinCng/WindowsCngPQCVulnerableUsage.qll | 13 ++++++++-----
2 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.ql b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.ql
index 86fe64528ee..b77ad05ced0 100644
--- a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.ql
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.ql
@@ -14,7 +14,7 @@
import cpp
import DataFlow::PathGraph
import WindowsCng
-import WindowsCngPQCVAsymmetricKeyUsage
+import WindowsCngPQCVulnerableUsage
// CNG-specific DataFlow configuration
class BCryptConfiguration extends DataFlow::Configuration {
@@ -30,7 +30,7 @@ class BCryptConfiguration extends DataFlow::Configuration {
}
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
- isWindowsCngAsymmetricKeyAdditionalTaintStep( node1, node2)
+ isWindowsCngAdditionalTaintStep( node1, node2)
}
}
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
index 9af62fdfa97..17139faabe6 100644
--- a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
@@ -3,10 +3,16 @@ import WindowsCng
predicate vulnerableCngFunctionName(string name) { name in ["BCryptSignHash", "BCryptEncrypt"] }
+predicate keyGenAndImportFunctionName(string name) { name in ["BCryptImportKeyPair", "BCryptGenerateKeyPair"] }
+
predicate vulnerableCngFunction(Function f) {
exists(string name | f.hasGlobalName(name) and vulnerableCngFunctionName(name))
}
+predicate keyGenAndImportFunction(Function f){
+ exists(string name | f.hasGlobalName(name) and keyGenAndImportFunctionName(name))
+}
+
//TODO: Verify NCrypt calls (parameters) & find all other APIs that should be included (i.e. decrypt, etc.)
predicate isExprKeyHandleForBCryptSignHash(Expr e) {
exists(FunctionCall call |
@@ -42,15 +48,12 @@ predicate stepOpenAlgorithmProvider(DataFlow::Node node1, DataFlow::Node node2)
predicate stepImportGenerateKeyPair(DataFlow::Node node1, DataFlow::Node node2) {
exists(FunctionCall call |
node1.asExpr() = call.getArgument(0) and
- (
- call.getTarget().hasGlobalName("BCryptImportKeyPair") or
- call.getTarget().hasGlobalName("BCryptGenerateKeyPair")
- ) and
+ keyGenAndImportFunction(call.getTarget()) and
node2.asDefiningArgument() = call.getArgument(1)
)
}
-predicate isWindowsCngAsymmetricKeyAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
+predicate isWindowsCngAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
stepOpenAlgorithmProvider(node1, node2)
or
stepImportGenerateKeyPair(node1, node2)
From eceda7a787bd5e3ac9a7d8bcb53cff12062f474a Mon Sep 17 00:00:00 2001
From: "REDMOND\\brodes"
Date: Tue, 8 Nov 2022 12:39:49 -0500
Subject: [PATCH 168/631] More cleanup
---
.../WinCng/WinCng.ql | 39 -------------------
.../WinCng/WindowsCngPQCVulnerableUsage.qll | 22 +++++------
2 files changed, 10 insertions(+), 51 deletions(-)
delete mode 100644 cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WinCng.ql
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WinCng.ql b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WinCng.ql
deleted file mode 100644
index 7f19a74aa70..00000000000
--- a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WinCng.ql
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * @id cpp/nist-pqc/pqc-vulnerable-algorithms-cng
- * @name Usage of PQC vulnerable algorithms
- * @description Usage of PQC vulnerable algorithms.
- * @microsoft.severity important
- * @kind path-problem
- * @problem.severity warning
- * @precision high
- * @tags security
- * pqc
- * nist
- */
-
-import cpp
-import DataFlow::PathGraph
-import WindowsCng
-import WindowsCngPQCVulnerableUsage
-
-// CNG-specific DataFlow configuration
-class BCryptConfiguration extends TaintTracking::Configuration {
- BCryptConfiguration() {
- this = "BCryptConfiguration"
- }
- override predicate isSource(DataFlow::Node source) {
- source instanceof BCryptOpenAlgorithmProviderSource
- }
-
- override predicate isSink(DataFlow::Node sink) {
- sink instanceof BCryptOpenAlgorithmProviderSink
- }
-
- override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
- isWindowsCngAsymmetricKeyAdditionalTaintStep( node1, node2)
- }
-}
-
-from BCryptConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
-where config.hasFlowPath(source, sink)
-select sink.getNode(), source, sink, "PQC vulnerable algorithm in use has been detected."
\ No newline at end of file
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
index 17139faabe6..fbac511039b 100644
--- a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
@@ -1,30 +1,28 @@
import cpp
import WindowsCng
-predicate vulnerableCngFunctionName(string name) { name in ["BCryptSignHash", "BCryptEncrypt"] }
-
predicate keyGenAndImportFunctionName(string name) { name in ["BCryptImportKeyPair", "BCryptGenerateKeyPair"] }
-predicate vulnerableCngFunction(Function f) {
- exists(string name | f.hasGlobalName(name) and vulnerableCngFunctionName(name))
-}
-
predicate keyGenAndImportFunction(Function f){
exists(string name | f.hasGlobalName(name) and keyGenAndImportFunctionName(name))
}
//TODO: Verify NCrypt calls (parameters) & find all other APIs that should be included (i.e. decrypt, etc.)
-predicate isExprKeyHandleForBCryptSignHash(Expr e) {
- exists(FunctionCall call |
- e = call.getArgument(0) and
- vulnerableCngFunction(call.getTarget())
- )
+
+
+predicate isCallArgument(string funcGlobalName, Expr arg, int index){
+ exists(Call c | c.getArgument(index) = arg and c.getTarget().hasGlobalName(funcGlobalName))
}
class BCryptSignHashArgumentSink extends BCryptOpenAlgorithmProviderSink {
- BCryptSignHashArgumentSink() { isExprKeyHandleForBCryptSignHash(this.asExpr()) }
+ BCryptSignHashArgumentSink() { isCallArgument("BCryptSignHash", this.asExpr(), 0) }
}
+class BCryptEncryptArgumentSink extends BCryptOpenAlgorithmProviderSink {
+ BCryptEncryptArgumentSink() { isCallArgument("BCryptEncrypt", this.asExpr(), 0) }
+ }
+
+
class BCryptOpenAlgorithmProviderPqcVulnerableAlgorithmsSource extends BCryptOpenAlgorithmProviderSource {
BCryptOpenAlgorithmProviderPqcVulnerableAlgorithmsSource() {
this.asExpr() instanceof StringLiteral and
From da8a7f36d18263f213cf86f1652ae7aeea29f963 Mon Sep 17 00:00:00 2001
From: "REDMOND\\brodes"
Date: Tue, 8 Nov 2022 12:43:22 -0500
Subject: [PATCH 169/631] More cleanup
---
.../WinCng/WindowsCngPQCVulnerableUsage.qll | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
index fbac511039b..412941a48e5 100644
--- a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
@@ -1,12 +1,6 @@
import cpp
import WindowsCng
-predicate keyGenAndImportFunctionName(string name) { name in ["BCryptImportKeyPair", "BCryptGenerateKeyPair"] }
-
-predicate keyGenAndImportFunction(Function f){
- exists(string name | f.hasGlobalName(name) and keyGenAndImportFunctionName(name))
-}
-
//TODO: Verify NCrypt calls (parameters) & find all other APIs that should be included (i.e. decrypt, etc.)
@@ -46,7 +40,7 @@ predicate stepOpenAlgorithmProvider(DataFlow::Node node1, DataFlow::Node node2)
predicate stepImportGenerateKeyPair(DataFlow::Node node1, DataFlow::Node node2) {
exists(FunctionCall call |
node1.asExpr() = call.getArgument(0) and
- keyGenAndImportFunction(call.getTarget()) and
+ exists(string name | name in ["BCryptImportKeyPair", "BCryptGenerateKeyPair"] and call.getTarget().hasGlobalName(name)) and
node2.asDefiningArgument() = call.getArgument(1)
)
}
From b242b4bba6420ebd80d6dedc191a18d42131ae22 Mon Sep 17 00:00:00 2001
From: "REDMOND\\brodes"
Date: Tue, 8 Nov 2022 12:49:57 -0500
Subject: [PATCH 170/631] More re-org
---
.../WinCng/WindowsCng.qll | 30 +++++++++++-
.../WinCng/WindowsCngPQCVulnerableUsage.ql | 19 +-------
.../WinCng/WindowsCngPQCVulnerableUsage.qll | 48 ++++++++-----------
3 files changed, 49 insertions(+), 48 deletions(-)
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
index 92f54b7ed0e..44900790b63 100644
--- a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
@@ -2,6 +2,32 @@ import cpp
import DataFlow::PathGraph
import semmle.code.cpp.dataflow.TaintTracking
-abstract class BCryptOpenAlgorithmProviderSink extends DataFlow::Node {}
-abstract class BCryptOpenAlgorithmProviderSource extends DataFlow::Node {}
+abstract class BCryptOpenAlgorithmProviderSink extends DataFlow::Node { }
+abstract class BCryptOpenAlgorithmProviderSource extends DataFlow::Node { }
+
+predicate isCallArgument(string funcGlobalName, Expr arg, int index) {
+ exists(Call c | c.getArgument(index) = arg and c.getTarget().hasGlobalName(funcGlobalName))
+}
+
+//TODO: Verify NCrypt calls (parameters) & find all other APIs that should be included (i.e. decrypt, etc.)
+// ------------------ SINKS ----------------------
+class BCryptSignHashArgumentSink extends BCryptOpenAlgorithmProviderSink {
+ BCryptSignHashArgumentSink() { isCallArgument("BCryptSignHash", this.asExpr(), 0) }
+}
+
+class BCryptEncryptArgumentSink extends BCryptOpenAlgorithmProviderSink {
+ BCryptEncryptArgumentSink() { isCallArgument("BCryptEncrypt", this.asExpr(), 0) }
+}
+
+// ----------------- SOURCES -----------------------
+class BCryptOpenAlgorithmProviderPqcVulnerableAlgorithmsSource extends BCryptOpenAlgorithmProviderSource {
+ BCryptOpenAlgorithmProviderPqcVulnerableAlgorithmsSource() {
+ this.asExpr() instanceof StringLiteral and
+ (
+ this.asExpr().getValue() in ["DH", "DSA", "ECDSA", "ECDH"] or
+ this.asExpr().getValue().matches("ECDH%") or
+ this.asExpr().getValue().matches("RSA%")
+ )
+ }
+}
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.ql b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.ql
index b77ad05ced0..ca1fd22e5f1 100644
--- a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.ql
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.ql
@@ -16,25 +16,8 @@ import DataFlow::PathGraph
import WindowsCng
import WindowsCngPQCVulnerableUsage
-// CNG-specific DataFlow configuration
-class BCryptConfiguration extends DataFlow::Configuration {
- BCryptConfiguration() {
- this = "BCryptConfiguration"
- }
- override predicate isSource(DataFlow::Node source) {
- source instanceof BCryptOpenAlgorithmProviderSource
- }
-
- override predicate isSink(DataFlow::Node sink) {
- sink instanceof BCryptOpenAlgorithmProviderSink
- }
-
- override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
- isWindowsCngAdditionalTaintStep( node1, node2)
- }
-}
from BCryptConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "PQC vulnerable algorithm $@ in use has been detected.",
- source.getNode().asExpr(), source.getNode().asExpr().toString()
\ No newline at end of file
+ source.getNode().asExpr(), source.getNode().asExpr().toString()
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
index 412941a48e5..0accc61f8db 100644
--- a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
@@ -1,33 +1,6 @@
import cpp
import WindowsCng
-//TODO: Verify NCrypt calls (parameters) & find all other APIs that should be included (i.e. decrypt, etc.)
-
-
-predicate isCallArgument(string funcGlobalName, Expr arg, int index){
- exists(Call c | c.getArgument(index) = arg and c.getTarget().hasGlobalName(funcGlobalName))
-}
-
-class BCryptSignHashArgumentSink extends BCryptOpenAlgorithmProviderSink {
- BCryptSignHashArgumentSink() { isCallArgument("BCryptSignHash", this.asExpr(), 0) }
-}
-
-class BCryptEncryptArgumentSink extends BCryptOpenAlgorithmProviderSink {
- BCryptEncryptArgumentSink() { isCallArgument("BCryptEncrypt", this.asExpr(), 0) }
- }
-
-
-class BCryptOpenAlgorithmProviderPqcVulnerableAlgorithmsSource extends BCryptOpenAlgorithmProviderSource {
- BCryptOpenAlgorithmProviderPqcVulnerableAlgorithmsSource() {
- this.asExpr() instanceof StringLiteral and
- (
- this.asExpr().getValue() in ["DH", "DSA", "ECDSA", "ECDH"] or
- this.asExpr().getValue().matches("ECDH%") or
- this.asExpr().getValue().matches("RSA%")
- )
- }
-}
-
predicate stepOpenAlgorithmProvider(DataFlow::Node node1, DataFlow::Node node2) {
exists(FunctionCall call |
// BCryptOpenAlgorithmProvider 2nd argument specifies the algorithm to be used
@@ -40,7 +13,10 @@ predicate stepOpenAlgorithmProvider(DataFlow::Node node1, DataFlow::Node node2)
predicate stepImportGenerateKeyPair(DataFlow::Node node1, DataFlow::Node node2) {
exists(FunctionCall call |
node1.asExpr() = call.getArgument(0) and
- exists(string name | name in ["BCryptImportKeyPair", "BCryptGenerateKeyPair"] and call.getTarget().hasGlobalName(name)) and
+ exists(string name |
+ name in ["BCryptImportKeyPair", "BCryptGenerateKeyPair"] and
+ call.getTarget().hasGlobalName(name)
+ ) and
node2.asDefiningArgument() = call.getArgument(1)
)
}
@@ -50,3 +26,19 @@ predicate isWindowsCngAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node n
or
stepImportGenerateKeyPair(node1, node2)
}
+
+
+// CNG-specific DataFlow configuration
+class BCryptConfiguration extends DataFlow::Configuration {
+ BCryptConfiguration() { this = "BCryptConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) {
+ source instanceof BCryptOpenAlgorithmProviderSource
+ }
+
+ override predicate isSink(DataFlow::Node sink) { sink instanceof BCryptOpenAlgorithmProviderSink }
+
+ override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
+ isWindowsCngAdditionalTaintStep(node1, node2)
+ }
+}
From 46bfa35c3516fe92d52a5fb3ca928e16446a0b0f Mon Sep 17 00:00:00 2001
From: "REDMOND\\brodes"
Date: Tue, 8 Nov 2022 12:52:41 -0500
Subject: [PATCH 171/631] More reformatting
---
.../WinCng/WindowsCng.qll | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
index 44900790b63..af82ea651e9 100644
--- a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
@@ -21,13 +21,14 @@ class BCryptEncryptArgumentSink extends BCryptOpenAlgorithmProviderSink {
}
// ----------------- SOURCES -----------------------
-class BCryptOpenAlgorithmProviderPqcVulnerableAlgorithmsSource extends BCryptOpenAlgorithmProviderSource {
- BCryptOpenAlgorithmProviderPqcVulnerableAlgorithmsSource() {
- this.asExpr() instanceof StringLiteral and
- (
- this.asExpr().getValue() in ["DH", "DSA", "ECDSA", "ECDH"] or
- this.asExpr().getValue().matches("ECDH%") or
- this.asExpr().getValue().matches("RSA%")
- )
- }
+predicate providerString(StringLiteral lit) {
+ exists(string s | s = lit.getValue() |
+ s in ["DH", "DSA", "ECDSA", "ECDH"] or
+ s.matches("ECDH%") or
+ s.matches("RSA%")
+ )
+}
+
+class BCryptOpenAlgorithmProviderPqcVulnerableAlgorithmsSource extends BCryptOpenAlgorithmProviderSource {
+ BCryptOpenAlgorithmProviderPqcVulnerableAlgorithmsSource() { providerString(this.asExpr()) }
}
From 5f2a42bb483c57e6cdcb48515c136ede6e7ebef8 Mon Sep 17 00:00:00 2001
From: "REDMOND\\brodes"
Date: Tue, 8 Nov 2022 12:53:34 -0500
Subject: [PATCH 172/631] Changing predicate names.
---
.../QuantumVulnerableDiscovery/WinCng/WindowsCng.qll | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
index af82ea651e9..5c775da7b97 100644
--- a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
@@ -21,7 +21,7 @@ class BCryptEncryptArgumentSink extends BCryptOpenAlgorithmProviderSink {
}
// ----------------- SOURCES -----------------------
-predicate providerString(StringLiteral lit) {
+predicate vulnProviderLiteral(StringLiteral lit) {
exists(string s | s = lit.getValue() |
s in ["DH", "DSA", "ECDSA", "ECDH"] or
s.matches("ECDH%") or
@@ -30,5 +30,5 @@ predicate providerString(StringLiteral lit) {
}
class BCryptOpenAlgorithmProviderPqcVulnerableAlgorithmsSource extends BCryptOpenAlgorithmProviderSource {
- BCryptOpenAlgorithmProviderPqcVulnerableAlgorithmsSource() { providerString(this.asExpr()) }
+ BCryptOpenAlgorithmProviderPqcVulnerableAlgorithmsSource() { vulnProviderLiteral(this.asExpr()) }
}
From b15a5a903074c51896aba8f33061ab08995d60f8 Mon Sep 17 00:00:00 2001
From: "REDMOND\\brodes"
Date: Tue, 8 Nov 2022 13:04:51 -0500
Subject: [PATCH 173/631] Comments and reorg.
---
.../WinCng/WindowsCng.qll | 40 ++++++++++++++-----
.../WinCng/WindowsCngPQCVulnerableUsage.ql | 1 -
.../WinCng/WindowsCngPQCVulnerableUsage.qll | 20 +++++++++-
3 files changed, 47 insertions(+), 14 deletions(-)
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
index 5c775da7b97..3869ba7d9fd 100644
--- a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
@@ -6,21 +6,19 @@ abstract class BCryptOpenAlgorithmProviderSink extends DataFlow::Node { }
abstract class BCryptOpenAlgorithmProviderSource extends DataFlow::Node { }
+// ------------------ Helper Predicates ----------------------
+/**
+ * Holds if there is a call with global name`funcGlobalName` with argument `arg` of that call
+ * at argument index `index`.
+ */
predicate isCallArgument(string funcGlobalName, Expr arg, int index) {
exists(Call c | c.getArgument(index) = arg and c.getTarget().hasGlobalName(funcGlobalName))
}
-//TODO: Verify NCrypt calls (parameters) & find all other APIs that should be included (i.e. decrypt, etc.)
-// ------------------ SINKS ----------------------
-class BCryptSignHashArgumentSink extends BCryptOpenAlgorithmProviderSink {
- BCryptSignHashArgumentSink() { isCallArgument("BCryptSignHash", this.asExpr(), 0) }
-}
-
-class BCryptEncryptArgumentSink extends BCryptOpenAlgorithmProviderSink {
- BCryptEncryptArgumentSink() { isCallArgument("BCryptEncrypt", this.asExpr(), 0) }
-}
-
-// ----------------- SOURCES -----------------------
+/**
+ * Holdes if StringLiteral `lit` has a string value indicative of a post quantum crypto
+ * vulnerable algorithm identifier.
+ */
predicate vulnProviderLiteral(StringLiteral lit) {
exists(string s | s = lit.getValue() |
s in ["DH", "DSA", "ECDSA", "ECDH"] or
@@ -29,6 +27,26 @@ predicate vulnProviderLiteral(StringLiteral lit) {
)
}
+//TODO: Verify NCrypt calls (parameters) & find all other APIs that should be included (i.e. decrypt, etc.)
+// ------------------ Default SINKS ----------------------
+/**
+ * Argument at index 0 of call to BCryptSignHash
+ */
+class BCryptSignHashArgumentSink extends BCryptOpenAlgorithmProviderSink {
+ BCryptSignHashArgumentSink() { isCallArgument("BCryptSignHash", this.asExpr(), 0) }
+}
+
+/**
+ * Argument at index 0 of call to BCryptEncrypt
+ */
+class BCryptEncryptArgumentSink extends BCryptOpenAlgorithmProviderSink {
+ BCryptEncryptArgumentSink() { isCallArgument("BCryptEncrypt", this.asExpr(), 0) }
+}
+
+// ----------------- Default SOURCES -----------------------
+/**
+ * A string identifier of known PQC vulnerable algorithms.
+ */
class BCryptOpenAlgorithmProviderPqcVulnerableAlgorithmsSource extends BCryptOpenAlgorithmProviderSource {
BCryptOpenAlgorithmProviderPqcVulnerableAlgorithmsSource() { vulnProviderLiteral(this.asExpr()) }
}
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.ql b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.ql
index ca1fd22e5f1..ea07019eca0 100644
--- a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.ql
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.ql
@@ -16,7 +16,6 @@ import DataFlow::PathGraph
import WindowsCng
import WindowsCngPQCVulnerableUsage
-
from BCryptConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "PQC vulnerable algorithm $@ in use has been detected.",
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
index 0accc61f8db..e017991a05e 100644
--- a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
@@ -1,6 +1,12 @@
import cpp
import WindowsCng
+/**
+ * Steps from input variable (argument 1) to output variable (argument 0)
+ * for CNG API BCryptOpenAlgorithmProvider.
+ * Argument 1 represents LPCWSTR (a string algorithm ID)
+ * Argument 0 represents BCRYPT_ALG_HANDLE
+ */
predicate stepOpenAlgorithmProvider(DataFlow::Node node1, DataFlow::Node node2) {
exists(FunctionCall call |
// BCryptOpenAlgorithmProvider 2nd argument specifies the algorithm to be used
@@ -10,6 +16,12 @@ predicate stepOpenAlgorithmProvider(DataFlow::Node node1, DataFlow::Node node2)
)
}
+/**
+ * Steps from input variable (argument 0) to output variable (argument 1)
+ * for CNG APIs BCryptImportKeyPair and BCryptGenerateKeyPair.
+ * Argument 0 represents a BCRYPT_ALG_HANDLE.
+ * Argument 1 represents a BCRYPT_KEY_HANDLE.
+ */
predicate stepImportGenerateKeyPair(DataFlow::Node node1, DataFlow::Node node2) {
exists(FunctionCall call |
node1.asExpr() = call.getArgument(0) and
@@ -21,14 +33,18 @@ predicate stepImportGenerateKeyPair(DataFlow::Node node1, DataFlow::Node node2)
)
}
+/**
+ * Additional DataFlow steps from input variables to output handle variables on CNG apis.
+ */
predicate isWindowsCngAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
stepOpenAlgorithmProvider(node1, node2)
or
stepImportGenerateKeyPair(node1, node2)
}
-
-// CNG-specific DataFlow configuration
+/**
+ * CNG-specific DataFlow configuration
+ */
class BCryptConfiguration extends DataFlow::Configuration {
BCryptConfiguration() { this = "BCryptConfiguration" }
From f5ce07d22c7983328894f955b6f28f547d4ad599 Mon Sep 17 00:00:00 2001
From: "REDMOND\\brodes"
Date: Tue, 8 Nov 2022 13:40:47 -0500
Subject: [PATCH 174/631] Class re-org
---
.../WinCng/WindowsCng.qll | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
index 3869ba7d9fd..ef7314c6426 100644
--- a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
@@ -33,14 +33,28 @@ predicate vulnProviderLiteral(StringLiteral lit) {
* Argument at index 0 of call to BCryptSignHash
*/
class BCryptSignHashArgumentSink extends BCryptOpenAlgorithmProviderSink {
- BCryptSignHashArgumentSink() { isCallArgument("BCryptSignHash", this.asExpr(), 0) }
+ int index;
+ string funcName;
+
+ BCryptSignHashArgumentSink() {
+ index = 0 and
+ funcName = "BCryptSignHash" and
+ isCallArgument(funcName, this.asExpr(), index)
+ }
}
/**
* Argument at index 0 of call to BCryptEncrypt
*/
class BCryptEncryptArgumentSink extends BCryptOpenAlgorithmProviderSink {
- BCryptEncryptArgumentSink() { isCallArgument("BCryptEncrypt", this.asExpr(), 0) }
+ int index;
+ string funcName;
+
+ BCryptEncryptArgumentSink() {
+ index = 0 and
+ funcName = "BCryptEncrypt" and
+ isCallArgument(funcName, this.asExpr(), index)
+ }
}
// ----------------- Default SOURCES -----------------------
From 24382453c3aa7bcc2cbb880b31f5b02494df8528 Mon Sep 17 00:00:00 2001
From: "REDMOND\\brodes"
Date: Tue, 8 Nov 2022 14:21:10 -0500
Subject: [PATCH 175/631] More comments, and added sink BCryptGenerateKeyPair
---
.../WinCng/WindowsCng.qll | 27 +++++++++++++++++--
.../WinCng/WindowsCngPQCVulnerableUsage.qll | 6 +++++
2 files changed, 31 insertions(+), 2 deletions(-)
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
index ef7314c6426..d3f499eaadf 100644
--- a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
@@ -2,8 +2,14 @@ import cpp
import DataFlow::PathGraph
import semmle.code.cpp.dataflow.TaintTracking
+/**
+ * Base abstract class to be extended to allow indirect extensions of vulnerable sinks.
+ */
abstract class BCryptOpenAlgorithmProviderSink extends DataFlow::Node { }
+/**
+ * Base abstract class to be extended to allow indirect extensions of vulnerable sources.
+ */
abstract class BCryptOpenAlgorithmProviderSource extends DataFlow::Node { }
// ------------------ Helper Predicates ----------------------
@@ -30,7 +36,8 @@ predicate vulnProviderLiteral(StringLiteral lit) {
//TODO: Verify NCrypt calls (parameters) & find all other APIs that should be included (i.e. decrypt, etc.)
// ------------------ Default SINKS ----------------------
/**
- * Argument at index 0 of call to BCryptSignHash
+ * Argument at index 0 of call to BCryptSignHash:
+ * [in] BCRYPT_KEY_HANDLE hKey,
*/
class BCryptSignHashArgumentSink extends BCryptOpenAlgorithmProviderSink {
int index;
@@ -44,7 +51,23 @@ class BCryptSignHashArgumentSink extends BCryptOpenAlgorithmProviderSink {
}
/**
- * Argument at index 0 of call to BCryptEncrypt
+ * Argument at index 0 of call to BCryptGenerateKeyPair:
+ * [in, out] BCRYPT_ALG_HANDLE hAlgorithm,
+ */
+class BCryptGenerateKeyPair extends BCryptOpenAlgorithmProviderSink {
+ int index;
+ string funcName;
+
+ BCryptGenerateKeyPair() {
+ index = 0 and
+ funcName = "BCryptGenerateKeyPair" and
+ isCallArgument(funcName, this.asExpr(), index)
+ }
+}
+
+/**
+ * Argument at index 0 of call to BCryptEncrypt:
+ * [in, out] BCRYPT_KEY_HANDLE hKey,
*/
class BCryptEncryptArgumentSink extends BCryptOpenAlgorithmProviderSink {
int index;
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
index e017991a05e..436ef57926a 100644
--- a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
@@ -48,10 +48,16 @@ predicate isWindowsCngAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node n
class BCryptConfiguration extends DataFlow::Configuration {
BCryptConfiguration() { this = "BCryptConfiguration" }
+ /**
+ * Uses indirect extensions of BCryptOpenAlgorithmProviderSource
+ */
override predicate isSource(DataFlow::Node source) {
source instanceof BCryptOpenAlgorithmProviderSource
}
+ /**
+ * Uses indirect extensions of BCryptOpenAlgorithmProviderSink
+ */
override predicate isSink(DataFlow::Node sink) { sink instanceof BCryptOpenAlgorithmProviderSink }
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
From 60a67dce7380926d373afc4077a7f1634d90e8da Mon Sep 17 00:00:00 2001
From: "REDMOND\\brodes"
Date: Tue, 8 Nov 2022 14:57:34 -0500
Subject: [PATCH 176/631] Adding NCryptEncrypt sink
---
.../WinCng/WindowsCng.qll | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
index d3f499eaadf..4c426dd38b1 100644
--- a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
@@ -80,6 +80,21 @@ class BCryptEncryptArgumentSink extends BCryptOpenAlgorithmProviderSink {
}
}
+/**
+ * Argument at index 0 of call to NCryptEncrypt:
+ * [in] NCRYPT_KEY_HANDLE hKey,
+ */
+class NCryptEncryptArgumentSink extends BCryptOpenAlgorithmProviderSink {
+ int index;
+ string funcName;
+
+ NCryptEncryptArgumentSink() {
+ index = 0 and
+ funcName = "NCryptEncrypt" and
+ isCallArgument(funcName, this.asExpr(), index)
+ }
+}
+
// ----------------- Default SOURCES -----------------------
/**
* A string identifier of known PQC vulnerable algorithms.
From 59ca3b26cda95e79cd635f05330f149af15300e8 Mon Sep 17 00:00:00 2001
From: "REDMOND\\brodes"
Date: Tue, 8 Nov 2022 15:00:21 -0500
Subject: [PATCH 177/631] Adding SslEncryptPacket sink.
---
.../WinCng/WindowsCng.qll | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
index 4c426dd38b1..ba400e9ed4c 100644
--- a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
@@ -95,6 +95,22 @@ class NCryptEncryptArgumentSink extends BCryptOpenAlgorithmProviderSink {
}
}
+
+/**
+ * Argument at index 1 of call to NCryptEncrypt:
+ * _Inout_ NCRYPT_KEY_HANDLE hKey,
+ */
+class SslEncryptPacketArgumentSink extends BCryptOpenAlgorithmProviderSink {
+ int index;
+ string funcName;
+
+ SslEncryptPacketArgumentSink() {
+ index = 1 and
+ funcName = "SslEncryptPacket" and
+ isCallArgument(funcName, this.asExpr(), index)
+ }
+}
+
// ----------------- Default SOURCES -----------------------
/**
* A string identifier of known PQC vulnerable algorithms.
From 68d668e71907d0cfada55fd73eeb364e9b1f27f3 Mon Sep 17 00:00:00 2001
From: "REDMOND\\brodes"
Date: Tue, 8 Nov 2022 15:40:00 -0500
Subject: [PATCH 178/631] Final bug fixes and new sink.
---
.../WinCng/WindowsCng.qll | 46 +++++++++----------
.../WinCng/WindowsCngPQCVulnerableUsage.qll | 10 ++--
2 files changed, 27 insertions(+), 29 deletions(-)
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
index ba400e9ed4c..72a4d7df8a9 100644
--- a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCng.qll
@@ -33,8 +33,22 @@ predicate vulnProviderLiteral(StringLiteral lit) {
)
}
-//TODO: Verify NCrypt calls (parameters) & find all other APIs that should be included (i.e. decrypt, etc.)
// ------------------ Default SINKS ----------------------
+/**
+ * Argument at index 0 of call to NCryptSignHash:
+ * [in] NCRYPT_KEY_HANDLE hKey
+ */
+class NCryptSignHashArgumentSink extends BCryptOpenAlgorithmProviderSink {
+ int index;
+ string funcName;
+
+ NCryptSignHashArgumentSink() {
+ index = 0 and
+ funcName = "NCryptSignHash " and
+ isCallArgument(funcName, this.asExpr(), index)
+ }
+}
+
/**
* Argument at index 0 of call to BCryptSignHash:
* [in] BCRYPT_KEY_HANDLE hKey,
@@ -85,30 +99,14 @@ class BCryptEncryptArgumentSink extends BCryptOpenAlgorithmProviderSink {
* [in] NCRYPT_KEY_HANDLE hKey,
*/
class NCryptEncryptArgumentSink extends BCryptOpenAlgorithmProviderSink {
- int index;
- string funcName;
+ int index;
+ string funcName;
- NCryptEncryptArgumentSink() {
- index = 0 and
- funcName = "NCryptEncrypt" and
- isCallArgument(funcName, this.asExpr(), index)
- }
-}
-
-
-/**
- * Argument at index 1 of call to NCryptEncrypt:
- * _Inout_ NCRYPT_KEY_HANDLE hKey,
- */
-class SslEncryptPacketArgumentSink extends BCryptOpenAlgorithmProviderSink {
- int index;
- string funcName;
-
- SslEncryptPacketArgumentSink() {
- index = 1 and
- funcName = "SslEncryptPacket" and
- isCallArgument(funcName, this.asExpr(), index)
- }
+ NCryptEncryptArgumentSink() {
+ index = 0 and
+ funcName = "NCryptEncrypt" and
+ isCallArgument(funcName, this.asExpr(), index)
+ }
}
// ----------------- Default SOURCES -----------------------
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
index 436ef57926a..d37fdff66c9 100644
--- a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
@@ -25,11 +25,11 @@ predicate stepOpenAlgorithmProvider(DataFlow::Node node1, DataFlow::Node node2)
predicate stepImportGenerateKeyPair(DataFlow::Node node1, DataFlow::Node node2) {
exists(FunctionCall call |
node1.asExpr() = call.getArgument(0) and
- exists(string name |
- name in ["BCryptImportKeyPair", "BCryptGenerateKeyPair"] and
- call.getTarget().hasGlobalName(name)
- ) and
- node2.asDefiningArgument() = call.getArgument(1)
+ exists(string name | call.getTarget().hasGlobalName(name) |
+ name = "BCryptImportKeyPair" and node2.asDefiningArgument() = call.getArgument(3)
+ or
+ name = "BCryptGenerateKeyPair" and node2.asDefiningArgument() = call.getArgument(1)
+ )
)
}
From 345b7e4c4e9440662c617a273d039adb89e6372e Mon Sep 17 00:00:00 2001
From: "REDMOND\\brodes"
Date: Tue, 8 Nov 2022 15:43:23 -0500
Subject: [PATCH 179/631] Comments.
---
.../WinCng/WindowsCngPQCVulnerableUsage.qll | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
index d37fdff66c9..db4e7215e35 100644
--- a/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
+++ b/cpp/ql/src/experimental/campaigns/nccoe-pqc-migration/QuantumVulnerableDiscovery/WinCng/WindowsCngPQCVulnerableUsage.qll
@@ -19,8 +19,12 @@ predicate stepOpenAlgorithmProvider(DataFlow::Node node1, DataFlow::Node node2)
/**
* Steps from input variable (argument 0) to output variable (argument 1)
* for CNG APIs BCryptImportKeyPair and BCryptGenerateKeyPair.
- * Argument 0 represents a BCRYPT_ALG_HANDLE.
- * Argument 1 represents a BCRYPT_KEY_HANDLE.
+ * BCryptGenerateKeyPair:
+ * Argument 0 represents a BCRYPT_ALG_HANDLE.
+ * Argument 1 represents a BCRYPT_KEY_HANDLE.
+ * BCryptImportKeyPair:
+ * Argument 0 represents a BCRYPT_ALG_HANDLE.
+ * Argument 3 represents a BCRYPT_KEY_HANDLE.
*/
predicate stepImportGenerateKeyPair(DataFlow::Node node1, DataFlow::Node node2) {
exists(FunctionCall call |
From 820d94098fb9c258e827bcb12822e47b1cac010e Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Thu, 1 Dec 2022 09:56:51 +0100
Subject: [PATCH 180/631] python: port `py/comparison-using-is` see triage
[here](https://github.com/github/codeql-python-team/issues/628#issuecomment-1328933001)
- no longer try to interpret the class of operands - simply alert in clear
bad cases of uninterned literals - surprisingly(?), all tests still pass
---
.../Expressions/IncorrectComparisonUsingIs.ql | 57 ++++++++++++++++---
1 file changed, 49 insertions(+), 8 deletions(-)
diff --git a/python/ql/src/Expressions/IncorrectComparisonUsingIs.ql b/python/ql/src/Expressions/IncorrectComparisonUsingIs.ql
index 5352dc9c9fa..aab2ba6bfa1 100644
--- a/python/ql/src/Expressions/IncorrectComparisonUsingIs.ql
+++ b/python/ql/src/Expressions/IncorrectComparisonUsingIs.ql
@@ -11,16 +11,57 @@
*/
import python
-import IsComparisons
-from Compare comp, Cmpop op, ClassValue c, string alt
-where
- invalid_portable_is_comparison(comp, op, c) and
- not cpython_interned_constant(comp.getASubExpression()) and
- (
- op instanceof Is and alt = "=="
+/** Holds if the comparison `comp` uses `is` or `is not` (represented as `op`) to compare its `left` and `right` arguments. */
+predicate comparison_using_is(Compare comp, ControlFlowNode left, Cmpop op, ControlFlowNode right) {
+ exists(CompareNode fcomp | fcomp = comp.getAFlowNode() |
+ fcomp.operands(left, op, right) and
+ (op instanceof Is or op instanceof IsNot)
+ )
+}
+
+private predicate cpython_interned_value(Expr e) {
+ exists(string text | text = e.(StrConst).getText() |
+ text.length() = 0
or
- op instanceof IsNot and alt = "!="
+ text.length() = 1 and text.regexpMatch("[U+0000-U+00ff]")
+ )
+ or
+ exists(int i | i = e.(IntegerLiteral).getN().toInt() | -5 <= i and i <= 256)
+ or
+ exists(Tuple t | t = e and not exists(t.getAnElt()))
+}
+
+predicate uninterned_literal(Expr e) {
+ (
+ e instanceof StrConst
+ or
+ e instanceof IntegerLiteral
+ or
+ e instanceof FloatLiteral
+ or
+ e instanceof Dict
+ or
+ e instanceof List
+ or
+ e instanceof Tuple
+ ) and
+ not cpython_interned_value(e)
+}
+
+from Compare comp, Cmpop op, string alt
+where
+ exists(ControlFlowNode left, ControlFlowNode right |
+ comparison_using_is(comp, left, op, right) and
+ (
+ op instanceof Is and alt = "=="
+ or
+ op instanceof IsNot and alt = "!="
+ )
+ |
+ uninterned_literal(left.getNode())
+ or
+ uninterned_literal(right.getNode())
)
select comp,
"Values compared using '" + op.getSymbol() +
From b3a1d02d8cb4b8b2a6192abd47b3a7d40cd0b98a Mon Sep 17 00:00:00 2001
From: Asger F
Date: Wed, 30 Nov 2022 10:50:36 +0100
Subject: [PATCH 181/631] JS: Add data extension docs
---
.../codeql-for-javascript.rst | 3 +
...tomizing-library-models-for-javascript.rst | 392 ++++++++++++++++++
2 files changed, 395 insertions(+)
create mode 100644 docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
diff --git a/docs/codeql/codeql-language-guides/codeql-for-javascript.rst b/docs/codeql/codeql-language-guides/codeql-for-javascript.rst
index 11a4c5e456c..3219b1f141d 100644
--- a/docs/codeql/codeql-language-guides/codeql-for-javascript.rst
+++ b/docs/codeql/codeql-language-guides/codeql-for-javascript.rst
@@ -13,6 +13,7 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
codeql-library-for-typescript
analyzing-data-flow-in-javascript-and-typescript
using-flow-labels-for-precise-data-flow-analysis
+ customizing-library-models-for-javascript
specifying-additional-remote-flow-sources-for-javascript
using-type-tracking-for-api-modeling
abstract-syntax-tree-classes-for-working-with-javascript-and-typescript-programs
@@ -30,6 +31,8 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
- :doc:`Specifying remote flow sources for JavaScript `: You can model potential sources of untrusted user input in your code without making changes to the CodeQL standard library by specifying extra remote flow sources in an external file.
+- :doc:`Customizing library models for JavaScript `: You can model potential sources of untrusted user input in your code without making changes to the CodeQL standard library by specifying extra remote flow sources in an external file.
+
- :doc:`Using type tracking for API modeling `: You can track data through an API by creating a model using the CodeQL type-tracking library for JavaScript.
- :doc:`Abstract syntax tree classes for working with JavaScript and TypeScript programs `: CodeQL has a large selection of classes for representing the abstract syntax tree of JavaScript and TypeScript programs.
diff --git a/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst b/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
new file mode 100644
index 00000000000..d225e9485f6
--- /dev/null
+++ b/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
@@ -0,0 +1,392 @@
+.. \_customizing-library-models-for-javascript:
+
+Customizing Library Models for JavaScript
+=========================================
+
+.. pull-quote::
+
+ Beta Notice - Unstable API
+
+ Library customization using data extensions is currently in beta and subject to change.
+
+ Breaking changes to this format may occur while in beta.
+
+The JavaScript analysis can be customized by adding library models in data extension files.
+
+A data extension for JavaScript is a YAML file of form:
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/javascript-all
+ extensible:
+ data:
+ -
+ -
+ - ...
+
+The data extension can contribute to the following extension points:
+
+- **sourceModel**\(type, path, kind)
+- **sinkModel**\(type, path, kind)
+- **typeModel**\(type1, type2, path)
+- **summaryModel**\(type, path, input, output, kind)
+
+TODO: mention how to actually load the data extension and/or link to page about data extensions.
+
+We'll explain how to use these using a few examples, and provide a `reference <#reference-1>`_ at the end of this article.
+
+Example: Taint sink in the 'execa' package
+------------------------------------------
+
+In this example, we'll show how to add the argument passed to **execa** below as a command-line injection sink:
+
+.. code-block:: js
+
+ import { shell } from "execa";
+ shell(cmd); // <-- add 'cmd' as a taint sink
+
+This sink is already recognized by the CodeQL JS analysis, but for the sake of example we'll show how it could be added as an extension.
+
+This can be achieved with the following data extension:
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/javascript-all
+ extensible: sinkModel
+ data:
+ - ["execa", "Member[shell].Argument[0]", "command-line-injection"]
+
+To break this down:
+
+- Since we're adding a new sink, we add a tuple to the **sinkModel** extension point.
+- The first column, **"execa"**, identifies a set of values from which to begin the search for the sink.
+ The string **"execa"** means we start at the places where the codebase imports the NPM package **execa**.
+- The second column is an access path that is evaluated from left to right, starting at the values that were identified by the first column.
+
+ - **Member[shell]** selects accesses to the **shell** member of the **execa** package.
+ - **Argument[0]** selects the first argument to calls to that member.
+
+- **command-line-injection** indicates that this is considered a sink for the command injection query.
+
+Example: Taint sources from window 'message' events
+---------------------------------------------------
+
+In this example, we'll show how the **event.data** expression below could be marked as a remote flow source:
+
+.. code-block:: js
+
+ window.addEventListener("message", function (event) {
+ let data = event.data; // <-- add 'event.data' as a taint source
+ });
+
+This source is already known by the CodeQL JS analysis, but we'll show how it could be added as an extension. This can be achieved with the following extension:
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/javascript-all
+ extensible: sourceModel
+ data:
+ - [
+ "global",
+ "Member[addEventListener].Argument[1].Parameter[0].Member[data]",
+ "remote-flow",
+ ]
+
+To break this down:
+
+- Since we're adding a new taint source, we add a tuple to the **sourceModel** extension point.
+- The first column, **"global"**, begins the search at references to the global object (also known as **window**). This is a special JavaScript object that contains all global variables and methods.
+- **Member[addEventListener]** selects accesses to the **addEventListener** member.
+- **Argument[1]** selects the second argument of calls to that member (the argument containing the callback).
+- **Parameter[0]** selects the first parameter of the callback (the parameter named **event**).
+- **Member[data]** selects accesses to the **data** property of the event object.
+- Finally, the kind **remote-flow** indicates that this is considered a source of remote flow.
+
+Continued example: Restricting the event type
+---------------------------------------------
+
+The model above treats all events as sources of remote flow, not just **message** events.
+For example, it would also pick up this irrelevant source:
+
+.. code-block:: js
+
+ window.addEventListener("onclick", function (event) {
+ let data = event.data; // <-- 'event.data' became a spurious taint source
+ });
+
+
+We can refine the model by adding the **WithStringArgument** component to restrict the set of calls being considered:
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/javascript-all
+ extensible: sourceModel
+ data:
+ - [
+ "global",
+ "Member[addEventListener].WithStringArgument[0=message].Argument[1].Parameter[0].Member[data]",
+ "remote-flow",
+ ]
+
+The **WithStringArgument[0=message]** component here selects the subset of calls to **addEventListener** where the first argument is a string literal with the value **"message"**.
+
+Example: Using types to add MySQL injection sinks
+-------------------------------------------------
+
+In this example, we'll show how to add the following SQL injection sink:
+
+.. code-block:: ts
+
+ import { Connection } from "mysql";
+
+ function submit(connection: Connection, q: string) {
+ connection.query(q); // <-- add 'q' as a SQL injection sink
+ }
+
+We can recognize this using the following extension:
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/javascript-all
+ extensible: sinkModel
+ data:
+ - ["mysql.Connection", "Member[query].Argument[0]", "sql-injection"]
+
+To break this down:
+
+- The first column, **"mysql.Connection"**, begins the search at any expression whose value is known to be an instance of
+ the **Connection** type from the **mysql** package. This will select the **connection** parameter above because of its type annotation.
+- **Member[query]** selects the **query** member from the connection object.
+- **Argument[0]** selects the first argument of a call to that member.
+- **sql-injection** indicates that this is considered a sink for the SQL injection query.
+
+This works for the example above because the **connection** parameter has a type annotation that matches what the model is looking for.
+
+In the next section, we'll show how to generalize the model to handle the absense of type annotations.
+
+Continued example: Dealing with untyped code
+--------------------------------------------
+
+Suppose we want the model from above to detect the sink in this snippet:
+
+.. code-block:: js
+
+ import { getConnection } from "@example/db";
+ let connection = getConnection();
+ connection.query(q); // <-- add 'q' as a SQL injection sink
+
+There is no type annotation on **connection**, and there is no indication of what **getConnection()** returns.
+Using a **typeModel** tuple we can tell our model that this function returns an instance of **mysql.Connection**:
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/javascript-all
+ extensible: typeModel
+ data:
+ - ["mysql.Connection", "@example/db", "Member[getConnection].ReturnValue"]
+
+To break this down:
+
+- Since we're providing type information, we add a tuple to the **typeModel** extension point.
+- The first column, **"mysql.Connection"**, names the type that we're adding a new definition for.
+- The second column, **"@example/db"**, begins the search at imports of the hypothetical NPM package **@example/db**.
+- **Member[getConnection]** selects references to the **getConnection** member from that package.
+- **ReturnValue** selects the return value from a call to that member.
+
+The new model states that the return value of **getConnection()** has type **mysql.Connection**.
+Combining this with the sink model we added earlier, the sink in the example is detected by the model.
+
+The mechanism used here is how library models work for both TypeScript and plain JavaScript.
+A good library model contains **typeModel** tuples to ensure it works even in codebases without type annotations.
+For example, the **mysql** model that is included with the CodeQL JS analysis includes this type definition (among many others):
+
+.. code-block:: yaml
+
+ - ["mysql.Connection", "mysql", "Member[createConnection].ReturnValue"]
+
+Reference
+=========
+
+Extension points
+----------------
+
+sourceModel(type, path, kind)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Adds a new taint source. Most taint-tracking queries will use the new source.
+
+- **type**: Name of a type from which to evaluate **path**.
+- **path**: Access path leading to the source.
+- **kind**: Kind of source to add. Currently only **remote-flow** is used.
+
+Example:
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/javascript-all
+ extensible: sourceModel
+ data:
+ - ["global", "Member[user].Member[name]", "remote-flow"]
+
+sinkModel(type, path, kind)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Adds a new taint sink. Sinks are query-specific and will usually affect one or two queries.
+
+- **type**: Name of a type from which to evaluate **path**.
+- **path**: Access path leading to the sink.
+- **kind**: Kind of sink to add. See `sink kinds <#sink-kinds>`_ for a list of supported kinds.
+
+Example:
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/javascript-all
+ extensible: sinkModel
+ data:
+ - ["global", "Member[eval].Argument[0]", "code-injection"]
+
+summaryModel(type, path, input, output, kind)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Adds flow through a function call.
+
+- **type**: Name of a type from which to evaluate **path**.
+- **path**: Access path leading to a function call.
+- **input**: Path relative to the function call that leads to input of the flow.
+- **output**: Path relative to the function call leading to the output of the flow.
+- **kind**: Kind of summary to add. Can be **taint** for taint-propagating flow, or **value** for value-preserving flow.
+
+Example:
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/javascript-all
+ extensible: summaryModel
+ data:
+ - [
+ "global",
+ "Member[decodeURIComponent]",
+ "Argument[0]",
+ "ReturnValue",
+ "taint",
+ ]
+
+typeModel(type1, type2, path)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Adds a new definition of a type.
+
+- **type1**: Name of the type to define.
+- **type2**: Name of the type from which to evaluate **path**.
+- **path**: Access path leading from **type2** to **type1**.
+
+Example:
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/javascript-all
+ extensible: typeModel
+ data:
+ - [
+ "mysql.Connection",
+ "@example/db",
+ "Member[getConnection].ReturnValue",
+ ]
+
+Types
+-----
+
+A type is a string that identifies a set of values.
+In each of the extension points mentioned above, the first column is always the name of a type.
+A type can be defined by adding **typeModel** tuples for that type. Additionally, the following built-in types are available:
+
+- The name of an NPM package matches imports of that package. For example, the type **express** matches the expression **require("express")**. If the package name includes dots, it must be surrounded by single quotes, such as in **'lodash.escape'**.
+- The type **global** identifies the global object, also known as **window**. In JavaScript, global variables are properties of the global object, so global variables can be identified using this type. (This type also matches imports of the NPM package named **global**, which is a package that happens to export the global object.)
+- A qualified type name of form **.** identifies expressions of type **** from ****. For example, **mysql.Connection** identifies expression of type **Connection** from the **mysql** package. Note that this only works if type annotations are present in the codebase, or if sufficient **typeModel** tuples have been provided for that type.
+
+Access paths
+------------
+
+The **path**, **input**, and **output** columns consist of a **.**-separated list of components, which is evaluted from left to right, with each step selecting a new set of values derived from the previous set of values.
+
+The following components are supported:
+
+- **Argument[**\ `number`\ **]** selects the argument at the given index.
+- **Argument[this]** selects the receiver of a method call.
+- **Parameter[**\ `number`\ **]** selects the parameter at the given index.
+- **Parameter[this]** selects the **this** parameter of a function.
+- **ReturnValue** selects the return value of a function or call.
+- **Member[**\ `name`\ **]** selects the property with the given name.
+- **AnyMember** selects any property regardless of name.
+- **ArrayElement** selects an element of an array.
+- **Element** selects an element of an array, iterator, or set object.
+- **MapValue** selects a value of a map object.
+- **Awaited** selects the value of a promise.
+- **Instance** selects instances of a class.
+
+The following components are called "call site filters". They select a subset of the previously-selected calls, if the call fits certain criteria:
+
+- **WithArity[**\ `number`\ **]** selects the subset of calls that have the given number of arguments.
+- **WithStringArgument[**\ `number`\ **=**\ `value`\ **]** selects the subset of calls where the argument at the given index is a string literal with the given value.
+
+Components related to decorators:
+
+- **DecoratedClass** selects a class that has the current value as a decorator. For example, **Member[Component].DecoratedClass** selects any class that is decorated with **@Component**.
+- **DecoratedParameter** selects a parameter that is decorated by the current value.
+- **DecoratedMember** selects a method, field, or accessor that is decorated by the current value.
+
+Some additional notes about the syntax of operands:
+
+- Multiple operands may be given to a single component, as a shorthand for the union of the operands. For example, **Member[foo,bar]** matches the union of **Member[foo]** and **Member[bar]**.
+- Numeric operands to **Argument**, **Parameter**, and **WithArity** may be given as an interval. For example, **Argument[0..2]** matches argument 0, 1, or 2.
+- **Argument[N-1]** selects the last argument of a call, and **Parameter[N-1]** selects the last parameter of a function, with **N-2** being the second-to-last and so on.
+
+Kinds
+-----
+
+Source kinds
+~~~~~~~~~~~~
+
+- **remote-flow**: A generic source of remote flow. Most taint-tracking queries will use such a source. Currently this is the only supported source kind.
+
+Sink kinds
+~~~~~~~~~~
+
+Unlike sources, sinks tend to be highly query-specific, rarely affecting more than one or two queries. Not every query supports customizable sinks. If there is no suitable sink kind below, it is best to add a new query instead.
+
+- **code-injection**: A sink that can be used to inject code, such as in calls to **eval**.
+- **command-line-injection**: A sink that can be used to inject shell commands, such as in calls to **child_process.spawn**.
+- **path-injection**: A sink that can be used for path injection in a file system access, such as in a calls to **fs.readFile**.
+- **sql-injection**: A sink that can be used for SQL injection, such as in a MySQL **query** call.
+- **nosql-injection**: A sink that can be used for NoSQL injection, such as in a MongoDB **findOne** call.
+- **html-injection**: A sink that can be used for HTML injection, such as in a jQuery **$()** call.
+- **request-forgery**: A sink that controls the URL of a request, such as in a **fetch** call.
+- **url-redirection**: A sink that can be used to redirect the user to a malicious URL.
+- **unsafe-deserialization**: A deserialization sink that can lead to code execution or other unsafe behaviour, such as an unsafe YAML parser.
+
+Summary kinds
+~~~~~~~~~~~~~
+
+- **taint**: A summary that propagates taint. This means the output is not necessarily equal to the input, but it was derived from the input in an unrestrictive way. An attacker who controls the input will have significant control over the output as well.
+- **value**: A summary that preserves the value of the input or creates a copy of the input such that all of its object properties are preserved.
From 9ce0c6024788355b5d93f91bc612de68d122beca Mon Sep 17 00:00:00 2001
From: Asger F
Date: Fri, 9 Dec 2022 12:37:39 +0100
Subject: [PATCH 182/631] JS: Rephrase link in ToC
---
docs/codeql/codeql-language-guides/codeql-for-javascript.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/codeql/codeql-language-guides/codeql-for-javascript.rst b/docs/codeql/codeql-language-guides/codeql-for-javascript.rst
index 3219b1f141d..0a067e87799 100644
--- a/docs/codeql/codeql-language-guides/codeql-for-javascript.rst
+++ b/docs/codeql/codeql-language-guides/codeql-for-javascript.rst
@@ -31,7 +31,7 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
- :doc:`Specifying remote flow sources for JavaScript `: You can model potential sources of untrusted user input in your code without making changes to the CodeQL standard library by specifying extra remote flow sources in an external file.
-- :doc:`Customizing library models for JavaScript `: You can model potential sources of untrusted user input in your code without making changes to the CodeQL standard library by specifying extra remote flow sources in an external file.
+- :doc:`Customizing library models for JavaScript `: You can model the libraries used in your code without making changes to the CodeQL standard library by specifying source, sink, and flow summaries in a data extension file.
- :doc:`Using type tracking for API modeling `: You can track data through an API by creating a model using the CodeQL type-tracking library for JavaScript.
From 111cabbfc1f47a8c6f836d8e077a09cfc5a4af43 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Fri, 9 Dec 2022 12:51:46 +0100
Subject: [PATCH 183/631] JS: Add deprecation notice to page about JSON-based
format
---
.../codeql-language-guides/codeql-for-javascript.rst | 2 --
.../customizing-library-models-for-javascript.rst | 2 +-
...-additional-remote-flow-sources-for-javascript.rst | 11 +++++++----
3 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/docs/codeql/codeql-language-guides/codeql-for-javascript.rst b/docs/codeql/codeql-language-guides/codeql-for-javascript.rst
index 0a067e87799..98f8d27a155 100644
--- a/docs/codeql/codeql-language-guides/codeql-for-javascript.rst
+++ b/docs/codeql/codeql-language-guides/codeql-for-javascript.rst
@@ -29,8 +29,6 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
- :doc:`Using flow labels for precise data flow analysis `: You can associate flow labels with each value tracked by the flow analysis to determine whether the flow contains potential vulnerabilities.
-- :doc:`Specifying remote flow sources for JavaScript `: You can model potential sources of untrusted user input in your code without making changes to the CodeQL standard library by specifying extra remote flow sources in an external file.
-
- :doc:`Customizing library models for JavaScript `: You can model the libraries used in your code without making changes to the CodeQL standard library by specifying source, sink, and flow summaries in a data extension file.
- :doc:`Using type tracking for API modeling `: You can track data through an API by creating a model using the CodeQL type-tracking library for JavaScript.
diff --git a/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst b/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
index d225e9485f6..60075c23921 100644
--- a/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
+++ b/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
@@ -1,4 +1,4 @@
-.. \_customizing-library-models-for-javascript:
+.. _customizing-library-models-for-javascript:
Customizing Library Models for JavaScript
=========================================
diff --git a/docs/codeql/codeql-language-guides/specifying-additional-remote-flow-sources-for-javascript.rst b/docs/codeql/codeql-language-guides/specifying-additional-remote-flow-sources-for-javascript.rst
index cc3fe5e9469..81cc1ff854c 100644
--- a/docs/codeql/codeql-language-guides/specifying-additional-remote-flow-sources-for-javascript.rst
+++ b/docs/codeql/codeql-language-guides/specifying-additional-remote-flow-sources-for-javascript.rst
@@ -3,13 +3,16 @@
Specifying additional remote flow sources for JavaScript
========================================================
-You can model potential sources of untrusted user input in your code without making changes to the CodeQL standard library by specifying extra remote flow sources in an external file.
-
.. pull-quote::
- Note
+ Deprecation Notice
- Specifying remote flow sources in external files is currently in beta and subject to change.
+ Specifying remote flow sources with the JSON format described here is soon to be deprecated
+ and will be removed in the future.
+
+ See :ref:`Customizing Library Models for JavaScript `.
+
+You can model potential sources of untrusted user input in your code without making changes to the CodeQL standard library by specifying extra remote flow sources in an external file.
As mentioned in the :ref:`Data flow cheat sheet for JavaScript `, the CodeQL libraries for JavaScript
provide a class `RemoteFlowSource `__ to represent sources of untrusted user input, sometimes also referred to as remote flow
From bbce52535a84e38ffd8fc1036fbcdf7a758c935e Mon Sep 17 00:00:00 2001
From: Asger F
Date: Fri, 9 Dec 2022 12:59:09 +0100
Subject: [PATCH 184/631] JS: Add clarification in another customization doc
---
javascript/documentation/library-customization.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/documentation/library-customization.rst b/javascript/documentation/library-customization.rst
index 62d8756e0a1..b06e5b5cab1 100644
--- a/javascript/documentation/library-customization.rst
+++ b/javascript/documentation/library-customization.rst
@@ -2,7 +2,7 @@ Customizing the JavaScript analysis
===================================
This document describes the main extension points offered by the JavaScript analysis for customizing
-analysis behavior without editing the queries or libraries themselves.
+analysis behavior from CodeQL without editing the queries or libraries themselves.
Customization mechanisms
------------------------
From fab798d65428f834a02dcef2c12d8158a5940d33 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Thu, 15 Dec 2022 11:45:53 +0100
Subject: [PATCH 185/631] JS: Add some text at the beginning of the reference
---
.../customizing-library-models-for-javascript.rst | 2 ++
1 file changed, 2 insertions(+)
diff --git a/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst b/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
index 60075c23921..99147a63a19 100644
--- a/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
+++ b/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
@@ -219,6 +219,8 @@ For example, the **mysql** model that is included with the CodeQL JS analysis in
Reference
=========
+The following sections provide reference material for extension points, access paths, types, and kinds.
+
Extension points
----------------
From 1c3dd93cbeda2d48fb6e6329fb17a38af44ab0b7 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Thu, 15 Dec 2022 11:46:27 +0100
Subject: [PATCH 186/631] JS: Rename section and remove broken link
It seems Sphinx just isn't capable of rendering these intra-document links.
---
.../customizing-library-models-for-javascript.rst | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst b/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
index 99147a63a19..0dd19dea8de 100644
--- a/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
+++ b/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
@@ -35,7 +35,7 @@ The data extension can contribute to the following extension points:
TODO: mention how to actually load the data extension and/or link to page about data extensions.
-We'll explain how to use these using a few examples, and provide a `reference <#reference-1>`_ at the end of this article.
+We'll explain how to use these using a few examples, and provide some reference material at the end of this article.
Example: Taint sink in the 'execa' package
------------------------------------------
@@ -216,8 +216,8 @@ For example, the **mysql** model that is included with the CodeQL JS analysis in
- ["mysql.Connection", "mysql", "Member[createConnection].ReturnValue"]
-Reference
-=========
+Reference material
+------------------
The following sections provide reference material for extension points, access paths, types, and kinds.
From 5c14eabb0c2cd48353cbe8176b4b8ddc6549bf81 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Thu, 15 Dec 2022 12:07:58 +0100
Subject: [PATCH 187/631] JS: Remove broken link to 'sink kinds' section
---
.../customizing-library-models-for-javascript.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst b/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
index 0dd19dea8de..5f6ac930933 100644
--- a/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
+++ b/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
@@ -251,7 +251,7 @@ Adds a new taint sink. Sinks are query-specific and will usually affect one or t
- **type**: Name of a type from which to evaluate **path**.
- **path**: Access path leading to the sink.
-- **kind**: Kind of sink to add. See `sink kinds <#sink-kinds>`_ for a list of supported kinds.
+- **kind**: Kind of sink to add. See the section on sink kinds for a list of supported kinds.
Example:
From cfdac73948dd29d67cd92bfc0cd66102ad8c158a Mon Sep 17 00:00:00 2001
From: Asger F
Date: Thu, 15 Dec 2022 12:07:45 +0100
Subject: [PATCH 188/631] JS: Add decodeURIComponent example
---
...tomizing-library-models-for-javascript.rst | 39 +++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst b/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
index 5f6ac930933..071047e4251 100644
--- a/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
+++ b/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
@@ -216,6 +216,45 @@ For example, the **mysql** model that is included with the CodeQL JS analysis in
- ["mysql.Connection", "mysql", "Member[createConnection].ReturnValue"]
+Example: Adding flow through 'decodeURIComponent'
+-------------------------------------------------
+
+In this example, we'll show how to add flow through calls to `decodeURIComponent`:
+
+.. code-block:: js
+
+ let y = decodeURIComponent(x); // add taint flow from 'x' to 'y'
+
+This flow is already recognized by the CodeQL JS analysis, but this is how it could be added with an extension:
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/javascript-all
+ extensible: summaryModel
+ data:
+ - [
+ "global",
+ "Member[decodeURIComponent]",
+ "Argument[0]",
+ "ReturnValue",
+ "taint",
+ ]
+
+To break this down:
+
+- Since we're adding flow *through* a function call, we add a tuple to the **summaryModel** extension point.
+- The first column, **"global"**, begins the search for relevant calls at references to the global object.
+ In JavaScript, global variables are properties of the global object, so this lets us access global variables or functions.
+- The second column, **Member[decodeURIComponent]**, is a path leading to the function calls we wish to model.
+ In this case, we select references to the **decodeURIComponent** member from the global object, that is,
+ the global variable named **decodeURIComponent**.
+- The third column, **Argument[0]**, indicates the input of the flow. In this case, the first argument to the function call.
+- The fourth column, **ReturnValue**, indicates the output of the flow. In this case, the return value of the function call.
+- The last column, **taint**, indicates the kind of flow to add. The value **taint** means the output is not necessarily equal
+ to the input, but was was derived from the input in a taint-preserving way.
+
Reference material
------------------
From 3f4ecd5fdd04ba61400d4cd7de4e9bc48fb1d585 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Thu, 15 Dec 2022 12:20:40 +0100
Subject: [PATCH 189/631] JS: Add underscore.forEach example
---
...tomizing-library-models-for-javascript.rst | 44 +++++++++++++++++++
1 file changed, 44 insertions(+)
diff --git a/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst b/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
index 071047e4251..fe80ad15e34 100644
--- a/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
+++ b/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
@@ -255,6 +255,50 @@ To break this down:
- The last column, **taint**, indicates the kind of flow to add. The value **taint** means the output is not necessarily equal
to the input, but was was derived from the input in a taint-preserving way.
+Example: Adding flow through 'underscore.forEach'
+-------------------------------------------------
+
+In this example, we'll show how to add flow through calls to **forEach** from the **underscore** package:
+
+.. code-block:: js
+
+ require('underscore').forEach([x, y], (v) => { ... }); // add value flow from 'x' and 'y' to 'v'
+
+This flow is already recognized by the CodeQL JS analysis, but we'll show how it could be added with an extension.
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/javascript-all
+ extensible: summaryModel
+ data:
+ - [
+ "underscore",
+ "Member[forEach]",
+ "Argument[0].ArrayElement",
+ "Argument[1].Parameter[0]",
+ "value",
+ ]
+
+To break this down:
+
+- Since we're adding flow *through* a function call, we add a tuple to the **summaryModel** extension point.
+- The first column, **"underscore"**, begins the search for relevant calls at places where the **underscore** package is imported.
+- The second column, **Member[forEach]**, selects references to the **forEach** member from the **underscore** package.
+- The third column specifies the input of the flow:
+
+ - **Argument[0]** selects the first argument of **forEach**, which is the array being iterated over.
+ - **ArrayElement** selects the elements of that array (the expressions **x** and **y**).
+
+- The fourth column specifies the output of the flow:
+
+ - **Argument[1]** selects the second argument of **forEach** (the argument containing the callback function).
+ - **Parameter[0]** selects the first parameter of the callback function (the parameter named **v**).
+
+- The last column, **value**, indicates the kind of flow to add. The value **value** means the input value is unchanged as
+ it flows to the output.
+
Reference material
------------------
From fb6a6e550c40285b3d4cb0bf9d3a4931c24e9c67 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Thu, 15 Dec 2022 12:42:59 +0100
Subject: [PATCH 190/631] JS: Factor beta notice into a reusable snippet
---
.../customizing-library-models-for-javascript.rst | 8 +-------
.../reusables/beta-note-customizing-library-models.rst | 7 +++++++
2 files changed, 8 insertions(+), 7 deletions(-)
create mode 100644 docs/codeql/reusables/beta-note-customizing-library-models.rst
diff --git a/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst b/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
index fe80ad15e34..beb050fdc49 100644
--- a/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
+++ b/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
@@ -3,13 +3,7 @@
Customizing Library Models for JavaScript
=========================================
-.. pull-quote::
-
- Beta Notice - Unstable API
-
- Library customization using data extensions is currently in beta and subject to change.
-
- Breaking changes to this format may occur while in beta.
+.. include:: ../reusables/beta-note-customizing-library-models.rst
The JavaScript analysis can be customized by adding library models in data extension files.
diff --git a/docs/codeql/reusables/beta-note-customizing-library-models.rst b/docs/codeql/reusables/beta-note-customizing-library-models.rst
new file mode 100644
index 00000000000..051078d61d0
--- /dev/null
+++ b/docs/codeql/reusables/beta-note-customizing-library-models.rst
@@ -0,0 +1,7 @@
+.. pull-quote::
+
+ Beta Notice - Unstable API
+
+ Library customization using data extensions is currently in beta and subject to change.
+
+ Breaking changes to this format may occur while in beta.
From 2052ba6fa695428e5c6e09b72012d8ac8018520f Mon Sep 17 00:00:00 2001
From: Asger F
Date: Thu, 15 Dec 2022 12:45:53 +0100
Subject: [PATCH 191/631] JS: sake of example -> this example
---
.../customizing-library-models-for-javascript.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst b/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
index beb050fdc49..1ce93c522c1 100644
--- a/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
+++ b/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
@@ -41,7 +41,7 @@ In this example, we'll show how to add the argument passed to **execa** below as
import { shell } from "execa";
shell(cmd); // <-- add 'cmd' as a taint sink
-This sink is already recognized by the CodeQL JS analysis, but for the sake of example we'll show how it could be added as an extension.
+This sink is already recognized by the CodeQL JS analysis, but for the sake of this example we'll show how it could be added as an extension.
This can be achieved with the following data extension:
From c2a10a3206fec354e37a8f536cfa8ef0067f12a4 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Thu, 15 Dec 2022 12:46:46 +0100
Subject: [PATCH 192/631] JS: Elaborate on comment about window
---
.../customizing-library-models-for-javascript.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst b/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
index 1ce93c522c1..f1a73a95e36 100644
--- a/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
+++ b/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
@@ -95,7 +95,7 @@ This source is already known by the CodeQL JS analysis, but we'll show how it co
To break this down:
- Since we're adding a new taint source, we add a tuple to the **sourceModel** extension point.
-- The first column, **"global"**, begins the search at references to the global object (also known as **window**). This is a special JavaScript object that contains all global variables and methods.
+- The first column, **"global"**, begins the search at references to the global object (also known as **window** in browser contexts). This is a special JavaScript object that contains all global variables and methods.
- **Member[addEventListener]** selects accesses to the **addEventListener** member.
- **Argument[1]** selects the second argument of calls to that member (the argument containing the callback).
- **Parameter[0]** selects the first parameter of the callback (the parameter named **event**).
From 016136a2e32cdf04c5ed82048e3d33a09c6aad16 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Thu, 16 Feb 2023 01:10:36 +0100
Subject: [PATCH 193/631] Update TimingAttack.qll
---
.../semmle/python/security/TimingAttack.qll | 31 +++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
index ee6be383415..2ae48662d34 100644
--- a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
+++ b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
@@ -289,3 +289,34 @@ class UserInputInComparisonConfig extends TaintTracking2::Configuration {
)
}
}
+
+private class CompareSink extends DataFlow::Node {
+ CompareSink() {
+ exists(Compare compare |
+ (
+ compare.getOp(0) instanceof Eq or
+ compare.getOp(0) instanceof NotEq or
+ compare.getOp(0) instanceof In
+ ) and
+ (
+ compare.getLeft() = this.asExpr() and
+ not compare.getComparator(0).(StrConst).getText() = "bearer"
+ or
+ compare.getComparator(0) = this.asExpr() and
+ not compare.getLeft().(StrConst).getText() = "bearer"
+ )
+ ) or
+ exists(Compare compare |
+ (
+ compare.getOp(0) instanceof IsNot
+ ) and
+ (
+ compare.getLeft() = this.asExpr() and
+ not compare.getComparator(0) instanceof None
+ or
+ compare.getComparator(0) = this.asExpr() and
+ not compare.getLeft() instanceof None
+ )
+ )
+ }
+}
From b8f9b2b42426868d79decbacc77f6c8b8cbefc31 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Thu, 16 Feb 2023 01:11:41 +0100
Subject: [PATCH 194/631] Update TimingAttackAgainstHeaderValue.ql
---
.../TimingAttackAgainstHeaderValue.ql | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHeaderValue/TimingAttackAgainstHeaderValue.ql b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHeaderValue/TimingAttackAgainstHeaderValue.ql
index 99d870afc83..d8e3aeb5e9f 100644
--- a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHeaderValue/TimingAttackAgainstHeaderValue.ql
+++ b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHeaderValue/TimingAttackAgainstHeaderValue.ql
@@ -24,13 +24,7 @@ class ClientSuppliedSecretConfig extends TaintTracking::Configuration {
override predicate isSource(DataFlow::Node source) { source instanceof ClientSuppliedSecret }
- override predicate isSink(DataFlow::Node sink) {
- exists(Compare cmp, Expr left, Expr right, Cmpop cmpop |
- cmpop.getSymbol() = ["==", "in", "is not", "!="] and
- cmp.compares(left, cmpop, right) and
- sink.asExpr() = [left, right]
- )
- }
+ override predicate isSink(DataFlow::Node sink) { sink instanceof CompareSink }
}
from ClientSuppliedSecretConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
From fbfe23b7c4b7ed0f16518565096468566f1d06d5 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Thu, 16 Feb 2023 01:21:50 +0100
Subject: [PATCH 195/631] Update TimingAttack.qll
---
.../ql/src/experimental/semmle/python/security/TimingAttack.qll | 2 ++
1 file changed, 2 insertions(+)
diff --git a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
index 2ae48662d34..a0124ba711b 100644
--- a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
+++ b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
@@ -308,6 +308,8 @@ private class CompareSink extends DataFlow::Node {
) or
exists(Compare compare |
(
+ compare.getOp(0) instanceof Eq or
+ compare.getOp(0) instanceof NotEq or
compare.getOp(0) instanceof IsNot
) and
(
From 01b865f75b91e496b6a1e62a0113da69ec3ed7f1 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Thu, 16 Feb 2023 01:36:06 +0100
Subject: [PATCH 196/631] Update TimingAttack.qll
---
.../src/experimental/semmle/python/security/TimingAttack.qll | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
index a0124ba711b..855973e1b58 100644
--- a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
+++ b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
@@ -307,9 +307,7 @@ private class CompareSink extends DataFlow::Node {
)
) or
exists(Compare compare |
- (
- compare.getOp(0) instanceof Eq or
- compare.getOp(0) instanceof NotEq or
+ (
compare.getOp(0) instanceof IsNot
) and
(
From 005839b46231b5101741c33354afe4b22d626637 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Thu, 16 Feb 2023 12:49:40 +0100
Subject: [PATCH 197/631] Update TimingAttack.qll
---
.../experimental/semmle/python/security/TimingAttack.qll | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
index 855973e1b58..b8957a89506 100644
--- a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
+++ b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
@@ -146,10 +146,12 @@ private predicate existsFailFastCheck(Expr firstInput, Expr secondInput) {
) and
(
compare.getLeft() = firstInput and
- compare.getComparator(0) = secondInput
+ compare.getComparator(0) = secondInput and
+ not compare.getAComparator() instanceof None
or
compare.getLeft() = secondInput and
- compare.getComparator(0) = firstInput
+ compare.getComparator(0) = firstInput and
+ not compare.getAComparator() instanceof None
)
)
}
From 4b3efa87dcf24dbc07569f50bce69809c679d0bd Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Thu, 16 Feb 2023 14:01:29 +0100
Subject: [PATCH 198/631] Update TimingAttack.qll
---
.../semmle/python/security/TimingAttack.qll | 28 +++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
index b8957a89506..30c7821c598 100644
--- a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
+++ b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
@@ -292,6 +292,25 @@ class UserInputInComparisonConfig extends TaintTracking2::Configuration {
}
}
+/**
+ * A configuration tracing flow from a client Secret obtained by an HTTP header to a len() function.
+ */
+private class ExcludeLenFunc extends TaintTracking2::Configuration {
+ ExcludeLenFunc() { this = "ExcludeLenFunc" }
+
+ override predicate isSource(DataFlow::Node source) { source instanceof ClientSuppliedSecret }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(Call call |
+ call.getFunc().(Name).getId() = "len" and
+ sink.asExpr() = call.getArg(0)
+ )
+ }
+}
+
+/**
+ * Holds if there is a fast-fail check.
+ */
private class CompareSink extends DataFlow::Node {
CompareSink() {
exists(Compare compare |
@@ -321,4 +340,13 @@ private class CompareSink extends DataFlow::Node {
)
)
}
+
+/**
+ * Holds if there is a flow to len().
+ */
+ predicate FlowToLen() {
+ exists(ExcludeLenFunc config, DataFlow2::PathNode source, DataFlow2::PathNode sink |
+ config.hasFlowPath(source, sink)
+ )
+ }
}
From f70f5c7935268aecb24a0e59a8272e9e1e023fdf Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Thu, 16 Feb 2023 14:03:26 +0100
Subject: [PATCH 199/631] Update TimingAttackAgainstHeaderValue.ql
---
.../TimingAttackAgainstHeaderValue.ql | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHeaderValue/TimingAttackAgainstHeaderValue.ql b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHeaderValue/TimingAttackAgainstHeaderValue.ql
index d8e3aeb5e9f..6a41486f0e1 100644
--- a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHeaderValue/TimingAttackAgainstHeaderValue.ql
+++ b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHeaderValue/TimingAttackAgainstHeaderValue.ql
@@ -28,6 +28,6 @@ class ClientSuppliedSecretConfig extends TaintTracking::Configuration {
}
from ClientSuppliedSecretConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
-where config.hasFlowPath(source, sink)
+where config.hasFlowPath(source, sink) and not sink.getNode().(CompareSink).FlowToLen()
select sink.getNode(), source, sink, "Timing attack against $@ validation.", source.getNode(),
"client-supplied token"
From f57861b6a39b73fc8d9cfca578f5a699402f5fd3 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Thu, 16 Feb 2023 14:14:13 +0100
Subject: [PATCH 200/631] Update TimingAttack.qll
---
.../ql/src/experimental/semmle/python/security/TimingAttack.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
index 30c7821c598..ebd6d4a49b1 100644
--- a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
+++ b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
@@ -344,7 +344,7 @@ private class CompareSink extends DataFlow::Node {
/**
* Holds if there is a flow to len().
*/
- predicate FlowToLen() {
+ predicate flowtolen() {
exists(ExcludeLenFunc config, DataFlow2::PathNode source, DataFlow2::PathNode sink |
config.hasFlowPath(source, sink)
)
From a421e3a3a366f7f7d01eec6f3053a3f949ed83d3 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Thu, 16 Feb 2023 14:14:43 +0100
Subject: [PATCH 201/631] Update TimingAttackAgainstHeaderValue.ql
---
.../TimingAttackAgainstHeaderValue.ql | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHeaderValue/TimingAttackAgainstHeaderValue.ql b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHeaderValue/TimingAttackAgainstHeaderValue.ql
index 6a41486f0e1..e88dbdcba36 100644
--- a/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHeaderValue/TimingAttackAgainstHeaderValue.ql
+++ b/python/ql/src/experimental/Security/CWE-208/TimingAttackAgainstHeaderValue/TimingAttackAgainstHeaderValue.ql
@@ -28,6 +28,6 @@ class ClientSuppliedSecretConfig extends TaintTracking::Configuration {
}
from ClientSuppliedSecretConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
-where config.hasFlowPath(source, sink) and not sink.getNode().(CompareSink).FlowToLen()
+where config.hasFlowPath(source, sink) and not sink.getNode().(CompareSink).flowtolen()
select sink.getNode(), source, sink, "Timing attack against $@ validation.", source.getNode(),
"client-supplied token"
From ccbb58966f0b484b8acd71b23f9b4828a3132824 Mon Sep 17 00:00:00 2001
From: Ahmed Farid
Date: Thu, 16 Feb 2023 14:15:04 +0100
Subject: [PATCH 202/631] Update TimingAttack.qll
---
.../ql/src/experimental/semmle/python/security/TimingAttack.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
index ebd6d4a49b1..46a0072b19b 100644
--- a/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
+++ b/python/ql/src/experimental/semmle/python/security/TimingAttack.qll
@@ -311,7 +311,7 @@ private class ExcludeLenFunc extends TaintTracking2::Configuration {
/**
* Holds if there is a fast-fail check.
*/
-private class CompareSink extends DataFlow::Node {
+class CompareSink extends DataFlow::Node {
CompareSink() {
exists(Compare compare |
(
From 27e2307d0c05ad134df166f264d1b993d823930e Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Fri, 17 Feb 2023 15:48:18 +0100
Subject: [PATCH 203/631] Python: Add import regression for refined variable
---
.../experimental/import-resolution/importflow.ql | 3 +++
.../ql/test/experimental/import-resolution/main.py | 4 ++++
.../test/experimental/import-resolution/refined.py | 12 ++++++++++++
3 files changed, 19 insertions(+)
create mode 100644 python/ql/test/experimental/import-resolution/refined.py
diff --git a/python/ql/test/experimental/import-resolution/importflow.ql b/python/ql/test/experimental/import-resolution/importflow.ql
index e6e51afa963..94f5e4b0ec8 100644
--- a/python/ql/test/experimental/import-resolution/importflow.ql
+++ b/python/ql/test/experimental/import-resolution/importflow.ql
@@ -11,6 +11,9 @@ private class SourceString extends DataFlow::Node {
SourceString() {
this.asExpr().(StrConst).getText() = contents and
this.asExpr().getParent() instanceof Assign
+ or
+ this.asExpr().(ClassExpr).getInnerScope().getName() = "SOURCE" and
+ contents = "SOURCE"
}
string getContents() { result = contents }
diff --git a/python/ql/test/experimental/import-resolution/main.py b/python/ql/test/experimental/import-resolution/main.py
index 840da95a03c..6ff5506cab4 100644
--- a/python/ql/test/experimental/import-resolution/main.py
+++ b/python/ql/test/experimental/import-resolution/main.py
@@ -93,6 +93,10 @@ check("subpackage2_attr", subpackage2_attr, "subpackage2_attr", globals()) #$ pr
from if_then_else import if_then_else_defined
check("if_then_else_defined", if_then_else_defined, "if_defined", globals()) #$ prints=if_defined prints=else_defined_1 prints=else_defined_2
+# check that refined definitions are handled correctly
+import refined # $ imports=refined as=refined
+check("refined.SOURCE", refined.SOURCE, refined.SOURCE, globals()) #$ MISSING: prints=SOURCE
+
exit(__file__)
print()
diff --git a/python/ql/test/experimental/import-resolution/refined.py b/python/ql/test/experimental/import-resolution/refined.py
new file mode 100644
index 00000000000..a687ce15655
--- /dev/null
+++ b/python/ql/test/experimental/import-resolution/refined.py
@@ -0,0 +1,12 @@
+from trace import *
+enter(__file__)
+
+class SOURCE(object): pass
+
+check("SOURCE", SOURCE, SOURCE, globals()) #$ prints=SOURCE
+
+SOURCE.foo = 42
+
+check("SOURCE", SOURCE, SOURCE, globals()) #$ prints=SOURCE
+
+exit(__file__)
From fb425b73fcac5d88c97a35b0a87479f1613308ff Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Tue, 21 Feb 2023 15:47:36 +0100
Subject: [PATCH 204/631] Python: Add import test of `py/insecure-protocol`
---
.../InsecureProtocol.expected | 5 ++++
.../import_all_one_file.py | 30 +++++++++++++++++++
.../CWE-327-InsecureProtocol/import_def.py | 11 +++++++
.../CWE-327-InsecureProtocol/import_use.py | 18 +++++++++++
4 files changed, 64 insertions(+)
create mode 100644 python/ql/test/query-tests/Security/CWE-327-InsecureProtocol/import_all_one_file.py
create mode 100644 python/ql/test/query-tests/Security/CWE-327-InsecureProtocol/import_def.py
create mode 100644 python/ql/test/query-tests/Security/CWE-327-InsecureProtocol/import_use.py
diff --git a/python/ql/test/query-tests/Security/CWE-327-InsecureProtocol/InsecureProtocol.expected b/python/ql/test/query-tests/Security/CWE-327-InsecureProtocol/InsecureProtocol.expected
index c08e19e5f1c..f097438b213 100644
--- a/python/ql/test/query-tests/Security/CWE-327-InsecureProtocol/InsecureProtocol.expected
+++ b/python/ql/test/query-tests/Security/CWE-327-InsecureProtocol/InsecureProtocol.expected
@@ -10,6 +10,11 @@
| InsecureProtocol.py:19:1:19:19 | ControlFlowNode for Attribute() | Insecure SSL/TLS protocol version SSLv2 specified by $@. | InsecureProtocol.py:19:1:19:19 | ControlFlowNode for Attribute() | call to SSL.Context |
| InsecureProtocol.py:23:1:23:43 | ControlFlowNode for Attribute() | Insecure SSL/TLS protocol version SSLv2 specified by $@. | InsecureProtocol.py:23:1:23:43 | ControlFlowNode for Attribute() | call to ssl.wrap_socket |
| InsecureProtocol.py:24:1:24:35 | ControlFlowNode for SSLContext() | Insecure SSL/TLS protocol version SSLv2 specified by $@. | InsecureProtocol.py:24:1:24:35 | ControlFlowNode for SSLContext() | call to SSLContext |
+| import_all_one_file.py:25:14:25:45 | ControlFlowNode for copy_completely_insecure_context | Insecure SSL/TLS protocol version TLSv1 allowed by $@. | import_all_one_file.py:9:36:9:67 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
+| import_all_one_file.py:25:14:25:45 | ControlFlowNode for copy_completely_insecure_context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | import_all_one_file.py:9:36:9:67 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
+| import_all_one_file.py:29:14:29:39 | ControlFlowNode for copy_also_insecure_context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | import_all_one_file.py:12:30:12:61 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
+| import_use.py:13:14:13:40 | ControlFlowNode for completely_insecure_context | Insecure SSL/TLS protocol version TLSv1 allowed by $@. | import_def.py:7:31:7:62 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
+| import_use.py:13:14:13:40 | ControlFlowNode for completely_insecure_context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | import_def.py:7:31:7:62 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
| pyOpenSSL_fluent.py:8:27:8:33 | ControlFlowNode for context | Insecure SSL/TLS protocol version SSLv2 allowed by $@. | pyOpenSSL_fluent.py:6:15:6:44 | ControlFlowNode for Attribute() | call to SSL.Context |
| pyOpenSSL_fluent.py:8:27:8:33 | ControlFlowNode for context | Insecure SSL/TLS protocol version SSLv3 allowed by $@. | pyOpenSSL_fluent.py:6:15:6:44 | ControlFlowNode for Attribute() | call to SSL.Context |
| pyOpenSSL_fluent.py:8:27:8:33 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1 allowed by $@. | pyOpenSSL_fluent.py:6:15:6:44 | ControlFlowNode for Attribute() | call to SSL.Context |
diff --git a/python/ql/test/query-tests/Security/CWE-327-InsecureProtocol/import_all_one_file.py b/python/ql/test/query-tests/Security/CWE-327-InsecureProtocol/import_all_one_file.py
new file mode 100644
index 00000000000..aab459ceeea
--- /dev/null
+++ b/python/ql/test/query-tests/Security/CWE-327-InsecureProtocol/import_all_one_file.py
@@ -0,0 +1,30 @@
+# use to compare alerts without import
+
+import ssl
+
+copy_secure_context = ssl.SSLContext(ssl.PROTOCOL_TLS)
+copy_secure_context.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
+
+# this is just to allow us to see how un-altered exports work
+copy_completely_insecure_context = ssl.SSLContext(ssl.PROTOCOL_TLS)
+
+# and an insecure export that is refined
+copy_also_insecure_context = ssl.SSLContext(ssl.PROTOCOL_TLS)
+copy_also_insecure_context.options |= ssl.OP_NO_TLSv1
+
+
+
+import socket
+hostname = 'www.python.org'
+
+with socket.create_connection((hostname, 443)) as sock:
+ with copy_secure_context.wrap_socket(sock, server_hostname=hostname) as ssock:
+ print(ssock.version())
+
+with socket.create_connection((hostname, 443)) as sock:
+ with copy_completely_insecure_context.wrap_socket(sock, server_hostname=hostname) as ssock:
+ print(ssock.version())
+
+with socket.create_connection((hostname, 443)) as sock:
+ with copy_also_insecure_context.wrap_socket(sock, server_hostname=hostname) as ssock:
+ print(ssock.version())
diff --git a/python/ql/test/query-tests/Security/CWE-327-InsecureProtocol/import_def.py b/python/ql/test/query-tests/Security/CWE-327-InsecureProtocol/import_def.py
new file mode 100644
index 00000000000..d8e37542b94
--- /dev/null
+++ b/python/ql/test/query-tests/Security/CWE-327-InsecureProtocol/import_def.py
@@ -0,0 +1,11 @@
+import ssl
+
+secure_context = ssl.SSLContext(ssl.PROTOCOL_TLS)
+secure_context.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
+
+# this is just to allow us to see how un-altered exports work
+completely_insecure_context = ssl.SSLContext(ssl.PROTOCOL_TLS)
+
+# and an insecure export that is refined
+also_insecure_context = ssl.SSLContext(ssl.PROTOCOL_TLS)
+also_insecure_context.options |= ssl.OP_NO_TLSv1
diff --git a/python/ql/test/query-tests/Security/CWE-327-InsecureProtocol/import_use.py b/python/ql/test/query-tests/Security/CWE-327-InsecureProtocol/import_use.py
new file mode 100644
index 00000000000..3c12fd81355
--- /dev/null
+++ b/python/ql/test/query-tests/Security/CWE-327-InsecureProtocol/import_use.py
@@ -0,0 +1,18 @@
+# check that query works properly with imports
+
+import socket
+from import_def import secure_context, completely_insecure_context, also_insecure_context
+
+hostname = 'www.python.org'
+
+with socket.create_connection((hostname, 443)) as sock:
+ with secure_context.wrap_socket(sock, server_hostname=hostname) as ssock:
+ print(ssock.version())
+
+with socket.create_connection((hostname, 443)) as sock:
+ with completely_insecure_context.wrap_socket(sock, server_hostname=hostname) as ssock:
+ print(ssock.version())
+
+with socket.create_connection((hostname, 443)) as sock:
+ with also_insecure_context.wrap_socket(sock, server_hostname=hostname) as ssock:
+ print(ssock.version())
From 00eec6986cacd1ae6e4789278c41f35fe5ed4f06 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Fri, 17 Feb 2023 15:52:53 +0100
Subject: [PATCH 205/631] Python: Allow import of refined variable
However, as illustrated by the `CWE-327-InsecureProtocol` test, this fix
is NOT good enough, since now even the `secure_context` is considered to
be insecure (for both versions). Ouch.
Will fix this in a later commit, since it was only discoverd late on.
---
.../ql/lib/change-notes/2023-02-17-import-refined-variable.md | 4 ++++
.../semmle/python/dataflow/new/internal/ImportResolution.qll | 3 +++
python/ql/test/experimental/import-resolution/main.py | 2 +-
.../CWE-327-InsecureProtocol/InsecureProtocol.expected | 4 ++++
4 files changed, 12 insertions(+), 1 deletion(-)
create mode 100644 python/ql/lib/change-notes/2023-02-17-import-refined-variable.md
diff --git a/python/ql/lib/change-notes/2023-02-17-import-refined-variable.md b/python/ql/lib/change-notes/2023-02-17-import-refined-variable.md
new file mode 100644
index 00000000000..e2dfcd8fd1a
--- /dev/null
+++ b/python/ql/lib/change-notes/2023-02-17-import-refined-variable.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* Fixed module resolution so we allow imports of definitions that have had an attribute assigned to it, such as `class Foo; Foo.bar = 42`.
diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll b/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll
index 7af9ca524aa..4895feff946 100644
--- a/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll
+++ b/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll
@@ -79,6 +79,9 @@ module ImportResolution {
or
// to handle definitions guarded by if-then-else
essaDef = v.getDefinition().(PhiFunction).getAnInput()
+ or
+ // refined variable
+ essaDef = v.getDefinition().(EssaNodeRefinement).getInput().getDefinition()
)
|
defn.getNode() = essaDef.(AssignmentDefinition).getValue()
diff --git a/python/ql/test/experimental/import-resolution/main.py b/python/ql/test/experimental/import-resolution/main.py
index 6ff5506cab4..2a94f180388 100644
--- a/python/ql/test/experimental/import-resolution/main.py
+++ b/python/ql/test/experimental/import-resolution/main.py
@@ -95,7 +95,7 @@ check("if_then_else_defined", if_then_else_defined, "if_defined", globals()) #$
# check that refined definitions are handled correctly
import refined # $ imports=refined as=refined
-check("refined.SOURCE", refined.SOURCE, refined.SOURCE, globals()) #$ MISSING: prints=SOURCE
+check("refined.SOURCE", refined.SOURCE, refined.SOURCE, globals()) #$ prints=SOURCE
exit(__file__)
diff --git a/python/ql/test/query-tests/Security/CWE-327-InsecureProtocol/InsecureProtocol.expected b/python/ql/test/query-tests/Security/CWE-327-InsecureProtocol/InsecureProtocol.expected
index f097438b213..cc0d620aa1c 100644
--- a/python/ql/test/query-tests/Security/CWE-327-InsecureProtocol/InsecureProtocol.expected
+++ b/python/ql/test/query-tests/Security/CWE-327-InsecureProtocol/InsecureProtocol.expected
@@ -13,8 +13,12 @@
| import_all_one_file.py:25:14:25:45 | ControlFlowNode for copy_completely_insecure_context | Insecure SSL/TLS protocol version TLSv1 allowed by $@. | import_all_one_file.py:9:36:9:67 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
| import_all_one_file.py:25:14:25:45 | ControlFlowNode for copy_completely_insecure_context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | import_all_one_file.py:9:36:9:67 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
| import_all_one_file.py:29:14:29:39 | ControlFlowNode for copy_also_insecure_context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | import_all_one_file.py:12:30:12:61 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
+| import_use.py:9:14:9:27 | ControlFlowNode for secure_context | Insecure SSL/TLS protocol version TLSv1 allowed by $@. | import_def.py:3:18:3:49 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
+| import_use.py:9:14:9:27 | ControlFlowNode for secure_context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | import_def.py:3:18:3:49 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
| import_use.py:13:14:13:40 | ControlFlowNode for completely_insecure_context | Insecure SSL/TLS protocol version TLSv1 allowed by $@. | import_def.py:7:31:7:62 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
| import_use.py:13:14:13:40 | ControlFlowNode for completely_insecure_context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | import_def.py:7:31:7:62 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
+| import_use.py:17:14:17:34 | ControlFlowNode for also_insecure_context | Insecure SSL/TLS protocol version TLSv1 allowed by $@. | import_def.py:10:25:10:56 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
+| import_use.py:17:14:17:34 | ControlFlowNode for also_insecure_context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | import_def.py:10:25:10:56 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
| pyOpenSSL_fluent.py:8:27:8:33 | ControlFlowNode for context | Insecure SSL/TLS protocol version SSLv2 allowed by $@. | pyOpenSSL_fluent.py:6:15:6:44 | ControlFlowNode for Attribute() | call to SSL.Context |
| pyOpenSSL_fluent.py:8:27:8:33 | ControlFlowNode for context | Insecure SSL/TLS protocol version SSLv3 allowed by $@. | pyOpenSSL_fluent.py:6:15:6:44 | ControlFlowNode for Attribute() | call to SSL.Context |
| pyOpenSSL_fluent.py:8:27:8:33 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1 allowed by $@. | pyOpenSSL_fluent.py:6:15:6:44 | ControlFlowNode for Attribute() | call to SSL.Context |
From e522009666776c507583feb75899bb4714a4209c Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Fri, 17 Feb 2023 15:56:16 +0100
Subject: [PATCH 206/631] Python: More complex import examples
We need some recursive unwinding to get all of these right
---
.../import-resolution/if_then_else_refined.py | 19 +++++++++++++++++++
.../experimental/import-resolution/main.py | 5 ++++-
.../experimental/import-resolution/refined.py | 2 ++
3 files changed, 25 insertions(+), 1 deletion(-)
create mode 100644 python/ql/test/experimental/import-resolution/if_then_else_refined.py
diff --git a/python/ql/test/experimental/import-resolution/if_then_else_refined.py b/python/ql/test/experimental/import-resolution/if_then_else_refined.py
new file mode 100644
index 00000000000..670c7f178e9
--- /dev/null
+++ b/python/ql/test/experimental/import-resolution/if_then_else_refined.py
@@ -0,0 +1,19 @@
+# combination of refined and if_then_else
+
+from trace import *
+enter(__file__)
+
+class SOURCE(): pass
+
+# definition based on "random" choice in this case it will always go the the if-branch,
+# but our analysis is not able to figure this out
+if eval("True"):
+ src = SOURCE
+else:
+ src = SOURCE
+
+src.foo = 42
+
+check("src", src, src, globals()) #$ prints=SOURCE
+
+exit(__file__)
diff --git a/python/ql/test/experimental/import-resolution/main.py b/python/ql/test/experimental/import-resolution/main.py
index 2a94f180388..9b0b71f9b59 100644
--- a/python/ql/test/experimental/import-resolution/main.py
+++ b/python/ql/test/experimental/import-resolution/main.py
@@ -95,7 +95,10 @@ check("if_then_else_defined", if_then_else_defined, "if_defined", globals()) #$
# check that refined definitions are handled correctly
import refined # $ imports=refined as=refined
-check("refined.SOURCE", refined.SOURCE, refined.SOURCE, globals()) #$ prints=SOURCE
+check("refined.SOURCE", refined.SOURCE, refined.SOURCE, globals()) #$ MISSING: prints=SOURCE
+
+import if_then_else_refined # $ imports=if_then_else_refined as=if_then_else_refined
+check("if_then_else_refined.src", if_then_else_refined.src, if_then_else_refined.src, globals()) #$ MISSING: prints=SOURCE
exit(__file__)
diff --git a/python/ql/test/experimental/import-resolution/refined.py b/python/ql/test/experimental/import-resolution/refined.py
index a687ce15655..3694168c5df 100644
--- a/python/ql/test/experimental/import-resolution/refined.py
+++ b/python/ql/test/experimental/import-resolution/refined.py
@@ -6,6 +6,8 @@ class SOURCE(object): pass
check("SOURCE", SOURCE, SOURCE, globals()) #$ prints=SOURCE
SOURCE.foo = 42
+SOURCE.bar = 43
+SOURCE.baz = 44
check("SOURCE", SOURCE, SOURCE, globals()) #$ prints=SOURCE
From 4a66e48dc52fc438c406cbdd565ca4f187b7bcca Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Fri, 17 Feb 2023 16:09:50 +0100
Subject: [PATCH 207/631] Python: Allow import resolution with recursive
phi/refine steps
---
.../new/internal/ImportResolution.qll | 23 +++++++++++--------
.../experimental/import-resolution/main.py | 4 ++--
2 files changed, 16 insertions(+), 11 deletions(-)
diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll b/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll
index 4895feff946..c440ace2922 100644
--- a/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll
+++ b/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll
@@ -64,6 +64,19 @@ private import semmle.python.dataflow.new.internal.DataFlowPrivate
* `bar` subpackage.
*/
module ImportResolution {
+ /**
+ * Holds if there is an ESSA step from `defFrom` to `defTo`, which should be allowed
+ * for import resolution.
+ */
+ private predicate allowedEssaImportStep(EssaDefinition defFrom, EssaDefinition defTo) {
+ // to handle definitions guarded by if-then-else
+ defFrom = defTo.(PhiFunction).getAnInput()
+ or
+ // refined variable
+ // example: https://github.com/nvbn/thefuck/blob/ceeaeab94b5df5a4fe9d94d61e4f6b0bbea96378/thefuck/utils.py#L25-L45
+ defFrom = defTo.(EssaNodeRefinement).getInput().getDefinition()
+ }
+
/**
* Holds if the module `m` defines a name `name` by assigning `defn` to it. This is an
* overapproximation, as `name` may not in fact be exported (e.g. by defining an `__all__` that does
@@ -74,15 +87,7 @@ module ImportResolution {
exists(EssaVariable v, EssaDefinition essaDef |
v.getName() = name and
v.getAUse() = ImportStar::getStarImported*(m).getANormalExit() and
- (
- essaDef = v.getDefinition()
- or
- // to handle definitions guarded by if-then-else
- essaDef = v.getDefinition().(PhiFunction).getAnInput()
- or
- // refined variable
- essaDef = v.getDefinition().(EssaNodeRefinement).getInput().getDefinition()
- )
+ allowedEssaImportStep*(essaDef, v.getDefinition())
|
defn.getNode() = essaDef.(AssignmentDefinition).getValue()
or
diff --git a/python/ql/test/experimental/import-resolution/main.py b/python/ql/test/experimental/import-resolution/main.py
index 9b0b71f9b59..f7cba832232 100644
--- a/python/ql/test/experimental/import-resolution/main.py
+++ b/python/ql/test/experimental/import-resolution/main.py
@@ -95,10 +95,10 @@ check("if_then_else_defined", if_then_else_defined, "if_defined", globals()) #$
# check that refined definitions are handled correctly
import refined # $ imports=refined as=refined
-check("refined.SOURCE", refined.SOURCE, refined.SOURCE, globals()) #$ MISSING: prints=SOURCE
+check("refined.SOURCE", refined.SOURCE, refined.SOURCE, globals()) #$ prints=SOURCE
import if_then_else_refined # $ imports=if_then_else_refined as=if_then_else_refined
-check("if_then_else_refined.src", if_then_else_refined.src, if_then_else_refined.src, globals()) #$ MISSING: prints=SOURCE
+check("if_then_else_refined.src", if_then_else_refined.src, if_then_else_refined.src, globals()) #$ prints=SOURCE
exit(__file__)
From 6a5eebe89162cce170b4656b66c3fbe8d019f178 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 22 Feb 2023 12:26:01 +0100
Subject: [PATCH 208/631] Python: Add test of `module_export`
---
.../import-resolution/ModuleExport.expected | 232 ++++++++++++++++++
.../import-resolution/ModuleExport.ql | 9 +
2 files changed, 241 insertions(+)
create mode 100644 python/ql/test/experimental/import-resolution/ModuleExport.expected
create mode 100644 python/ql/test/experimental/import-resolution/ModuleExport.ql
diff --git a/python/ql/test/experimental/import-resolution/ModuleExport.expected b/python/ql/test/experimental/import-resolution/ModuleExport.expected
new file mode 100644
index 00000000000..6733a9e378a
--- /dev/null
+++ b/python/ql/test/experimental/import-resolution/ModuleExport.expected
@@ -0,0 +1,232 @@
+| attr_clash.__init__ | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
+| attr_clash.__init__ | __file__ | attr_clash/__init__.py:2:7:2:14 | ControlFlowNode for __file__ |
+| attr_clash.__init__ | __file__ | attr_clash/__init__.py:6:6:6:13 | ControlFlowNode for __file__ |
+| attr_clash.__init__ | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
+| attr_clash.__init__ | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
+| attr_clash.__init__ | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
+| attr_clash.__init__ | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
+| attr_clash.__init__ | clashing_attr | attr_clash/__init__.py:4:17:4:31 | ControlFlowNode for Str |
+| attr_clash.__init__ | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
+| attr_clash.__init__ | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
+| attr_clash.__init__ | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
+| attr_clash.__init__ | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
+| attr_clash.__init__ | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
+| attr_clash.clashing_attr | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
+| attr_clash.clashing_attr | __file__ | attr_clash/clashing_attr.py:2:7:2:14 | ControlFlowNode for __file__ |
+| attr_clash.clashing_attr | __file__ | attr_clash/clashing_attr.py:4:6:4:13 | ControlFlowNode for __file__ |
+| attr_clash.clashing_attr | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
+| attr_clash.clashing_attr | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
+| attr_clash.clashing_attr | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
+| attr_clash.clashing_attr | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
+| attr_clash.clashing_attr | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
+| attr_clash.clashing_attr | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
+| attr_clash.clashing_attr | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
+| attr_clash.clashing_attr | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
+| attr_clash.clashing_attr | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
+| attr_clash.non_clashing_submodule | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
+| attr_clash.non_clashing_submodule | __file__ | attr_clash/non_clashing_submodule.py:2:7:2:14 | ControlFlowNode for __file__ |
+| attr_clash.non_clashing_submodule | __file__ | attr_clash/non_clashing_submodule.py:4:6:4:13 | ControlFlowNode for __file__ |
+| attr_clash.non_clashing_submodule | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
+| attr_clash.non_clashing_submodule | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
+| attr_clash.non_clashing_submodule | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
+| attr_clash.non_clashing_submodule | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
+| attr_clash.non_clashing_submodule | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
+| attr_clash.non_clashing_submodule | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
+| attr_clash.non_clashing_submodule | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
+| attr_clash.non_clashing_submodule | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
+| attr_clash.non_clashing_submodule | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
+| bar | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
+| bar | __file__ | bar.py:2:7:2:14 | ControlFlowNode for __file__ |
+| bar | __file__ | bar.py:6:6:6:13 | ControlFlowNode for __file__ |
+| bar | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
+| bar | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
+| bar | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
+| bar | bar_attr | bar.py:4:12:4:21 | ControlFlowNode for Str |
+| bar | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
+| bar | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
+| bar | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
+| bar | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
+| bar | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
+| bar | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
+| foo | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
+| foo | __file__ | foo.py:2:7:2:14 | ControlFlowNode for __file__ |
+| foo | __file__ | foo.py:14:6:14:13 | ControlFlowNode for __file__ |
+| foo | __private_foo_attr | foo.py:8:22:8:41 | ControlFlowNode for Str |
+| foo | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
+| foo | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
+| foo | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
+| foo | bar_reexported | foo.py:11:8:11:10 | ControlFlowNode for ImportExpr |
+| foo | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
+| foo | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
+| foo | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
+| foo | foo_attr | foo.py:5:12:5:21 | ControlFlowNode for Str |
+| foo | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
+| foo | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
+| foo | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
+| if_then_else | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
+| if_then_else | __file__ | if_then_else.py:2:7:2:14 | ControlFlowNode for __file__ |
+| if_then_else | __file__ | if_then_else.py:16:6:16:13 | ControlFlowNode for __file__ |
+| if_then_else | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
+| if_then_else | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
+| if_then_else | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
+| if_then_else | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
+| if_then_else | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
+| if_then_else | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
+| if_then_else | if_then_else_defined | if_then_else.py:7:28:7:39 | ControlFlowNode for Str |
+| if_then_else | if_then_else_defined | if_then_else.py:12:32:12:47 | ControlFlowNode for Str |
+| if_then_else | if_then_else_defined | if_then_else.py:14:32:14:47 | ControlFlowNode for Str |
+| if_then_else | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
+| if_then_else | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
+| if_then_else | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
+| if_then_else_refined | SOURCE | if_then_else_refined.py:6:1:6:15 | ControlFlowNode for ClassExpr |
+| if_then_else_refined | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
+| if_then_else_refined | __file__ | if_then_else_refined.py:4:7:4:14 | ControlFlowNode for __file__ |
+| if_then_else_refined | __file__ | if_then_else_refined.py:19:6:19:13 | ControlFlowNode for __file__ |
+| if_then_else_refined | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
+| if_then_else_refined | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
+| if_then_else_refined | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
+| if_then_else_refined | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
+| if_then_else_refined | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
+| if_then_else_refined | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
+| if_then_else_refined | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
+| if_then_else_refined | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
+| if_then_else_refined | src | if_then_else_refined.py:11:11:11:16 | ControlFlowNode for SOURCE |
+| if_then_else_refined | src | if_then_else_refined.py:13:11:13:16 | ControlFlowNode for SOURCE |
+| if_then_else_refined | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
+| main | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
+| main | __file__ | main.py:24:7:24:14 | ControlFlowNode for __file__ |
+| main | __file__ | main.py:103:6:103:13 | ControlFlowNode for __file__ |
+| main | __file__ | package/subpackage2/__init__.py:2:7:2:14 | ControlFlowNode for __file__ |
+| main | __file__ | package/subpackage2/__init__.py:6:6:6:13 | ControlFlowNode for __file__ |
+| main | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
+| main | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
+| main | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
+| main | aliased_subpackage | main.py:54:6:54:12 | ControlFlowNode for ImportExpr |
+| main | aliased_subpackage | main.py:54:21:54:52 | ControlFlowNode for ImportMember |
+| main | aliased_subpackage | main.py:65:8:65:25 | ControlFlowNode for ImportExpr |
+| main | bar_attr | main.py:42:6:42:8 | ControlFlowNode for ImportExpr |
+| main | bar_attr | main.py:42:17:42:24 | ControlFlowNode for ImportMember |
+| main | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
+| main | clashing_attr | main.py:83:6:83:15 | ControlFlowNode for ImportExpr |
+| main | clashing_attr | main.py:83:24:83:36 | ControlFlowNode for ImportMember |
+| main | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
+| main | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
+| main | foo | main.py:27:8:27:10 | ControlFlowNode for ImportExpr |
+| main | foo_alias | main.py:34:8:34:10 | ControlFlowNode for ImportExpr |
+| main | if_then_else_defined | main.py:93:6:93:17 | ControlFlowNode for ImportExpr |
+| main | if_then_else_defined | main.py:93:26:93:45 | ControlFlowNode for ImportMember |
+| main | if_then_else_refined | main.py:100:8:100:27 | ControlFlowNode for ImportExpr |
+| main | local_import | main.py:57:1:57:19 | ControlFlowNode for FunctionExpr |
+| main | namespace_module_attr | main.py:79:10:79:43 | ControlFlowNode for ImportExpr |
+| main | namespace_module_attr | main.py:79:52:79:72 | ControlFlowNode for ImportMember |
+| main | non_clashing_submodule | main.py:83:6:83:15 | ControlFlowNode for ImportExpr |
+| main | non_clashing_submodule | main.py:83:39:83:60 | ControlFlowNode for ImportMember |
+| main | package | main.py:69:8:69:25 | ControlFlowNode for ImportExpr |
+| main | package | main.py:73:8:73:35 | ControlFlowNode for ImportExpr |
+| main | package_attr_alias | main.py:50:6:50:12 | ControlFlowNode for ImportExpr |
+| main | package_attr_alias | main.py:50:21:50:54 | ControlFlowNode for ImportMember |
+| main | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
+| main | print_function | main.py:21:6:21:15 | ControlFlowNode for ImportExpr |
+| main | print_function | main.py:21:24:21:37 | ControlFlowNode for ImportMember |
+| main | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
+| main | refined | main.py:97:8:97:14 | ControlFlowNode for ImportExpr |
+| main | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
+| main | subpackage2_attr | package/subpackage2/__init__.py:4:20:4:37 | ControlFlowNode for Str |
+| main | subpackage_attr | main.py:46:6:46:23 | ControlFlowNode for ImportExpr |
+| main | subpackage_attr | main.py:46:32:46:46 | ControlFlowNode for ImportMember |
+| main | sys | main.py:22:8:22:10 | ControlFlowNode for ImportExpr |
+| namespace_package.namespace_module | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
+| namespace_package.namespace_module | __file__ | namespace_package/namespace_module.py:2:7:2:14 | ControlFlowNode for __file__ |
+| namespace_package.namespace_module | __file__ | namespace_package/namespace_module.py:6:6:6:13 | ControlFlowNode for __file__ |
+| namespace_package.namespace_module | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
+| namespace_package.namespace_module | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
+| namespace_package.namespace_module | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
+| namespace_package.namespace_module | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
+| namespace_package.namespace_module | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
+| namespace_package.namespace_module | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
+| namespace_package.namespace_module | namespace_module_attr | namespace_package/namespace_module.py:4:25:4:47 | ControlFlowNode for Str |
+| namespace_package.namespace_module | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
+| namespace_package.namespace_module | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
+| namespace_package.namespace_module | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
+| package.__init__ | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
+| package.__init__ | __file__ | package/__init__.py:2:7:2:14 | ControlFlowNode for __file__ |
+| package.__init__ | __file__ | package/__init__.py:7:6:7:13 | ControlFlowNode for __file__ |
+| package.__init__ | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
+| package.__init__ | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
+| package.__init__ | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
+| package.__init__ | attr_used_in_subpackage | package/__init__.py:4:27:4:51 | ControlFlowNode for Str |
+| package.__init__ | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
+| package.__init__ | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
+| package.__init__ | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
+| package.__init__ | package_attr | package/__init__.py:5:16:5:29 | ControlFlowNode for Str |
+| package.__init__ | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
+| package.__init__ | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
+| package.__init__ | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
+| package.subpackage2.__init__ | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
+| package.subpackage2.__init__ | __file__ | package/subpackage2/__init__.py:2:7:2:14 | ControlFlowNode for __file__ |
+| package.subpackage2.__init__ | __file__ | package/subpackage2/__init__.py:6:6:6:13 | ControlFlowNode for __file__ |
+| package.subpackage2.__init__ | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
+| package.subpackage2.__init__ | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
+| package.subpackage2.__init__ | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
+| package.subpackage2.__init__ | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
+| package.subpackage2.__init__ | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
+| package.subpackage2.__init__ | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
+| package.subpackage2.__init__ | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
+| package.subpackage2.__init__ | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
+| package.subpackage2.__init__ | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
+| package.subpackage2.__init__ | subpackage2_attr | package/subpackage2/__init__.py:4:20:4:37 | ControlFlowNode for Str |
+| package.subpackage.__init__ | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
+| package.subpackage.__init__ | __file__ | package/subpackage/__init__.py:2:7:2:14 | ControlFlowNode for __file__ |
+| package.subpackage.__init__ | __file__ | package/subpackage/__init__.py:14:6:14:13 | ControlFlowNode for __file__ |
+| package.subpackage.__init__ | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
+| package.subpackage.__init__ | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
+| package.subpackage.__init__ | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
+| package.subpackage.__init__ | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
+| package.subpackage.__init__ | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
+| package.subpackage.__init__ | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
+| package.subpackage.__init__ | imported_attr | package/subpackage/__init__.py:7:6:7:7 | ControlFlowNode for ImportExpr |
+| package.subpackage.__init__ | imported_attr | package/subpackage/__init__.py:7:16:7:55 | ControlFlowNode for ImportMember |
+| package.subpackage.__init__ | irrelevant_attr | package/subpackage/__init__.py:11:6:11:15 | ControlFlowNode for ImportExpr |
+| package.subpackage.__init__ | irrelevant_attr | package/subpackage/__init__.py:11:24:11:38 | ControlFlowNode for ImportMember |
+| package.subpackage.__init__ | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
+| package.subpackage.__init__ | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
+| package.subpackage.__init__ | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
+| package.subpackage.__init__ | subpackage_attr | package/subpackage/__init__.py:4:19:4:35 | ControlFlowNode for Str |
+| package.subpackage.submodule | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
+| package.subpackage.submodule | __file__ | package/subpackage/submodule.py:2:7:2:14 | ControlFlowNode for __file__ |
+| package.subpackage.submodule | __file__ | package/subpackage/submodule.py:7:6:7:13 | ControlFlowNode for __file__ |
+| package.subpackage.submodule | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
+| package.subpackage.submodule | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
+| package.subpackage.submodule | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
+| package.subpackage.submodule | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
+| package.subpackage.submodule | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
+| package.subpackage.submodule | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
+| package.subpackage.submodule | irrelevant_attr | package/subpackage/submodule.py:5:19:5:35 | ControlFlowNode for Str |
+| package.subpackage.submodule | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
+| package.subpackage.submodule | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
+| package.subpackage.submodule | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
+| package.subpackage.submodule | submodule_attr | package/subpackage/submodule.py:4:18:4:33 | ControlFlowNode for Str |
+| refined | SOURCE | refined.py:4:1:4:21 | ControlFlowNode for ClassExpr |
+| refined | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
+| refined | __file__ | refined.py:2:7:2:14 | ControlFlowNode for __file__ |
+| refined | __file__ | refined.py:14:6:14:13 | ControlFlowNode for __file__ |
+| refined | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
+| refined | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
+| refined | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
+| refined | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
+| refined | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
+| refined | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
+| refined | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
+| refined | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
+| refined | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
+| trace | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
+| trace | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
+| trace | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
+| trace | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
+| trace | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
+| trace | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
+| trace | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
+| trace | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
+| trace | print_function | trace.py:1:6:1:15 | ControlFlowNode for ImportExpr |
+| trace | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
+| trace | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
diff --git a/python/ql/test/experimental/import-resolution/ModuleExport.ql b/python/ql/test/experimental/import-resolution/ModuleExport.ql
new file mode 100644
index 00000000000..9d45b0ec8d0
--- /dev/null
+++ b/python/ql/test/experimental/import-resolution/ModuleExport.ql
@@ -0,0 +1,9 @@
+import python
+import semmle.python.dataflow.new.DataFlow
+import semmle.python.dataflow.new.internal.ImportResolution
+
+from Module m, string name, DataFlow::Node defn
+where
+ ImportResolution::module_export(m, name, defn) and
+ exists(m.getLocation().getFile().getRelativePath())
+select m.getName(), name, defn
From 6ba39d5fb348900a09e197e8bb9c951d78e7797b Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 22 Feb 2023 14:50:42 +0100
Subject: [PATCH 209/631] Python: Add import regression for re-exported things
---
.../import-resolution/ModuleExport.expected | 35 ++++++++++++++++++-
.../experimental/import-resolution/baz.py | 6 ++++
.../experimental/import-resolution/main.py | 4 +++
.../import-resolution/simplistic_reexport.py | 19 ++++++++++
4 files changed, 63 insertions(+), 1 deletion(-)
create mode 100644 python/ql/test/experimental/import-resolution/baz.py
create mode 100644 python/ql/test/experimental/import-resolution/simplistic_reexport.py
diff --git a/python/ql/test/experimental/import-resolution/ModuleExport.expected b/python/ql/test/experimental/import-resolution/ModuleExport.expected
index 6733a9e378a..dc633f7e707 100644
--- a/python/ql/test/experimental/import-resolution/ModuleExport.expected
+++ b/python/ql/test/experimental/import-resolution/ModuleExport.expected
@@ -48,6 +48,19 @@
| bar | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
| bar | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
| bar | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
+| baz | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
+| baz | __file__ | baz.py:2:7:2:14 | ControlFlowNode for __file__ |
+| baz | __file__ | baz.py:6:6:6:13 | ControlFlowNode for __file__ |
+| baz | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
+| baz | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
+| baz | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
+| baz | baz_attr | baz.py:4:12:4:21 | ControlFlowNode for Str |
+| baz | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
+| baz | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
+| baz | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
+| baz | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
+| baz | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
+| baz | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| foo | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
| foo | __file__ | foo.py:2:7:2:14 | ControlFlowNode for __file__ |
| foo | __file__ | foo.py:14:6:14:13 | ControlFlowNode for __file__ |
@@ -95,7 +108,7 @@
| if_then_else_refined | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| main | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
| main | __file__ | main.py:24:7:24:14 | ControlFlowNode for __file__ |
-| main | __file__ | main.py:103:6:103:13 | ControlFlowNode for __file__ |
+| main | __file__ | main.py:107:6:107:13 | ControlFlowNode for __file__ |
| main | __file__ | package/subpackage2/__init__.py:2:7:2:14 | ControlFlowNode for __file__ |
| main | __file__ | package/subpackage2/__init__.py:6:6:6:13 | ControlFlowNode for __file__ |
| main | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
@@ -130,6 +143,7 @@
| main | print_function | main.py:21:24:21:37 | ControlFlowNode for ImportMember |
| main | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
| main | refined | main.py:97:8:97:14 | ControlFlowNode for ImportExpr |
+| main | simplistic_reexport | main.py:103:8:103:26 | ControlFlowNode for ImportExpr |
| main | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| main | subpackage2_attr | package/subpackage2/__init__.py:4:20:4:37 | ControlFlowNode for Str |
| main | subpackage_attr | main.py:46:6:46:23 | ControlFlowNode for ImportExpr |
@@ -219,6 +233,25 @@
| refined | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
| refined | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
| refined | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
+| simplistic_reexport | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
+| simplistic_reexport | __file__ | baz.py:2:7:2:14 | ControlFlowNode for __file__ |
+| simplistic_reexport | __file__ | baz.py:6:6:6:13 | ControlFlowNode for __file__ |
+| simplistic_reexport | __file__ | simplistic_reexport.py:4:7:4:14 | ControlFlowNode for __file__ |
+| simplistic_reexport | __file__ | simplistic_reexport.py:19:6:19:13 | ControlFlowNode for __file__ |
+| simplistic_reexport | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
+| simplistic_reexport | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
+| simplistic_reexport | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
+| simplistic_reexport | bar_attr | simplistic_reexport.py:6:6:6:8 | ControlFlowNode for ImportExpr |
+| simplistic_reexport | bar_attr | simplistic_reexport.py:6:17:6:24 | ControlFlowNode for ImportMember |
+| simplistic_reexport | bar_attr | simplistic_reexport.py:9:12:9:24 | ControlFlowNode for Str |
+| simplistic_reexport | baz_attr | baz.py:4:12:4:21 | ControlFlowNode for Str |
+| simplistic_reexport | baz_attr | simplistic_reexport.py:16:12:16:24 | ControlFlowNode for Str |
+| simplistic_reexport | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
+| simplistic_reexport | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
+| simplistic_reexport | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
+| simplistic_reexport | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
+| simplistic_reexport | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
+| simplistic_reexport | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| trace | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
| trace | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
| trace | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
diff --git a/python/ql/test/experimental/import-resolution/baz.py b/python/ql/test/experimental/import-resolution/baz.py
new file mode 100644
index 00000000000..66838a315be
--- /dev/null
+++ b/python/ql/test/experimental/import-resolution/baz.py
@@ -0,0 +1,6 @@
+from trace import *
+enter(__file__)
+
+baz_attr = "baz_attr"
+
+exit(__file__)
diff --git a/python/ql/test/experimental/import-resolution/main.py b/python/ql/test/experimental/import-resolution/main.py
index f7cba832232..ee777707b92 100644
--- a/python/ql/test/experimental/import-resolution/main.py
+++ b/python/ql/test/experimental/import-resolution/main.py
@@ -100,6 +100,10 @@ check("refined.SOURCE", refined.SOURCE, refined.SOURCE, globals()) #$ prints=SOU
import if_then_else_refined # $ imports=if_then_else_refined as=if_then_else_refined
check("if_then_else_refined.src", if_then_else_refined.src, if_then_else_refined.src, globals()) #$ prints=SOURCE
+import simplistic_reexport # $ imports=simplistic_reexport as=simplistic_reexport
+check("simplistic_reexport.bar_attr", simplistic_reexport.bar_attr, "overwritten", globals()) #$ prints=overwritten SPURIOUS: prints="" prints=bar_attr
+check("simplistic_reexport.baz_attr", simplistic_reexport.baz_attr, "overwritten", globals()) #$ prints=overwritten SPURIOUS: prints=baz_attr
+
exit(__file__)
print()
diff --git a/python/ql/test/experimental/import-resolution/simplistic_reexport.py b/python/ql/test/experimental/import-resolution/simplistic_reexport.py
new file mode 100644
index 00000000000..2847a8a66e0
--- /dev/null
+++ b/python/ql/test/experimental/import-resolution/simplistic_reexport.py
@@ -0,0 +1,19 @@
+# we might consider anything imported to also be exported, but this is not the case
+
+from trace import *
+enter(__file__)
+
+from bar import bar_attr
+check("bar_attr", bar_attr, "bar_attr", globals()) #$ prints=bar_attr
+
+bar_attr = "overwritten"
+check("bar_attr", bar_attr, "overwritten", globals()) #$ prints=overwritten
+
+
+from baz import *
+check("baz_attr", baz_attr, "baz_attr", globals()) #$ MISSING: prints=baz_attr
+
+baz_attr = "overwritten"
+check("baz_attr", baz_attr, "overwritten", globals()) #$ prints=overwritten
+
+exit(__file__)
From 4df7dfbff65e24d20286bf1fff4a343cfde354ef Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 22 Feb 2023 14:52:35 +0100
Subject: [PATCH 210/631] Python: Don't import module as module_attr
For `from import ` we would use to treat the ``
(ImportExpr) as a definition of the name ``.
Since this removes bad import-flow, and nothing broke, I'm guessing this
was never intentional.
---
.../dataflow/new/internal/ImportResolution.qll | 2 +-
.../import-resolution/ModuleExport.expected | 13 -------------
.../ql/test/experimental/import-resolution/main.py | 2 +-
3 files changed, 2 insertions(+), 15 deletions(-)
diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll b/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll
index c440ace2922..a123f05dbc4 100644
--- a/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll
+++ b/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll
@@ -95,7 +95,7 @@ module ImportResolution {
)
or
exists(Alias a |
- defn.asExpr() = [a.getValue(), a.getValue().(ImportMember).getModule()] and
+ defn.asExpr() = a.getValue() and
a.getAsname().(Name).getId() = name and
defn.getScope() = m
)
diff --git a/python/ql/test/experimental/import-resolution/ModuleExport.expected b/python/ql/test/experimental/import-resolution/ModuleExport.expected
index dc633f7e707..11cf82374d6 100644
--- a/python/ql/test/experimental/import-resolution/ModuleExport.expected
+++ b/python/ql/test/experimental/import-resolution/ModuleExport.expected
@@ -114,39 +114,30 @@
| main | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
| main | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
| main | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
-| main | aliased_subpackage | main.py:54:6:54:12 | ControlFlowNode for ImportExpr |
| main | aliased_subpackage | main.py:54:21:54:52 | ControlFlowNode for ImportMember |
| main | aliased_subpackage | main.py:65:8:65:25 | ControlFlowNode for ImportExpr |
-| main | bar_attr | main.py:42:6:42:8 | ControlFlowNode for ImportExpr |
| main | bar_attr | main.py:42:17:42:24 | ControlFlowNode for ImportMember |
| main | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
-| main | clashing_attr | main.py:83:6:83:15 | ControlFlowNode for ImportExpr |
| main | clashing_attr | main.py:83:24:83:36 | ControlFlowNode for ImportMember |
| main | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
| main | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
| main | foo | main.py:27:8:27:10 | ControlFlowNode for ImportExpr |
| main | foo_alias | main.py:34:8:34:10 | ControlFlowNode for ImportExpr |
-| main | if_then_else_defined | main.py:93:6:93:17 | ControlFlowNode for ImportExpr |
| main | if_then_else_defined | main.py:93:26:93:45 | ControlFlowNode for ImportMember |
| main | if_then_else_refined | main.py:100:8:100:27 | ControlFlowNode for ImportExpr |
| main | local_import | main.py:57:1:57:19 | ControlFlowNode for FunctionExpr |
-| main | namespace_module_attr | main.py:79:10:79:43 | ControlFlowNode for ImportExpr |
| main | namespace_module_attr | main.py:79:52:79:72 | ControlFlowNode for ImportMember |
-| main | non_clashing_submodule | main.py:83:6:83:15 | ControlFlowNode for ImportExpr |
| main | non_clashing_submodule | main.py:83:39:83:60 | ControlFlowNode for ImportMember |
| main | package | main.py:69:8:69:25 | ControlFlowNode for ImportExpr |
| main | package | main.py:73:8:73:35 | ControlFlowNode for ImportExpr |
-| main | package_attr_alias | main.py:50:6:50:12 | ControlFlowNode for ImportExpr |
| main | package_attr_alias | main.py:50:21:50:54 | ControlFlowNode for ImportMember |
| main | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
-| main | print_function | main.py:21:6:21:15 | ControlFlowNode for ImportExpr |
| main | print_function | main.py:21:24:21:37 | ControlFlowNode for ImportMember |
| main | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
| main | refined | main.py:97:8:97:14 | ControlFlowNode for ImportExpr |
| main | simplistic_reexport | main.py:103:8:103:26 | ControlFlowNode for ImportExpr |
| main | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| main | subpackage2_attr | package/subpackage2/__init__.py:4:20:4:37 | ControlFlowNode for Str |
-| main | subpackage_attr | main.py:46:6:46:23 | ControlFlowNode for ImportExpr |
| main | subpackage_attr | main.py:46:32:46:46 | ControlFlowNode for ImportMember |
| main | sys | main.py:22:8:22:10 | ControlFlowNode for ImportExpr |
| namespace_package.namespace_module | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
@@ -198,9 +189,7 @@
| package.subpackage.__init__ | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
| package.subpackage.__init__ | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
| package.subpackage.__init__ | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
-| package.subpackage.__init__ | imported_attr | package/subpackage/__init__.py:7:6:7:7 | ControlFlowNode for ImportExpr |
| package.subpackage.__init__ | imported_attr | package/subpackage/__init__.py:7:16:7:55 | ControlFlowNode for ImportMember |
-| package.subpackage.__init__ | irrelevant_attr | package/subpackage/__init__.py:11:6:11:15 | ControlFlowNode for ImportExpr |
| package.subpackage.__init__ | irrelevant_attr | package/subpackage/__init__.py:11:24:11:38 | ControlFlowNode for ImportMember |
| package.subpackage.__init__ | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
| package.subpackage.__init__ | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
@@ -241,7 +230,6 @@
| simplistic_reexport | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
| simplistic_reexport | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
| simplistic_reexport | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
-| simplistic_reexport | bar_attr | simplistic_reexport.py:6:6:6:8 | ControlFlowNode for ImportExpr |
| simplistic_reexport | bar_attr | simplistic_reexport.py:6:17:6:24 | ControlFlowNode for ImportMember |
| simplistic_reexport | bar_attr | simplistic_reexport.py:9:12:9:24 | ControlFlowNode for Str |
| simplistic_reexport | baz_attr | baz.py:4:12:4:21 | ControlFlowNode for Str |
@@ -260,6 +248,5 @@
| trace | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
| trace | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
| trace | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
-| trace | print_function | trace.py:1:6:1:15 | ControlFlowNode for ImportExpr |
| trace | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
| trace | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
diff --git a/python/ql/test/experimental/import-resolution/main.py b/python/ql/test/experimental/import-resolution/main.py
index ee777707b92..4e09ad6173c 100644
--- a/python/ql/test/experimental/import-resolution/main.py
+++ b/python/ql/test/experimental/import-resolution/main.py
@@ -101,7 +101,7 @@ import if_then_else_refined # $ imports=if_then_else_refined as=if_then_else_ref
check("if_then_else_refined.src", if_then_else_refined.src, if_then_else_refined.src, globals()) #$ prints=SOURCE
import simplistic_reexport # $ imports=simplistic_reexport as=simplistic_reexport
-check("simplistic_reexport.bar_attr", simplistic_reexport.bar_attr, "overwritten", globals()) #$ prints=overwritten SPURIOUS: prints="" prints=bar_attr
+check("simplistic_reexport.bar_attr", simplistic_reexport.bar_attr, "overwritten", globals()) #$ prints=overwritten SPURIOUS: prints=bar_attr
check("simplistic_reexport.baz_attr", simplistic_reexport.baz_attr, "overwritten", globals()) #$ prints=overwritten SPURIOUS: prints=baz_attr
exit(__file__)
From d77ce4f3d7217e7e9c7f3469a3477fface9bbeed Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 22 Feb 2023 15:00:55 +0100
Subject: [PATCH 211/631] Python: minor rewrite of `from import *`
handling
---
.../python/dataflow/new/internal/ImportResolution.qll | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll b/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll
index a123f05dbc4..76542363a0c 100644
--- a/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll
+++ b/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll
@@ -86,7 +86,7 @@ module ImportResolution {
predicate module_export(Module m, string name, DataFlow::CfgNode defn) {
exists(EssaVariable v, EssaDefinition essaDef |
v.getName() = name and
- v.getAUse() = ImportStar::getStarImported*(m).getANormalExit() and
+ v.getAUse() = m.getANormalExit() and
allowedEssaImportStep*(essaDef, v.getDefinition())
|
defn.getNode() = essaDef.(AssignmentDefinition).getValue()
@@ -94,6 +94,10 @@ module ImportResolution {
defn.getNode() = essaDef.(ArgumentRefinement).getArgument()
)
or
+ // `from import *`
+ module_export(ImportStar::getStarImported+(m), name, defn)
+ or
+ // `import ` or `from import `
exists(Alias a |
defn.asExpr() = a.getValue() and
a.getAsname().(Name).getId() = name and
From be5812cf910a568037eeebd08592b572c0c09e5b Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 22 Feb 2023 15:08:47 +0100
Subject: [PATCH 212/631] Python: `from import *` ignores `__all__`
regression
Notice that `has_defined_all_indirection` all have both
`all_defined_bar_copy` and `all_defined_foo_copy` marked as exported,
even though only `all_defined_foo_copy` is available.
---
.../import-resolution/ModuleExport.expected | 71 +++++++++++++++++--
.../import-resolution/has_defined_all.py | 9 +++
.../import-resolution/has_defined_all_copy.py | 11 +++
.../has_defined_all_indirection.py | 6 ++
.../experimental/import-resolution/main.py | 42 +++++++++++
5 files changed, 134 insertions(+), 5 deletions(-)
create mode 100644 python/ql/test/experimental/import-resolution/has_defined_all.py
create mode 100644 python/ql/test/experimental/import-resolution/has_defined_all_copy.py
create mode 100644 python/ql/test/experimental/import-resolution/has_defined_all_indirection.py
diff --git a/python/ql/test/experimental/import-resolution/ModuleExport.expected b/python/ql/test/experimental/import-resolution/ModuleExport.expected
index 11cf82374d6..e73deb80468 100644
--- a/python/ql/test/experimental/import-resolution/ModuleExport.expected
+++ b/python/ql/test/experimental/import-resolution/ModuleExport.expected
@@ -76,6 +76,53 @@
| foo | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
| foo | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
| foo | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
+| has_defined_all | __all__ | has_defined_all.py:7:11:7:29 | ControlFlowNode for List |
+| has_defined_all | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
+| has_defined_all | __file__ | has_defined_all.py:2:7:2:14 | ControlFlowNode for __file__ |
+| has_defined_all | __file__ | has_defined_all.py:9:6:9:13 | ControlFlowNode for __file__ |
+| has_defined_all | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
+| has_defined_all | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
+| has_defined_all | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
+| has_defined_all | all_defined_bar | has_defined_all.py:5:19:5:35 | ControlFlowNode for Str |
+| has_defined_all | all_defined_foo | has_defined_all.py:4:19:4:35 | ControlFlowNode for Str |
+| has_defined_all | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
+| has_defined_all | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
+| has_defined_all | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
+| has_defined_all | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
+| has_defined_all | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
+| has_defined_all | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
+| has_defined_all_copy | __all__ | has_defined_all_copy.py:9:11:9:34 | ControlFlowNode for List |
+| has_defined_all_copy | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
+| has_defined_all_copy | __file__ | has_defined_all_copy.py:4:7:4:14 | ControlFlowNode for __file__ |
+| has_defined_all_copy | __file__ | has_defined_all_copy.py:11:6:11:13 | ControlFlowNode for __file__ |
+| has_defined_all_copy | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
+| has_defined_all_copy | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
+| has_defined_all_copy | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
+| has_defined_all_copy | all_defined_bar_copy | has_defined_all_copy.py:7:24:7:45 | ControlFlowNode for Str |
+| has_defined_all_copy | all_defined_foo_copy | has_defined_all_copy.py:6:24:6:45 | ControlFlowNode for Str |
+| has_defined_all_copy | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
+| has_defined_all_copy | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
+| has_defined_all_copy | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
+| has_defined_all_copy | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
+| has_defined_all_copy | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
+| has_defined_all_copy | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
+| has_defined_all_indirection | __all__ | has_defined_all_copy.py:9:11:9:34 | ControlFlowNode for List |
+| has_defined_all_indirection | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
+| has_defined_all_indirection | __file__ | has_defined_all_copy.py:4:7:4:14 | ControlFlowNode for __file__ |
+| has_defined_all_indirection | __file__ | has_defined_all_copy.py:11:6:11:13 | ControlFlowNode for __file__ |
+| has_defined_all_indirection | __file__ | has_defined_all_indirection.py:2:7:2:14 | ControlFlowNode for __file__ |
+| has_defined_all_indirection | __file__ | has_defined_all_indirection.py:6:6:6:13 | ControlFlowNode for __file__ |
+| has_defined_all_indirection | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
+| has_defined_all_indirection | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
+| has_defined_all_indirection | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
+| has_defined_all_indirection | all_defined_bar_copy | has_defined_all_copy.py:7:24:7:45 | ControlFlowNode for Str |
+| has_defined_all_indirection | all_defined_foo_copy | has_defined_all_copy.py:6:24:6:45 | ControlFlowNode for Str |
+| has_defined_all_indirection | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
+| has_defined_all_indirection | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
+| has_defined_all_indirection | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
+| has_defined_all_indirection | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
+| has_defined_all_indirection | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
+| has_defined_all_indirection | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| if_then_else | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
| if_then_else | __file__ | if_then_else.py:2:7:2:14 | ControlFlowNode for __file__ |
| if_then_else | __file__ | if_then_else.py:16:6:16:13 | ControlFlowNode for __file__ |
@@ -106,9 +153,17 @@
| if_then_else_refined | src | if_then_else_refined.py:11:11:11:16 | ControlFlowNode for SOURCE |
| if_then_else_refined | src | if_then_else_refined.py:13:11:13:16 | ControlFlowNode for SOURCE |
| if_then_else_refined | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
+| main | __all__ | has_defined_all.py:7:11:7:29 | ControlFlowNode for List |
+| main | __all__ | has_defined_all_copy.py:9:11:9:34 | ControlFlowNode for List |
| main | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
+| main | __file__ | has_defined_all.py:2:7:2:14 | ControlFlowNode for __file__ |
+| main | __file__ | has_defined_all.py:9:6:9:13 | ControlFlowNode for __file__ |
+| main | __file__ | has_defined_all_copy.py:4:7:4:14 | ControlFlowNode for __file__ |
+| main | __file__ | has_defined_all_copy.py:11:6:11:13 | ControlFlowNode for __file__ |
+| main | __file__ | has_defined_all_indirection.py:2:7:2:14 | ControlFlowNode for __file__ |
+| main | __file__ | has_defined_all_indirection.py:6:6:6:13 | ControlFlowNode for __file__ |
| main | __file__ | main.py:24:7:24:14 | ControlFlowNode for __file__ |
-| main | __file__ | main.py:107:6:107:13 | ControlFlowNode for __file__ |
+| main | __file__ | main.py:149:6:149:13 | ControlFlowNode for __file__ |
| main | __file__ | package/subpackage2/__init__.py:2:7:2:14 | ControlFlowNode for __file__ |
| main | __file__ | package/subpackage2/__init__.py:6:6:6:13 | ControlFlowNode for __file__ |
| main | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
@@ -116,6 +171,10 @@
| main | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
| main | aliased_subpackage | main.py:54:21:54:52 | ControlFlowNode for ImportMember |
| main | aliased_subpackage | main.py:65:8:65:25 | ControlFlowNode for ImportExpr |
+| main | all_defined_bar | has_defined_all.py:5:19:5:35 | ControlFlowNode for Str |
+| main | all_defined_bar_copy | has_defined_all_copy.py:7:24:7:45 | ControlFlowNode for Str |
+| main | all_defined_foo | has_defined_all.py:4:19:4:35 | ControlFlowNode for Str |
+| main | all_defined_foo_copy | has_defined_all_copy.py:6:24:6:45 | ControlFlowNode for Str |
| main | bar_attr | main.py:42:17:42:24 | ControlFlowNode for ImportMember |
| main | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
| main | clashing_attr | main.py:83:24:83:36 | ControlFlowNode for ImportMember |
@@ -123,8 +182,10 @@
| main | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
| main | foo | main.py:27:8:27:10 | ControlFlowNode for ImportExpr |
| main | foo_alias | main.py:34:8:34:10 | ControlFlowNode for ImportExpr |
-| main | if_then_else_defined | main.py:93:26:93:45 | ControlFlowNode for ImportMember |
-| main | if_then_else_refined | main.py:100:8:100:27 | ControlFlowNode for ImportExpr |
+| main | has_defined_all | main.py:100:8:100:22 | ControlFlowNode for ImportExpr |
+| main | has_defined_all_indirection | main.py:118:8:118:34 | ControlFlowNode for ImportExpr |
+| main | if_then_else_defined | main.py:135:26:135:45 | ControlFlowNode for ImportMember |
+| main | if_then_else_refined | main.py:142:8:142:27 | ControlFlowNode for ImportExpr |
| main | local_import | main.py:57:1:57:19 | ControlFlowNode for FunctionExpr |
| main | namespace_module_attr | main.py:79:52:79:72 | ControlFlowNode for ImportMember |
| main | non_clashing_submodule | main.py:83:39:83:60 | ControlFlowNode for ImportMember |
@@ -134,8 +195,8 @@
| main | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
| main | print_function | main.py:21:24:21:37 | ControlFlowNode for ImportMember |
| main | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
-| main | refined | main.py:97:8:97:14 | ControlFlowNode for ImportExpr |
-| main | simplistic_reexport | main.py:103:8:103:26 | ControlFlowNode for ImportExpr |
+| main | refined | main.py:139:8:139:14 | ControlFlowNode for ImportExpr |
+| main | simplistic_reexport | main.py:145:8:145:26 | ControlFlowNode for ImportExpr |
| main | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| main | subpackage2_attr | package/subpackage2/__init__.py:4:20:4:37 | ControlFlowNode for Str |
| main | subpackage_attr | main.py:46:32:46:46 | ControlFlowNode for ImportMember |
diff --git a/python/ql/test/experimental/import-resolution/has_defined_all.py b/python/ql/test/experimental/import-resolution/has_defined_all.py
new file mode 100644
index 00000000000..9716a9a2ee1
--- /dev/null
+++ b/python/ql/test/experimental/import-resolution/has_defined_all.py
@@ -0,0 +1,9 @@
+from trace import *
+enter(__file__)
+
+all_defined_foo = "all_defined_foo"
+all_defined_bar = "all_defined_bar"
+
+__all__ = ["all_defined_foo"]
+
+exit(__file__)
diff --git a/python/ql/test/experimental/import-resolution/has_defined_all_copy.py b/python/ql/test/experimental/import-resolution/has_defined_all_copy.py
new file mode 100644
index 00000000000..bfcb1eb5054
--- /dev/null
+++ b/python/ql/test/experimental/import-resolution/has_defined_all_copy.py
@@ -0,0 +1,11 @@
+# a copy of `has_defined_all.py` that is imported by `has_defined_all_indirection.py`
+# with its' own names such that we can check both `import *` without any cross-talk
+from trace import *
+enter(__file__)
+
+all_defined_foo_copy = "all_defined_foo_copy"
+all_defined_bar_copy = "all_defined_bar_copy"
+
+__all__ = ["all_defined_foo_copy"]
+
+exit(__file__)
diff --git a/python/ql/test/experimental/import-resolution/has_defined_all_indirection.py b/python/ql/test/experimental/import-resolution/has_defined_all_indirection.py
new file mode 100644
index 00000000000..c928205fdcb
--- /dev/null
+++ b/python/ql/test/experimental/import-resolution/has_defined_all_indirection.py
@@ -0,0 +1,6 @@
+from trace import *
+enter(__file__)
+
+from has_defined_all_copy import *
+
+exit(__file__)
diff --git a/python/ql/test/experimental/import-resolution/main.py b/python/ql/test/experimental/import-resolution/main.py
index 4e09ad6173c..fab3a447d64 100644
--- a/python/ql/test/experimental/import-resolution/main.py
+++ b/python/ql/test/experimental/import-resolution/main.py
@@ -84,6 +84,48 @@ from attr_clash import clashing_attr, non_clashing_submodule #$ imports=attr_cla
check("clashing_attr", clashing_attr, "clashing_attr", globals()) #$ prints=clashing_attr SPURIOUS: prints=""
check("non_clashing_submodule", non_clashing_submodule, "", globals()) #$ prints=""
+# check that import * only imports the __all__ attributes
+from has_defined_all import *
+check("all_defined_foo", all_defined_foo, "all_defined_foo", globals()) #$ prints=all_defined_foo
+
+try:
+ check("all_defined_bar", all_defined_bar, "all_defined_bar", globals()) #$ SPURIOUS: prints=all_defined_bar
+ raise Exception("Did not get expected NameError")
+except NameError as e:
+ if "all_defined_bar" in str(e):
+ print("Got expected NameError:", e)
+ else:
+ raise
+
+import has_defined_all # $ imports=has_defined_all as=has_defined_all
+check("has_defined_all.all_defined_foo", has_defined_all.all_defined_foo, "all_defined_foo", globals()) #$ prints=all_defined_foo
+check("has_defined_all.all_defined_bar", has_defined_all.all_defined_bar, "all_defined_bar", globals()) #$ prints=all_defined_bar
+
+# same check as above, but going through one level of indirection (which can make a difference)
+from has_defined_all_indirection import *
+check("all_defined_foo_copy", all_defined_foo_copy, "all_defined_foo_copy", globals()) #$ prints=all_defined_foo_copy
+
+try:
+ check("all_defined_bar_copy", all_defined_bar_copy, "all_defined_bar_copy", globals()) #$ SPURIOUS: prints=all_defined_bar_copy
+ raise Exception("Did not get expected NameError")
+except NameError as e:
+ if "all_defined_bar_copy" in str(e):
+ print("Got expected NameError:", e)
+ else:
+ raise
+
+# same check as above, but going through one level of indirection (which can make a difference)
+import has_defined_all_indirection # $ imports=has_defined_all_indirection as=has_defined_all_indirection
+check("has_defined_all_indirection.all_defined_foo_copy", has_defined_all_indirection.all_defined_foo_copy, "all_defined_foo_copy", globals()) #$ prints=all_defined_foo_copy
+
+try:
+ check("has_defined_all_indirection.all_defined_bar_copy", has_defined_all_indirection.all_defined_bar_copy, "all_defined_bar_copy", globals()) #$ SPURIOUS: prints=all_defined_bar_copy
+ raise Exception("Did not get expected AttributeError")
+except AttributeError as e:
+ if "all_defined_bar_copy" in str(e):
+ print("Got expected AttributeError:", e)
+ else:
+ raise
# check that import * from an __init__ file works
from package.subpackage2 import *
From c8a76246d8992dade7496409fc07da6e31423e42 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 22 Feb 2023 15:33:02 +0100
Subject: [PATCH 213/631] Python: Take `__all__` into consideration for
re-export of `from import *`
However, we can see that `from import *` and `import pkg` are
handled differently. Would have liked `has_defined_all_indirection` to
behave in the same way no matter how the import was made.
---
.../new/internal/ImportResolution.qll | 6 +-
.../import-resolution/ModuleExport.expected | 133 ------------------
.../experimental/import-resolution/main.py | 2 +-
3 files changed, 6 insertions(+), 135 deletions(-)
diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll b/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll
index 76542363a0c..5e0192b1553 100644
--- a/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll
+++ b/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll
@@ -95,7 +95,11 @@ module ImportResolution {
)
or
// `from import *`
- module_export(ImportStar::getStarImported+(m), name, defn)
+ exists(Module importedFrom |
+ importedFrom = ImportStar::getStarImported(m) and
+ module_export(importedFrom, name, defn) and
+ potential_module_export(importedFrom, name)
+ )
or
// `import ` or `from import `
exists(Alias a |
diff --git a/python/ql/test/experimental/import-resolution/ModuleExport.expected b/python/ql/test/experimental/import-resolution/ModuleExport.expected
index e73deb80468..341a58e4d34 100644
--- a/python/ql/test/experimental/import-resolution/ModuleExport.expected
+++ b/python/ql/test/experimental/import-resolution/ModuleExport.expected
@@ -1,180 +1,93 @@
-| attr_clash.__init__ | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
| attr_clash.__init__ | __file__ | attr_clash/__init__.py:2:7:2:14 | ControlFlowNode for __file__ |
| attr_clash.__init__ | __file__ | attr_clash/__init__.py:6:6:6:13 | ControlFlowNode for __file__ |
-| attr_clash.__init__ | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
-| attr_clash.__init__ | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
-| attr_clash.__init__ | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
| attr_clash.__init__ | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
| attr_clash.__init__ | clashing_attr | attr_clash/__init__.py:4:17:4:31 | ControlFlowNode for Str |
| attr_clash.__init__ | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
| attr_clash.__init__ | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
-| attr_clash.__init__ | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
-| attr_clash.__init__ | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
| attr_clash.__init__ | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
-| attr_clash.clashing_attr | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
| attr_clash.clashing_attr | __file__ | attr_clash/clashing_attr.py:2:7:2:14 | ControlFlowNode for __file__ |
| attr_clash.clashing_attr | __file__ | attr_clash/clashing_attr.py:4:6:4:13 | ControlFlowNode for __file__ |
-| attr_clash.clashing_attr | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
-| attr_clash.clashing_attr | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
-| attr_clash.clashing_attr | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
| attr_clash.clashing_attr | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
| attr_clash.clashing_attr | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
| attr_clash.clashing_attr | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
-| attr_clash.clashing_attr | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
-| attr_clash.clashing_attr | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
| attr_clash.clashing_attr | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
-| attr_clash.non_clashing_submodule | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
| attr_clash.non_clashing_submodule | __file__ | attr_clash/non_clashing_submodule.py:2:7:2:14 | ControlFlowNode for __file__ |
| attr_clash.non_clashing_submodule | __file__ | attr_clash/non_clashing_submodule.py:4:6:4:13 | ControlFlowNode for __file__ |
-| attr_clash.non_clashing_submodule | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
-| attr_clash.non_clashing_submodule | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
-| attr_clash.non_clashing_submodule | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
| attr_clash.non_clashing_submodule | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
| attr_clash.non_clashing_submodule | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
| attr_clash.non_clashing_submodule | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
-| attr_clash.non_clashing_submodule | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
-| attr_clash.non_clashing_submodule | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
| attr_clash.non_clashing_submodule | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
-| bar | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
| bar | __file__ | bar.py:2:7:2:14 | ControlFlowNode for __file__ |
| bar | __file__ | bar.py:6:6:6:13 | ControlFlowNode for __file__ |
-| bar | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
-| bar | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
-| bar | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
| bar | bar_attr | bar.py:4:12:4:21 | ControlFlowNode for Str |
| bar | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
| bar | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
| bar | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
-| bar | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
-| bar | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
| bar | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
-| baz | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
| baz | __file__ | baz.py:2:7:2:14 | ControlFlowNode for __file__ |
| baz | __file__ | baz.py:6:6:6:13 | ControlFlowNode for __file__ |
-| baz | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
-| baz | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
-| baz | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
| baz | baz_attr | baz.py:4:12:4:21 | ControlFlowNode for Str |
| baz | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
| baz | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
| baz | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
-| baz | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
-| baz | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
| baz | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
-| foo | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
| foo | __file__ | foo.py:2:7:2:14 | ControlFlowNode for __file__ |
| foo | __file__ | foo.py:14:6:14:13 | ControlFlowNode for __file__ |
| foo | __private_foo_attr | foo.py:8:22:8:41 | ControlFlowNode for Str |
-| foo | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
-| foo | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
-| foo | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
| foo | bar_reexported | foo.py:11:8:11:10 | ControlFlowNode for ImportExpr |
| foo | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
| foo | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
| foo | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
| foo | foo_attr | foo.py:5:12:5:21 | ControlFlowNode for Str |
-| foo | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
-| foo | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
| foo | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| has_defined_all | __all__ | has_defined_all.py:7:11:7:29 | ControlFlowNode for List |
-| has_defined_all | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
| has_defined_all | __file__ | has_defined_all.py:2:7:2:14 | ControlFlowNode for __file__ |
| has_defined_all | __file__ | has_defined_all.py:9:6:9:13 | ControlFlowNode for __file__ |
-| has_defined_all | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
-| has_defined_all | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
-| has_defined_all | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
| has_defined_all | all_defined_bar | has_defined_all.py:5:19:5:35 | ControlFlowNode for Str |
| has_defined_all | all_defined_foo | has_defined_all.py:4:19:4:35 | ControlFlowNode for Str |
| has_defined_all | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
| has_defined_all | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
| has_defined_all | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
-| has_defined_all | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
-| has_defined_all | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
| has_defined_all | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| has_defined_all_copy | __all__ | has_defined_all_copy.py:9:11:9:34 | ControlFlowNode for List |
-| has_defined_all_copy | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
| has_defined_all_copy | __file__ | has_defined_all_copy.py:4:7:4:14 | ControlFlowNode for __file__ |
| has_defined_all_copy | __file__ | has_defined_all_copy.py:11:6:11:13 | ControlFlowNode for __file__ |
-| has_defined_all_copy | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
-| has_defined_all_copy | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
-| has_defined_all_copy | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
| has_defined_all_copy | all_defined_bar_copy | has_defined_all_copy.py:7:24:7:45 | ControlFlowNode for Str |
| has_defined_all_copy | all_defined_foo_copy | has_defined_all_copy.py:6:24:6:45 | ControlFlowNode for Str |
| has_defined_all_copy | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
| has_defined_all_copy | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
| has_defined_all_copy | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
-| has_defined_all_copy | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
-| has_defined_all_copy | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
| has_defined_all_copy | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
-| has_defined_all_indirection | __all__ | has_defined_all_copy.py:9:11:9:34 | ControlFlowNode for List |
-| has_defined_all_indirection | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
-| has_defined_all_indirection | __file__ | has_defined_all_copy.py:4:7:4:14 | ControlFlowNode for __file__ |
-| has_defined_all_indirection | __file__ | has_defined_all_copy.py:11:6:11:13 | ControlFlowNode for __file__ |
| has_defined_all_indirection | __file__ | has_defined_all_indirection.py:2:7:2:14 | ControlFlowNode for __file__ |
| has_defined_all_indirection | __file__ | has_defined_all_indirection.py:6:6:6:13 | ControlFlowNode for __file__ |
-| has_defined_all_indirection | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
-| has_defined_all_indirection | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
-| has_defined_all_indirection | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
-| has_defined_all_indirection | all_defined_bar_copy | has_defined_all_copy.py:7:24:7:45 | ControlFlowNode for Str |
| has_defined_all_indirection | all_defined_foo_copy | has_defined_all_copy.py:6:24:6:45 | ControlFlowNode for Str |
| has_defined_all_indirection | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
| has_defined_all_indirection | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
| has_defined_all_indirection | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
-| has_defined_all_indirection | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
-| has_defined_all_indirection | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
| has_defined_all_indirection | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
-| if_then_else | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
| if_then_else | __file__ | if_then_else.py:2:7:2:14 | ControlFlowNode for __file__ |
| if_then_else | __file__ | if_then_else.py:16:6:16:13 | ControlFlowNode for __file__ |
-| if_then_else | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
-| if_then_else | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
-| if_then_else | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
| if_then_else | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
| if_then_else | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
| if_then_else | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
| if_then_else | if_then_else_defined | if_then_else.py:7:28:7:39 | ControlFlowNode for Str |
| if_then_else | if_then_else_defined | if_then_else.py:12:32:12:47 | ControlFlowNode for Str |
| if_then_else | if_then_else_defined | if_then_else.py:14:32:14:47 | ControlFlowNode for Str |
-| if_then_else | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
-| if_then_else | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
| if_then_else | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| if_then_else_refined | SOURCE | if_then_else_refined.py:6:1:6:15 | ControlFlowNode for ClassExpr |
-| if_then_else_refined | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
| if_then_else_refined | __file__ | if_then_else_refined.py:4:7:4:14 | ControlFlowNode for __file__ |
| if_then_else_refined | __file__ | if_then_else_refined.py:19:6:19:13 | ControlFlowNode for __file__ |
-| if_then_else_refined | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
-| if_then_else_refined | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
-| if_then_else_refined | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
| if_then_else_refined | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
| if_then_else_refined | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
| if_then_else_refined | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
-| if_then_else_refined | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
-| if_then_else_refined | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
| if_then_else_refined | src | if_then_else_refined.py:11:11:11:16 | ControlFlowNode for SOURCE |
| if_then_else_refined | src | if_then_else_refined.py:13:11:13:16 | ControlFlowNode for SOURCE |
| if_then_else_refined | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
-| main | __all__ | has_defined_all.py:7:11:7:29 | ControlFlowNode for List |
-| main | __all__ | has_defined_all_copy.py:9:11:9:34 | ControlFlowNode for List |
-| main | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
-| main | __file__ | has_defined_all.py:2:7:2:14 | ControlFlowNode for __file__ |
-| main | __file__ | has_defined_all.py:9:6:9:13 | ControlFlowNode for __file__ |
-| main | __file__ | has_defined_all_copy.py:4:7:4:14 | ControlFlowNode for __file__ |
-| main | __file__ | has_defined_all_copy.py:11:6:11:13 | ControlFlowNode for __file__ |
-| main | __file__ | has_defined_all_indirection.py:2:7:2:14 | ControlFlowNode for __file__ |
-| main | __file__ | has_defined_all_indirection.py:6:6:6:13 | ControlFlowNode for __file__ |
| main | __file__ | main.py:24:7:24:14 | ControlFlowNode for __file__ |
| main | __file__ | main.py:149:6:149:13 | ControlFlowNode for __file__ |
-| main | __file__ | package/subpackage2/__init__.py:2:7:2:14 | ControlFlowNode for __file__ |
-| main | __file__ | package/subpackage2/__init__.py:6:6:6:13 | ControlFlowNode for __file__ |
-| main | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
-| main | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
-| main | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
| main | aliased_subpackage | main.py:54:21:54:52 | ControlFlowNode for ImportMember |
| main | aliased_subpackage | main.py:65:8:65:25 | ControlFlowNode for ImportExpr |
-| main | all_defined_bar | has_defined_all.py:5:19:5:35 | ControlFlowNode for Str |
-| main | all_defined_bar_copy | has_defined_all_copy.py:7:24:7:45 | ControlFlowNode for Str |
| main | all_defined_foo | has_defined_all.py:4:19:4:35 | ControlFlowNode for Str |
-| main | all_defined_foo_copy | has_defined_all_copy.py:6:24:6:45 | ControlFlowNode for Str |
| main | bar_attr | main.py:42:17:42:24 | ControlFlowNode for ImportMember |
| main | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
| main | clashing_attr | main.py:83:24:83:36 | ControlFlowNode for ImportMember |
@@ -192,105 +105,61 @@
| main | package | main.py:69:8:69:25 | ControlFlowNode for ImportExpr |
| main | package | main.py:73:8:73:35 | ControlFlowNode for ImportExpr |
| main | package_attr_alias | main.py:50:21:50:54 | ControlFlowNode for ImportMember |
-| main | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
| main | print_function | main.py:21:24:21:37 | ControlFlowNode for ImportMember |
-| main | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
| main | refined | main.py:139:8:139:14 | ControlFlowNode for ImportExpr |
| main | simplistic_reexport | main.py:145:8:145:26 | ControlFlowNode for ImportExpr |
| main | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| main | subpackage2_attr | package/subpackage2/__init__.py:4:20:4:37 | ControlFlowNode for Str |
| main | subpackage_attr | main.py:46:32:46:46 | ControlFlowNode for ImportMember |
| main | sys | main.py:22:8:22:10 | ControlFlowNode for ImportExpr |
-| namespace_package.namespace_module | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
| namespace_package.namespace_module | __file__ | namespace_package/namespace_module.py:2:7:2:14 | ControlFlowNode for __file__ |
| namespace_package.namespace_module | __file__ | namespace_package/namespace_module.py:6:6:6:13 | ControlFlowNode for __file__ |
-| namespace_package.namespace_module | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
-| namespace_package.namespace_module | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
-| namespace_package.namespace_module | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
| namespace_package.namespace_module | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
| namespace_package.namespace_module | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
| namespace_package.namespace_module | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
| namespace_package.namespace_module | namespace_module_attr | namespace_package/namespace_module.py:4:25:4:47 | ControlFlowNode for Str |
-| namespace_package.namespace_module | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
-| namespace_package.namespace_module | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
| namespace_package.namespace_module | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
-| package.__init__ | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
| package.__init__ | __file__ | package/__init__.py:2:7:2:14 | ControlFlowNode for __file__ |
| package.__init__ | __file__ | package/__init__.py:7:6:7:13 | ControlFlowNode for __file__ |
-| package.__init__ | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
-| package.__init__ | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
-| package.__init__ | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
| package.__init__ | attr_used_in_subpackage | package/__init__.py:4:27:4:51 | ControlFlowNode for Str |
| package.__init__ | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
| package.__init__ | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
| package.__init__ | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
| package.__init__ | package_attr | package/__init__.py:5:16:5:29 | ControlFlowNode for Str |
-| package.__init__ | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
-| package.__init__ | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
| package.__init__ | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
-| package.subpackage2.__init__ | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
| package.subpackage2.__init__ | __file__ | package/subpackage2/__init__.py:2:7:2:14 | ControlFlowNode for __file__ |
| package.subpackage2.__init__ | __file__ | package/subpackage2/__init__.py:6:6:6:13 | ControlFlowNode for __file__ |
-| package.subpackage2.__init__ | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
-| package.subpackage2.__init__ | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
-| package.subpackage2.__init__ | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
| package.subpackage2.__init__ | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
| package.subpackage2.__init__ | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
| package.subpackage2.__init__ | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
-| package.subpackage2.__init__ | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
-| package.subpackage2.__init__ | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
| package.subpackage2.__init__ | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| package.subpackage2.__init__ | subpackage2_attr | package/subpackage2/__init__.py:4:20:4:37 | ControlFlowNode for Str |
-| package.subpackage.__init__ | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
| package.subpackage.__init__ | __file__ | package/subpackage/__init__.py:2:7:2:14 | ControlFlowNode for __file__ |
| package.subpackage.__init__ | __file__ | package/subpackage/__init__.py:14:6:14:13 | ControlFlowNode for __file__ |
-| package.subpackage.__init__ | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
-| package.subpackage.__init__ | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
-| package.subpackage.__init__ | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
| package.subpackage.__init__ | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
| package.subpackage.__init__ | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
| package.subpackage.__init__ | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
| package.subpackage.__init__ | imported_attr | package/subpackage/__init__.py:7:16:7:55 | ControlFlowNode for ImportMember |
| package.subpackage.__init__ | irrelevant_attr | package/subpackage/__init__.py:11:24:11:38 | ControlFlowNode for ImportMember |
-| package.subpackage.__init__ | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
-| package.subpackage.__init__ | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
| package.subpackage.__init__ | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| package.subpackage.__init__ | subpackage_attr | package/subpackage/__init__.py:4:19:4:35 | ControlFlowNode for Str |
-| package.subpackage.submodule | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
| package.subpackage.submodule | __file__ | package/subpackage/submodule.py:2:7:2:14 | ControlFlowNode for __file__ |
| package.subpackage.submodule | __file__ | package/subpackage/submodule.py:7:6:7:13 | ControlFlowNode for __file__ |
-| package.subpackage.submodule | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
-| package.subpackage.submodule | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
-| package.subpackage.submodule | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
| package.subpackage.submodule | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
| package.subpackage.submodule | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
| package.subpackage.submodule | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
| package.subpackage.submodule | irrelevant_attr | package/subpackage/submodule.py:5:19:5:35 | ControlFlowNode for Str |
-| package.subpackage.submodule | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
-| package.subpackage.submodule | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
| package.subpackage.submodule | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| package.subpackage.submodule | submodule_attr | package/subpackage/submodule.py:4:18:4:33 | ControlFlowNode for Str |
| refined | SOURCE | refined.py:4:1:4:21 | ControlFlowNode for ClassExpr |
-| refined | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
| refined | __file__ | refined.py:2:7:2:14 | ControlFlowNode for __file__ |
| refined | __file__ | refined.py:14:6:14:13 | ControlFlowNode for __file__ |
-| refined | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
-| refined | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
-| refined | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
| refined | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
| refined | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
| refined | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
-| refined | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
-| refined | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
| refined | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
-| simplistic_reexport | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
-| simplistic_reexport | __file__ | baz.py:2:7:2:14 | ControlFlowNode for __file__ |
-| simplistic_reexport | __file__ | baz.py:6:6:6:13 | ControlFlowNode for __file__ |
| simplistic_reexport | __file__ | simplistic_reexport.py:4:7:4:14 | ControlFlowNode for __file__ |
| simplistic_reexport | __file__ | simplistic_reexport.py:19:6:19:13 | ControlFlowNode for __file__ |
-| simplistic_reexport | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
-| simplistic_reexport | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
-| simplistic_reexport | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
| simplistic_reexport | bar_attr | simplistic_reexport.py:6:17:6:24 | ControlFlowNode for ImportMember |
| simplistic_reexport | bar_attr | simplistic_reexport.py:9:12:9:24 | ControlFlowNode for Str |
| simplistic_reexport | baz_attr | baz.py:4:12:4:21 | ControlFlowNode for Str |
@@ -298,8 +167,6 @@
| simplistic_reexport | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
| simplistic_reexport | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
| simplistic_reexport | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
-| simplistic_reexport | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
-| simplistic_reexport | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
| simplistic_reexport | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| trace | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
| trace | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
diff --git a/python/ql/test/experimental/import-resolution/main.py b/python/ql/test/experimental/import-resolution/main.py
index fab3a447d64..98b97fafb0e 100644
--- a/python/ql/test/experimental/import-resolution/main.py
+++ b/python/ql/test/experimental/import-resolution/main.py
@@ -119,7 +119,7 @@ import has_defined_all_indirection # $ imports=has_defined_all_indirection as=ha
check("has_defined_all_indirection.all_defined_foo_copy", has_defined_all_indirection.all_defined_foo_copy, "all_defined_foo_copy", globals()) #$ prints=all_defined_foo_copy
try:
- check("has_defined_all_indirection.all_defined_bar_copy", has_defined_all_indirection.all_defined_bar_copy, "all_defined_bar_copy", globals()) #$ SPURIOUS: prints=all_defined_bar_copy
+ check("has_defined_all_indirection.all_defined_bar_copy", has_defined_all_indirection.all_defined_bar_copy, "all_defined_bar_copy", globals())
raise Exception("Did not get expected AttributeError")
except AttributeError as e:
if "all_defined_bar_copy" in str(e):
From 8eaaf8e3e5aa7e24bacae7ad9df3d7727d37564e Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 22 Feb 2023 15:42:28 +0100
Subject: [PATCH 214/631] Python: Ignore `trace.py` in `ModuleExport.ql` test
I guess we could have done this at the very start of introducing this
test in this PR, but I think the last commit was mostly inspired from
looking at all the things that evidently was re-exported from the trace
import, even when I knew they were not available because of the
`__all__` definition.
---
.../import-resolution/ModuleExport.expected | 86 -------------------
.../import-resolution/ModuleExport.ql | 3 +-
2 files changed, 2 insertions(+), 87 deletions(-)
diff --git a/python/ql/test/experimental/import-resolution/ModuleExport.expected b/python/ql/test/experimental/import-resolution/ModuleExport.expected
index 341a58e4d34..ebe5e897444 100644
--- a/python/ql/test/experimental/import-resolution/ModuleExport.expected
+++ b/python/ql/test/experimental/import-resolution/ModuleExport.expected
@@ -1,98 +1,51 @@
| attr_clash.__init__ | __file__ | attr_clash/__init__.py:2:7:2:14 | ControlFlowNode for __file__ |
| attr_clash.__init__ | __file__ | attr_clash/__init__.py:6:6:6:13 | ControlFlowNode for __file__ |
-| attr_clash.__init__ | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
| attr_clash.__init__ | clashing_attr | attr_clash/__init__.py:4:17:4:31 | ControlFlowNode for Str |
-| attr_clash.__init__ | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
-| attr_clash.__init__ | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
-| attr_clash.__init__ | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| attr_clash.clashing_attr | __file__ | attr_clash/clashing_attr.py:2:7:2:14 | ControlFlowNode for __file__ |
| attr_clash.clashing_attr | __file__ | attr_clash/clashing_attr.py:4:6:4:13 | ControlFlowNode for __file__ |
-| attr_clash.clashing_attr | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
-| attr_clash.clashing_attr | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
-| attr_clash.clashing_attr | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
-| attr_clash.clashing_attr | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| attr_clash.non_clashing_submodule | __file__ | attr_clash/non_clashing_submodule.py:2:7:2:14 | ControlFlowNode for __file__ |
| attr_clash.non_clashing_submodule | __file__ | attr_clash/non_clashing_submodule.py:4:6:4:13 | ControlFlowNode for __file__ |
-| attr_clash.non_clashing_submodule | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
-| attr_clash.non_clashing_submodule | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
-| attr_clash.non_clashing_submodule | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
-| attr_clash.non_clashing_submodule | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| bar | __file__ | bar.py:2:7:2:14 | ControlFlowNode for __file__ |
| bar | __file__ | bar.py:6:6:6:13 | ControlFlowNode for __file__ |
| bar | bar_attr | bar.py:4:12:4:21 | ControlFlowNode for Str |
-| bar | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
-| bar | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
-| bar | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
-| bar | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| baz | __file__ | baz.py:2:7:2:14 | ControlFlowNode for __file__ |
| baz | __file__ | baz.py:6:6:6:13 | ControlFlowNode for __file__ |
| baz | baz_attr | baz.py:4:12:4:21 | ControlFlowNode for Str |
-| baz | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
-| baz | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
-| baz | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
-| baz | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| foo | __file__ | foo.py:2:7:2:14 | ControlFlowNode for __file__ |
| foo | __file__ | foo.py:14:6:14:13 | ControlFlowNode for __file__ |
| foo | __private_foo_attr | foo.py:8:22:8:41 | ControlFlowNode for Str |
| foo | bar_reexported | foo.py:11:8:11:10 | ControlFlowNode for ImportExpr |
-| foo | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
-| foo | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
-| foo | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
| foo | foo_attr | foo.py:5:12:5:21 | ControlFlowNode for Str |
-| foo | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| has_defined_all | __all__ | has_defined_all.py:7:11:7:29 | ControlFlowNode for List |
| has_defined_all | __file__ | has_defined_all.py:2:7:2:14 | ControlFlowNode for __file__ |
| has_defined_all | __file__ | has_defined_all.py:9:6:9:13 | ControlFlowNode for __file__ |
| has_defined_all | all_defined_bar | has_defined_all.py:5:19:5:35 | ControlFlowNode for Str |
| has_defined_all | all_defined_foo | has_defined_all.py:4:19:4:35 | ControlFlowNode for Str |
-| has_defined_all | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
-| has_defined_all | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
-| has_defined_all | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
-| has_defined_all | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| has_defined_all_copy | __all__ | has_defined_all_copy.py:9:11:9:34 | ControlFlowNode for List |
| has_defined_all_copy | __file__ | has_defined_all_copy.py:4:7:4:14 | ControlFlowNode for __file__ |
| has_defined_all_copy | __file__ | has_defined_all_copy.py:11:6:11:13 | ControlFlowNode for __file__ |
| has_defined_all_copy | all_defined_bar_copy | has_defined_all_copy.py:7:24:7:45 | ControlFlowNode for Str |
| has_defined_all_copy | all_defined_foo_copy | has_defined_all_copy.py:6:24:6:45 | ControlFlowNode for Str |
-| has_defined_all_copy | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
-| has_defined_all_copy | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
-| has_defined_all_copy | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
-| has_defined_all_copy | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| has_defined_all_indirection | __file__ | has_defined_all_indirection.py:2:7:2:14 | ControlFlowNode for __file__ |
| has_defined_all_indirection | __file__ | has_defined_all_indirection.py:6:6:6:13 | ControlFlowNode for __file__ |
| has_defined_all_indirection | all_defined_foo_copy | has_defined_all_copy.py:6:24:6:45 | ControlFlowNode for Str |
-| has_defined_all_indirection | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
-| has_defined_all_indirection | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
-| has_defined_all_indirection | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
-| has_defined_all_indirection | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| if_then_else | __file__ | if_then_else.py:2:7:2:14 | ControlFlowNode for __file__ |
| if_then_else | __file__ | if_then_else.py:16:6:16:13 | ControlFlowNode for __file__ |
-| if_then_else | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
-| if_then_else | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
-| if_then_else | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
| if_then_else | if_then_else_defined | if_then_else.py:7:28:7:39 | ControlFlowNode for Str |
| if_then_else | if_then_else_defined | if_then_else.py:12:32:12:47 | ControlFlowNode for Str |
| if_then_else | if_then_else_defined | if_then_else.py:14:32:14:47 | ControlFlowNode for Str |
-| if_then_else | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| if_then_else_refined | SOURCE | if_then_else_refined.py:6:1:6:15 | ControlFlowNode for ClassExpr |
| if_then_else_refined | __file__ | if_then_else_refined.py:4:7:4:14 | ControlFlowNode for __file__ |
| if_then_else_refined | __file__ | if_then_else_refined.py:19:6:19:13 | ControlFlowNode for __file__ |
-| if_then_else_refined | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
-| if_then_else_refined | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
-| if_then_else_refined | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
| if_then_else_refined | src | if_then_else_refined.py:11:11:11:16 | ControlFlowNode for SOURCE |
| if_then_else_refined | src | if_then_else_refined.py:13:11:13:16 | ControlFlowNode for SOURCE |
-| if_then_else_refined | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| main | __file__ | main.py:24:7:24:14 | ControlFlowNode for __file__ |
| main | __file__ | main.py:149:6:149:13 | ControlFlowNode for __file__ |
| main | aliased_subpackage | main.py:54:21:54:52 | ControlFlowNode for ImportMember |
| main | aliased_subpackage | main.py:65:8:65:25 | ControlFlowNode for ImportExpr |
| main | all_defined_foo | has_defined_all.py:4:19:4:35 | ControlFlowNode for Str |
| main | bar_attr | main.py:42:17:42:24 | ControlFlowNode for ImportMember |
-| main | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
| main | clashing_attr | main.py:83:24:83:36 | ControlFlowNode for ImportMember |
-| main | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
-| main | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
| main | foo | main.py:27:8:27:10 | ControlFlowNode for ImportExpr |
| main | foo_alias | main.py:34:8:34:10 | ControlFlowNode for ImportExpr |
| main | has_defined_all | main.py:100:8:100:22 | ControlFlowNode for ImportExpr |
@@ -108,73 +61,34 @@
| main | print_function | main.py:21:24:21:37 | ControlFlowNode for ImportMember |
| main | refined | main.py:139:8:139:14 | ControlFlowNode for ImportExpr |
| main | simplistic_reexport | main.py:145:8:145:26 | ControlFlowNode for ImportExpr |
-| main | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| main | subpackage2_attr | package/subpackage2/__init__.py:4:20:4:37 | ControlFlowNode for Str |
| main | subpackage_attr | main.py:46:32:46:46 | ControlFlowNode for ImportMember |
| main | sys | main.py:22:8:22:10 | ControlFlowNode for ImportExpr |
| namespace_package.namespace_module | __file__ | namespace_package/namespace_module.py:2:7:2:14 | ControlFlowNode for __file__ |
| namespace_package.namespace_module | __file__ | namespace_package/namespace_module.py:6:6:6:13 | ControlFlowNode for __file__ |
-| namespace_package.namespace_module | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
-| namespace_package.namespace_module | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
-| namespace_package.namespace_module | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
| namespace_package.namespace_module | namespace_module_attr | namespace_package/namespace_module.py:4:25:4:47 | ControlFlowNode for Str |
-| namespace_package.namespace_module | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| package.__init__ | __file__ | package/__init__.py:2:7:2:14 | ControlFlowNode for __file__ |
| package.__init__ | __file__ | package/__init__.py:7:6:7:13 | ControlFlowNode for __file__ |
| package.__init__ | attr_used_in_subpackage | package/__init__.py:4:27:4:51 | ControlFlowNode for Str |
-| package.__init__ | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
-| package.__init__ | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
-| package.__init__ | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
| package.__init__ | package_attr | package/__init__.py:5:16:5:29 | ControlFlowNode for Str |
-| package.__init__ | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| package.subpackage2.__init__ | __file__ | package/subpackage2/__init__.py:2:7:2:14 | ControlFlowNode for __file__ |
| package.subpackage2.__init__ | __file__ | package/subpackage2/__init__.py:6:6:6:13 | ControlFlowNode for __file__ |
-| package.subpackage2.__init__ | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
-| package.subpackage2.__init__ | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
-| package.subpackage2.__init__ | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
-| package.subpackage2.__init__ | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| package.subpackage2.__init__ | subpackage2_attr | package/subpackage2/__init__.py:4:20:4:37 | ControlFlowNode for Str |
| package.subpackage.__init__ | __file__ | package/subpackage/__init__.py:2:7:2:14 | ControlFlowNode for __file__ |
| package.subpackage.__init__ | __file__ | package/subpackage/__init__.py:14:6:14:13 | ControlFlowNode for __file__ |
-| package.subpackage.__init__ | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
-| package.subpackage.__init__ | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
-| package.subpackage.__init__ | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
| package.subpackage.__init__ | imported_attr | package/subpackage/__init__.py:7:16:7:55 | ControlFlowNode for ImportMember |
| package.subpackage.__init__ | irrelevant_attr | package/subpackage/__init__.py:11:24:11:38 | ControlFlowNode for ImportMember |
-| package.subpackage.__init__ | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| package.subpackage.__init__ | subpackage_attr | package/subpackage/__init__.py:4:19:4:35 | ControlFlowNode for Str |
| package.subpackage.submodule | __file__ | package/subpackage/submodule.py:2:7:2:14 | ControlFlowNode for __file__ |
| package.subpackage.submodule | __file__ | package/subpackage/submodule.py:7:6:7:13 | ControlFlowNode for __file__ |
-| package.subpackage.submodule | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
-| package.subpackage.submodule | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
-| package.subpackage.submodule | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
| package.subpackage.submodule | irrelevant_attr | package/subpackage/submodule.py:5:19:5:35 | ControlFlowNode for Str |
-| package.subpackage.submodule | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| package.subpackage.submodule | submodule_attr | package/subpackage/submodule.py:4:18:4:33 | ControlFlowNode for Str |
| refined | SOURCE | refined.py:4:1:4:21 | ControlFlowNode for ClassExpr |
| refined | __file__ | refined.py:2:7:2:14 | ControlFlowNode for __file__ |
| refined | __file__ | refined.py:14:6:14:13 | ControlFlowNode for __file__ |
-| refined | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
-| refined | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
-| refined | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
-| refined | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
| simplistic_reexport | __file__ | simplistic_reexport.py:4:7:4:14 | ControlFlowNode for __file__ |
| simplistic_reexport | __file__ | simplistic_reexport.py:19:6:19:13 | ControlFlowNode for __file__ |
| simplistic_reexport | bar_attr | simplistic_reexport.py:6:17:6:24 | ControlFlowNode for ImportMember |
| simplistic_reexport | bar_attr | simplistic_reexport.py:9:12:9:24 | ControlFlowNode for Str |
| simplistic_reexport | baz_attr | baz.py:4:12:4:21 | ControlFlowNode for Str |
| simplistic_reexport | baz_attr | simplistic_reexport.py:16:12:16:24 | ControlFlowNode for Str |
-| simplistic_reexport | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
-| simplistic_reexport | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
-| simplistic_reexport | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
-| simplistic_reexport | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
-| trace | __all__ | trace.py:52:11:52:46 | ControlFlowNode for List |
-| trace | _indent_level | trace.py:3:17:3:17 | ControlFlowNode for IntegerLiteral |
-| trace | _print | trace.py:5:10:5:14 | ControlFlowNode for print |
-| trace | _status | trace.py:21:11:21:11 | ControlFlowNode for IntegerLiteral |
-| trace | check | trace.py:26:1:26:61 | ControlFlowNode for FunctionExpr |
-| trace | enter | trace.py:11:1:11:21 | ControlFlowNode for FunctionExpr |
-| trace | exit | trace.py:16:1:16:20 | ControlFlowNode for FunctionExpr |
-| trace | print | trace.py:7:1:7:27 | ControlFlowNode for FunctionExpr |
-| trace | print_function | trace.py:1:24:1:37 | ControlFlowNode for ImportMember |
-| trace | status | trace.py:23:1:23:13 | ControlFlowNode for FunctionExpr |
diff --git a/python/ql/test/experimental/import-resolution/ModuleExport.ql b/python/ql/test/experimental/import-resolution/ModuleExport.ql
index 9d45b0ec8d0..c4f0f7af7d8 100644
--- a/python/ql/test/experimental/import-resolution/ModuleExport.ql
+++ b/python/ql/test/experimental/import-resolution/ModuleExport.ql
@@ -5,5 +5,6 @@ import semmle.python.dataflow.new.internal.ImportResolution
from Module m, string name, DataFlow::Node defn
where
ImportResolution::module_export(m, name, defn) and
- exists(m.getLocation().getFile().getRelativePath())
+ exists(m.getLocation().getFile().getRelativePath()) and
+ not defn.getScope() = any(Module trace | trace.getName() = "trace")
select m.getName(), name, defn
From 321a4b4ef20111f6962f231ee66b8a8c4e52a08e Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 22 Feb 2023 16:32:53 +0100
Subject: [PATCH 215/631] Python: `ModuleExport.ql` test: ignore `main.py`
It's not very useful to look at, and it's a mess when you change any
tests to see all the changes lines in the expected output that you
really do not care about!
---
.../import-resolution/ModuleExport.expected | 25 -------------------
.../import-resolution/ModuleExport.ql | 3 ++-
2 files changed, 2 insertions(+), 26 deletions(-)
diff --git a/python/ql/test/experimental/import-resolution/ModuleExport.expected b/python/ql/test/experimental/import-resolution/ModuleExport.expected
index ebe5e897444..c1a1a6d2c35 100644
--- a/python/ql/test/experimental/import-resolution/ModuleExport.expected
+++ b/python/ql/test/experimental/import-resolution/ModuleExport.expected
@@ -39,31 +39,6 @@
| if_then_else_refined | __file__ | if_then_else_refined.py:19:6:19:13 | ControlFlowNode for __file__ |
| if_then_else_refined | src | if_then_else_refined.py:11:11:11:16 | ControlFlowNode for SOURCE |
| if_then_else_refined | src | if_then_else_refined.py:13:11:13:16 | ControlFlowNode for SOURCE |
-| main | __file__ | main.py:24:7:24:14 | ControlFlowNode for __file__ |
-| main | __file__ | main.py:149:6:149:13 | ControlFlowNode for __file__ |
-| main | aliased_subpackage | main.py:54:21:54:52 | ControlFlowNode for ImportMember |
-| main | aliased_subpackage | main.py:65:8:65:25 | ControlFlowNode for ImportExpr |
-| main | all_defined_foo | has_defined_all.py:4:19:4:35 | ControlFlowNode for Str |
-| main | bar_attr | main.py:42:17:42:24 | ControlFlowNode for ImportMember |
-| main | clashing_attr | main.py:83:24:83:36 | ControlFlowNode for ImportMember |
-| main | foo | main.py:27:8:27:10 | ControlFlowNode for ImportExpr |
-| main | foo_alias | main.py:34:8:34:10 | ControlFlowNode for ImportExpr |
-| main | has_defined_all | main.py:100:8:100:22 | ControlFlowNode for ImportExpr |
-| main | has_defined_all_indirection | main.py:118:8:118:34 | ControlFlowNode for ImportExpr |
-| main | if_then_else_defined | main.py:135:26:135:45 | ControlFlowNode for ImportMember |
-| main | if_then_else_refined | main.py:142:8:142:27 | ControlFlowNode for ImportExpr |
-| main | local_import | main.py:57:1:57:19 | ControlFlowNode for FunctionExpr |
-| main | namespace_module_attr | main.py:79:52:79:72 | ControlFlowNode for ImportMember |
-| main | non_clashing_submodule | main.py:83:39:83:60 | ControlFlowNode for ImportMember |
-| main | package | main.py:69:8:69:25 | ControlFlowNode for ImportExpr |
-| main | package | main.py:73:8:73:35 | ControlFlowNode for ImportExpr |
-| main | package_attr_alias | main.py:50:21:50:54 | ControlFlowNode for ImportMember |
-| main | print_function | main.py:21:24:21:37 | ControlFlowNode for ImportMember |
-| main | refined | main.py:139:8:139:14 | ControlFlowNode for ImportExpr |
-| main | simplistic_reexport | main.py:145:8:145:26 | ControlFlowNode for ImportExpr |
-| main | subpackage2_attr | package/subpackage2/__init__.py:4:20:4:37 | ControlFlowNode for Str |
-| main | subpackage_attr | main.py:46:32:46:46 | ControlFlowNode for ImportMember |
-| main | sys | main.py:22:8:22:10 | ControlFlowNode for ImportExpr |
| namespace_package.namespace_module | __file__ | namespace_package/namespace_module.py:2:7:2:14 | ControlFlowNode for __file__ |
| namespace_package.namespace_module | __file__ | namespace_package/namespace_module.py:6:6:6:13 | ControlFlowNode for __file__ |
| namespace_package.namespace_module | namespace_module_attr | namespace_package/namespace_module.py:4:25:4:47 | ControlFlowNode for Str |
diff --git a/python/ql/test/experimental/import-resolution/ModuleExport.ql b/python/ql/test/experimental/import-resolution/ModuleExport.ql
index c4f0f7af7d8..4b84527573e 100644
--- a/python/ql/test/experimental/import-resolution/ModuleExport.ql
+++ b/python/ql/test/experimental/import-resolution/ModuleExport.ql
@@ -6,5 +6,6 @@ from Module m, string name, DataFlow::Node defn
where
ImportResolution::module_export(m, name, defn) and
exists(m.getLocation().getFile().getRelativePath()) and
- not defn.getScope() = any(Module trace | trace.getName() = "trace")
+ not defn.getScope() = any(Module trace | trace.getName() = "trace") and
+ not m.getName() = "main"
select m.getName(), name, defn
From bea0acb497f4ac9426c17508380f1b7bfea393ef Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Thu, 23 Feb 2023 00:22:01 +0100
Subject: [PATCH 216/631] Python: Add barrier test to import resolution
Just like the one added for `py/insecure-protocol` in fb425b7, but
instead added in the import-resolution tests, such that we don't have to
remember it's in a completely different directory.
---
.../import-resolution/ModuleExport.expected | 3 +++
.../import-resolution/block_flow_check.py | 14 ++++++++++++++
.../experimental/import-resolution/importflow.ql | 4 ++++
.../ql/test/experimental/import-resolution/main.py | 4 ++++
4 files changed, 25 insertions(+)
create mode 100644 python/ql/test/experimental/import-resolution/block_flow_check.py
diff --git a/python/ql/test/experimental/import-resolution/ModuleExport.expected b/python/ql/test/experimental/import-resolution/ModuleExport.expected
index c1a1a6d2c35..abfa29d0f58 100644
--- a/python/ql/test/experimental/import-resolution/ModuleExport.expected
+++ b/python/ql/test/experimental/import-resolution/ModuleExport.expected
@@ -11,6 +11,9 @@
| baz | __file__ | baz.py:2:7:2:14 | ControlFlowNode for __file__ |
| baz | __file__ | baz.py:6:6:6:13 | ControlFlowNode for __file__ |
| baz | baz_attr | baz.py:4:12:4:21 | ControlFlowNode for Str |
+| block_flow_check | SOURCE | block_flow_check.py:4:1:4:21 | ControlFlowNode for ClassExpr |
+| block_flow_check | __file__ | block_flow_check.py:2:7:2:14 | ControlFlowNode for __file__ |
+| block_flow_check | __file__ | block_flow_check.py:14:6:14:13 | ControlFlowNode for __file__ |
| foo | __file__ | foo.py:2:7:2:14 | ControlFlowNode for __file__ |
| foo | __file__ | foo.py:14:6:14:13 | ControlFlowNode for __file__ |
| foo | __private_foo_attr | foo.py:8:22:8:41 | ControlFlowNode for Str |
diff --git a/python/ql/test/experimental/import-resolution/block_flow_check.py b/python/ql/test/experimental/import-resolution/block_flow_check.py
new file mode 100644
index 00000000000..cd939b037e6
--- /dev/null
+++ b/python/ql/test/experimental/import-resolution/block_flow_check.py
@@ -0,0 +1,14 @@
+from trace import *
+enter(__file__)
+
+class SOURCE(object):
+ @staticmethod
+ def block_flow(): pass
+
+check("SOURCE", SOURCE, SOURCE, globals()) #$ prints=SOURCE
+
+SOURCE.block_flow()
+
+check("SOURCE", SOURCE, SOURCE, globals())
+
+exit(__file__)
diff --git a/python/ql/test/experimental/import-resolution/importflow.ql b/python/ql/test/experimental/import-resolution/importflow.ql
index 94f5e4b0ec8..afb2278d1c5 100644
--- a/python/ql/test/experimental/import-resolution/importflow.ql
+++ b/python/ql/test/experimental/import-resolution/importflow.ql
@@ -66,6 +66,10 @@ private class ImportConfiguration extends DataFlow::Configuration {
override predicate isSink(DataFlow::Node sink) {
sink = API::moduleImport("trace").getMember("check").getACall().getArg(1)
}
+
+ override predicate isBarrier(DataFlow::Node node) {
+ exists(DataFlow::MethodCallNode call | call.calls(node, "block_flow"))
+ }
}
class ResolutionTest extends InlineExpectationsTest {
diff --git a/python/ql/test/experimental/import-resolution/main.py b/python/ql/test/experimental/import-resolution/main.py
index 98b97fafb0e..31a4f60d56d 100644
--- a/python/ql/test/experimental/import-resolution/main.py
+++ b/python/ql/test/experimental/import-resolution/main.py
@@ -146,6 +146,10 @@ import simplistic_reexport # $ imports=simplistic_reexport as=simplistic_reexpor
check("simplistic_reexport.bar_attr", simplistic_reexport.bar_attr, "overwritten", globals()) #$ prints=overwritten SPURIOUS: prints=bar_attr
check("simplistic_reexport.baz_attr", simplistic_reexport.baz_attr, "overwritten", globals()) #$ prints=overwritten SPURIOUS: prints=baz_attr
+# check that we don't treat all assignments as being exports
+import block_flow_check #$ imports=block_flow_check as=block_flow_check
+check("block_flow_check.SOURCE", block_flow_check.SOURCE, block_flow_check.SOURCE, globals()) #$ SPURIOUS: prints=SOURCE
+
exit(__file__)
print()
From 97fefd2545d64dc07eeda5081804999c8cff0af9 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Wed, 22 Feb 2023 16:04:03 +0100
Subject: [PATCH 217/631] Python: Attempt to fix import flow
It's nice that it fixes the `InsecureProtocol` test-case (which maybe
should have been a test-case for the import resolution library in the
first place?)
But it's not quite right:
1. it adds spurious flow for `clashing_attr`
2. it runs into huge problems for typetracking_imports/tracked.expected
3. it runs into the problem for
https://github.com/github/codeql/pull/10176 with an `from
import *` blocking flow from previously defined variable, that is NOT
overridden. (simplistic_reexport.bar_attr)
---
.../new/internal/ImportResolution.qll | 59 ++++--
.../typetracking_imports/tracked.expected | 19 ++
.../import-resolution/ModuleExport.expected | 194 ++++++++++++------
.../experimental/import-resolution/main.py | 6 +-
.../InsecureProtocol.expected | 3 -
5 files changed, 192 insertions(+), 89 deletions(-)
diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll b/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll
index 5e0192b1553..5090ba8fcd3 100644
--- a/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll
+++ b/python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll
@@ -78,34 +78,63 @@ module ImportResolution {
}
/**
- * Holds if the module `m` defines a name `name` by assigning `defn` to it. This is an
- * overapproximation, as `name` may not in fact be exported (e.g. by defining an `__all__` that does
- * not include `name`).
+ * Holds if the module `m` defines a name `name` with the value `val`. The value
+ * represents the value `name` will have at the end of the module (the last place we
+ * have def-use flow to).
+ *
+ * Note: The handling of re-exporting imports is a bit simplistic. We assume that if
+ * an import is made, it will be re-exported (which will not be the case if a new
+ * value is assigned to the name, or it is deleted).
*/
pragma[nomagic]
- predicate module_export(Module m, string name, DataFlow::CfgNode defn) {
- exists(EssaVariable v, EssaDefinition essaDef |
- v.getName() = name and
- v.getAUse() = m.getANormalExit() and
- allowedEssaImportStep*(essaDef, v.getDefinition())
+ predicate module_export(Module m, string name, DataFlow::Node val) {
+ // Definitions made inside `m` itself
+ //
+ // for code such as `foo = ...; foo.bar = ...` there will be TWO
+ // EssaDefinition/EssaVariable. One for `foo = ...` (AssignmentDefinition) and one
+ // for `foo.bar = ...`. The one for `foo.bar = ...` (EssaNodeRefinement). The
+ // EssaNodeRefinement is the one that will reach the end of the module (normal
+ // exit).
+ //
+ // However, we cannot just use the EssaNodeRefinement as the `val`, because the
+ // normal data-flow depends on use-use flow, and use-use flow targets CFG nodes not
+ // EssaNodes. So we need to go back from the EssaDefinition/EssaVariable that
+ // reaches the end of the module, to the first definition of the variable, and then
+ // track forwards using use-use flow to find a suitable CFG node that has flow into
+ // it from use-use flow.
+ exists(EssaVariable lastUseVar, EssaVariable firstDef |
+ lastUseVar.getName() = name and
+ // we ignore special variable $ introduced by our analysis (not used for anything)
+ // we ignore special variable * introduced by `from import *` -- TODO: understand why we even have this?
+ not name in ["$", "*"] and
+ lastUseVar.getAUse() = m.getANormalExit() and
+ allowedEssaImportStep*(firstDef, lastUseVar) and
+ not allowedEssaImportStep(_, firstDef)
|
- defn.getNode() = essaDef.(AssignmentDefinition).getValue()
+ not EssaFlow::defToFirstUse(firstDef, _) and
+ val.asVar() = firstDef
or
- defn.getNode() = essaDef.(ArgumentRefinement).getArgument()
+ exists(ControlFlowNode mid, ControlFlowNode end |
+ EssaFlow::defToFirstUse(firstDef, mid) and
+ EssaFlow::useToNextUse*(mid, end) and
+ not EssaFlow::useToNextUse(end, _) and
+ lastUseVar.getAUse() = end and
+ val.asCfgNode() = end
+ )
)
or
- // `from import *`
+ // re-exports from `from import *`
exists(Module importedFrom |
importedFrom = ImportStar::getStarImported(m) and
- module_export(importedFrom, name, defn) and
+ module_export(importedFrom, name, val) and
potential_module_export(importedFrom, name)
)
or
- // `import ` or `from import `
+ // re-exports from `import ` or `from import `
exists(Alias a |
- defn.asExpr() = a.getValue() and
+ val.asExpr() = a.getValue() and
a.getAsname().(Name).getId() = name and
- defn.getScope() = m
+ val.getScope() = m
)
}
diff --git a/python/ql/test/experimental/dataflow/typetracking_imports/tracked.expected b/python/ql/test/experimental/dataflow/typetracking_imports/tracked.expected
index e69de29bb2d..546325a8645 100644
--- a/python/ql/test/experimental/dataflow/typetracking_imports/tracked.expected
+++ b/python/ql/test/experimental/dataflow/typetracking_imports/tracked.expected
@@ -0,0 +1,19 @@
+| pkg/alias_only_direct.py:1:26:1:36 | Comment # $ tracked | Missing result:tracked= |
+| pkg/alias_problem.py:1:26:1:36 | Comment # $ tracked | Missing result:tracked= |
+| pkg/alias_problem_fixed.py:3:26:3:36 | Comment # $ tracked | Missing result:tracked= |
+| pkg/problem_absolute_import.py:1:29:1:39 | Comment # $ tracked | Missing result:tracked= |
+| pkg/use.py:2:30:2:40 | Comment # $ tracked | Missing result:tracked= |
+| pkg/use.py:3:16:3:26 | Comment # $ tracked | Missing result:tracked= |
+| pkg/use.py:9:36:9:46 | Comment # $ tracked | Missing result:tracked= |
+| pkg/use.py:10:16:10:26 | Comment # $ tracked | Missing result:tracked= |
+| pkg/use.py:16:42:16:52 | Comment # $ tracked | Missing result:tracked= |
+| pkg/use.py:17:16:17:26 | Comment # $ tracked | Missing result:tracked= |
+| pkg/use.py:23:33:23:43 | Comment # $ tracked | Missing result:tracked= |
+| pkg/use.py:24:16:24:26 | Comment # $ tracked | Missing result:tracked= |
+| pkg/use.py:30:40:30:50 | Comment # $ tracked | Missing result:tracked= |
+| pkg/use.py:31:16:31:26 | Comment # $ tracked | Missing result:tracked= |
+| pkg/use.py:37:49:37:59 | Comment # $ tracked | Missing result:tracked= |
+| pkg/use.py:38:16:38:26 | Comment # $ tracked | Missing result:tracked= |
+| pkg/use.py:43:47:43:57 | Comment # $ tracked | Missing result:tracked= |
+| pkg/use.py:44:16:44:26 | Comment # $ tracked | Missing result:tracked= |
+| pkg/works_absolute_import.py:2:29:2:39 | Comment # $ tracked | Missing result:tracked= |
diff --git a/python/ql/test/experimental/import-resolution/ModuleExport.expected b/python/ql/test/experimental/import-resolution/ModuleExport.expected
index abfa29d0f58..ad2dffde65b 100644
--- a/python/ql/test/experimental/import-resolution/ModuleExport.expected
+++ b/python/ql/test/experimental/import-resolution/ModuleExport.expected
@@ -1,72 +1,130 @@
-| attr_clash.__init__ | __file__ | attr_clash/__init__.py:2:7:2:14 | ControlFlowNode for __file__ |
-| attr_clash.__init__ | __file__ | attr_clash/__init__.py:6:6:6:13 | ControlFlowNode for __file__ |
-| attr_clash.__init__ | clashing_attr | attr_clash/__init__.py:4:17:4:31 | ControlFlowNode for Str |
-| attr_clash.clashing_attr | __file__ | attr_clash/clashing_attr.py:2:7:2:14 | ControlFlowNode for __file__ |
-| attr_clash.clashing_attr | __file__ | attr_clash/clashing_attr.py:4:6:4:13 | ControlFlowNode for __file__ |
-| attr_clash.non_clashing_submodule | __file__ | attr_clash/non_clashing_submodule.py:2:7:2:14 | ControlFlowNode for __file__ |
-| attr_clash.non_clashing_submodule | __file__ | attr_clash/non_clashing_submodule.py:4:6:4:13 | ControlFlowNode for __file__ |
-| bar | __file__ | bar.py:2:7:2:14 | ControlFlowNode for __file__ |
-| bar | __file__ | bar.py:6:6:6:13 | ControlFlowNode for __file__ |
-| bar | bar_attr | bar.py:4:12:4:21 | ControlFlowNode for Str |
-| baz | __file__ | baz.py:2:7:2:14 | ControlFlowNode for __file__ |
-| baz | __file__ | baz.py:6:6:6:13 | ControlFlowNode for __file__ |
-| baz | baz_attr | baz.py:4:12:4:21 | ControlFlowNode for Str |
-| block_flow_check | SOURCE | block_flow_check.py:4:1:4:21 | ControlFlowNode for ClassExpr |
-| block_flow_check | __file__ | block_flow_check.py:2:7:2:14 | ControlFlowNode for __file__ |
-| block_flow_check | __file__ | block_flow_check.py:14:6:14:13 | ControlFlowNode for __file__ |
-| foo | __file__ | foo.py:2:7:2:14 | ControlFlowNode for __file__ |
-| foo | __file__ | foo.py:14:6:14:13 | ControlFlowNode for __file__ |
-| foo | __private_foo_attr | foo.py:8:22:8:41 | ControlFlowNode for Str |
+| attr_clash.__init__ | __name__ | attr_clash/__init__.py:0:0:0:0 | GSSA Variable __name__ |
+| attr_clash.__init__ | __package__ | attr_clash/__init__.py:0:0:0:0 | GSSA Variable __package__ |
+| attr_clash.__init__ | clashing_attr | attr_clash/__init__.py:4:1:4:13 | GSSA Variable clashing_attr |
+| attr_clash.__init__ | enter | attr_clash/__init__.py:2:1:2:5 | ControlFlowNode for enter |
+| attr_clash.__init__ | exit | attr_clash/__init__.py:6:1:6:4 | ControlFlowNode for exit |
+| attr_clash.clashing_attr | __name__ | attr_clash/clashing_attr.py:0:0:0:0 | GSSA Variable __name__ |
+| attr_clash.clashing_attr | __package__ | attr_clash/clashing_attr.py:0:0:0:0 | GSSA Variable __package__ |
+| attr_clash.clashing_attr | enter | attr_clash/clashing_attr.py:2:1:2:5 | ControlFlowNode for enter |
+| attr_clash.clashing_attr | exit | attr_clash/clashing_attr.py:4:1:4:4 | ControlFlowNode for exit |
+| attr_clash.non_clashing_submodule | __name__ | attr_clash/non_clashing_submodule.py:0:0:0:0 | GSSA Variable __name__ |
+| attr_clash.non_clashing_submodule | __package__ | attr_clash/non_clashing_submodule.py:0:0:0:0 | GSSA Variable __package__ |
+| attr_clash.non_clashing_submodule | enter | attr_clash/non_clashing_submodule.py:2:1:2:5 | ControlFlowNode for enter |
+| attr_clash.non_clashing_submodule | exit | attr_clash/non_clashing_submodule.py:4:1:4:4 | ControlFlowNode for exit |
+| bar | __name__ | bar.py:0:0:0:0 | GSSA Variable __name__ |
+| bar | __package__ | bar.py:0:0:0:0 | GSSA Variable __package__ |
+| bar | bar_attr | bar.py:4:1:4:8 | GSSA Variable bar_attr |
+| bar | enter | bar.py:2:1:2:5 | ControlFlowNode for enter |
+| bar | exit | bar.py:6:1:6:4 | ControlFlowNode for exit |
+| baz | __name__ | baz.py:0:0:0:0 | GSSA Variable __name__ |
+| baz | __package__ | baz.py:0:0:0:0 | GSSA Variable __package__ |
+| baz | baz_attr | baz.py:4:1:4:8 | GSSA Variable baz_attr |
+| baz | enter | baz.py:2:1:2:5 | ControlFlowNode for enter |
+| baz | exit | baz.py:6:1:6:4 | ControlFlowNode for exit |
+| block_flow_check | SOURCE | block_flow_check.py:12:25:12:30 | ControlFlowNode for SOURCE |
+| block_flow_check | __name__ | block_flow_check.py:0:0:0:0 | GSSA Variable __name__ |
+| block_flow_check | __package__ | block_flow_check.py:0:0:0:0 | GSSA Variable __package__ |
+| block_flow_check | check | block_flow_check.py:12:1:12:5 | ControlFlowNode for check |
+| block_flow_check | enter | block_flow_check.py:2:1:2:5 | ControlFlowNode for enter |
+| block_flow_check | exit | block_flow_check.py:14:1:14:4 | ControlFlowNode for exit |
+| block_flow_check | globals | block_flow_check.py:12:33:12:39 | ControlFlowNode for globals |
+| block_flow_check | object | block_flow_check.py:4:14:4:19 | ControlFlowNode for object |
+| block_flow_check | staticmethod | block_flow_check.py:0:0:0:0 | GSSA Variable staticmethod |
+| foo | __name__ | foo.py:0:0:0:0 | GSSA Variable __name__ |
+| foo | __package__ | foo.py:0:0:0:0 | GSSA Variable __package__ |
+| foo | __private_foo_attr | foo.py:8:1:8:18 | GSSA Variable __private_foo_attr |
| foo | bar_reexported | foo.py:11:8:11:10 | ControlFlowNode for ImportExpr |
-| foo | foo_attr | foo.py:5:12:5:21 | ControlFlowNode for Str |
-| has_defined_all | __all__ | has_defined_all.py:7:11:7:29 | ControlFlowNode for List |
-| has_defined_all | __file__ | has_defined_all.py:2:7:2:14 | ControlFlowNode for __file__ |
-| has_defined_all | __file__ | has_defined_all.py:9:6:9:13 | ControlFlowNode for __file__ |
-| has_defined_all | all_defined_bar | has_defined_all.py:5:19:5:35 | ControlFlowNode for Str |
-| has_defined_all | all_defined_foo | has_defined_all.py:4:19:4:35 | ControlFlowNode for Str |
-| has_defined_all_copy | __all__ | has_defined_all_copy.py:9:11:9:34 | ControlFlowNode for List |
-| has_defined_all_copy | __file__ | has_defined_all_copy.py:4:7:4:14 | ControlFlowNode for __file__ |
-| has_defined_all_copy | __file__ | has_defined_all_copy.py:11:6:11:13 | ControlFlowNode for __file__ |
-| has_defined_all_copy | all_defined_bar_copy | has_defined_all_copy.py:7:24:7:45 | ControlFlowNode for Str |
-| has_defined_all_copy | all_defined_foo_copy | has_defined_all_copy.py:6:24:6:45 | ControlFlowNode for Str |
-| has_defined_all_indirection | __file__ | has_defined_all_indirection.py:2:7:2:14 | ControlFlowNode for __file__ |
-| has_defined_all_indirection | __file__ | has_defined_all_indirection.py:6:6:6:13 | ControlFlowNode for __file__ |
-| has_defined_all_indirection | all_defined_foo_copy | has_defined_all_copy.py:6:24:6:45 | ControlFlowNode for Str |
-| if_then_else | __file__ | if_then_else.py:2:7:2:14 | ControlFlowNode for __file__ |
-| if_then_else | __file__ | if_then_else.py:16:6:16:13 | ControlFlowNode for __file__ |
-| if_then_else | if_then_else_defined | if_then_else.py:7:28:7:39 | ControlFlowNode for Str |
-| if_then_else | if_then_else_defined | if_then_else.py:12:32:12:47 | ControlFlowNode for Str |
-| if_then_else | if_then_else_defined | if_then_else.py:14:32:14:47 | ControlFlowNode for Str |
-| if_then_else_refined | SOURCE | if_then_else_refined.py:6:1:6:15 | ControlFlowNode for ClassExpr |
-| if_then_else_refined | __file__ | if_then_else_refined.py:4:7:4:14 | ControlFlowNode for __file__ |
-| if_then_else_refined | __file__ | if_then_else_refined.py:19:6:19:13 | ControlFlowNode for __file__ |
-| if_then_else_refined | src | if_then_else_refined.py:11:11:11:16 | ControlFlowNode for SOURCE |
-| if_then_else_refined | src | if_then_else_refined.py:13:11:13:16 | ControlFlowNode for SOURCE |
-| namespace_package.namespace_module | __file__ | namespace_package/namespace_module.py:2:7:2:14 | ControlFlowNode for __file__ |
-| namespace_package.namespace_module | __file__ | namespace_package/namespace_module.py:6:6:6:13 | ControlFlowNode for __file__ |
-| namespace_package.namespace_module | namespace_module_attr | namespace_package/namespace_module.py:4:25:4:47 | ControlFlowNode for Str |
-| package.__init__ | __file__ | package/__init__.py:2:7:2:14 | ControlFlowNode for __file__ |
-| package.__init__ | __file__ | package/__init__.py:7:6:7:13 | ControlFlowNode for __file__ |
-| package.__init__ | attr_used_in_subpackage | package/__init__.py:4:27:4:51 | ControlFlowNode for Str |
-| package.__init__ | package_attr | package/__init__.py:5:16:5:29 | ControlFlowNode for Str |
-| package.subpackage2.__init__ | __file__ | package/subpackage2/__init__.py:2:7:2:14 | ControlFlowNode for __file__ |
-| package.subpackage2.__init__ | __file__ | package/subpackage2/__init__.py:6:6:6:13 | ControlFlowNode for __file__ |
-| package.subpackage2.__init__ | subpackage2_attr | package/subpackage2/__init__.py:4:20:4:37 | ControlFlowNode for Str |
-| package.subpackage.__init__ | __file__ | package/subpackage/__init__.py:2:7:2:14 | ControlFlowNode for __file__ |
-| package.subpackage.__init__ | __file__ | package/subpackage/__init__.py:14:6:14:13 | ControlFlowNode for __file__ |
+| foo | bar_reexported | foo.py:12:34:12:47 | ControlFlowNode for bar_reexported |
+| foo | check | foo.py:12:1:12:5 | ControlFlowNode for check |
+| foo | enter | foo.py:2:1:2:5 | ControlFlowNode for enter |
+| foo | exit | foo.py:14:1:14:4 | ControlFlowNode for exit |
+| foo | foo_attr | foo.py:5:1:5:8 | GSSA Variable foo_attr |
+| foo | globals | foo.py:12:71:12:77 | ControlFlowNode for globals |
+| has_defined_all | __all__ | has_defined_all.py:7:1:7:7 | GSSA Variable __all__ |
+| has_defined_all | __name__ | has_defined_all.py:0:0:0:0 | GSSA Variable __name__ |
+| has_defined_all | __package__ | has_defined_all.py:0:0:0:0 | GSSA Variable __package__ |
+| has_defined_all | all_defined_bar | has_defined_all.py:5:1:5:15 | GSSA Variable all_defined_bar |
+| has_defined_all | all_defined_foo | has_defined_all.py:4:1:4:15 | GSSA Variable all_defined_foo |
+| has_defined_all | enter | has_defined_all.py:2:1:2:5 | ControlFlowNode for enter |
+| has_defined_all | exit | has_defined_all.py:9:1:9:4 | ControlFlowNode for exit |
+| has_defined_all_copy | __all__ | has_defined_all_copy.py:9:1:9:7 | GSSA Variable __all__ |
+| has_defined_all_copy | __name__ | has_defined_all_copy.py:0:0:0:0 | GSSA Variable __name__ |
+| has_defined_all_copy | __package__ | has_defined_all_copy.py:0:0:0:0 | GSSA Variable __package__ |
+| has_defined_all_copy | all_defined_bar_copy | has_defined_all_copy.py:7:1:7:20 | GSSA Variable all_defined_bar_copy |
+| has_defined_all_copy | all_defined_foo_copy | has_defined_all_copy.py:6:1:6:20 | GSSA Variable all_defined_foo_copy |
+| has_defined_all_copy | enter | has_defined_all_copy.py:4:1:4:5 | ControlFlowNode for enter |
+| has_defined_all_copy | exit | has_defined_all_copy.py:11:1:11:4 | ControlFlowNode for exit |
+| has_defined_all_indirection | __name__ | has_defined_all_indirection.py:0:0:0:0 | GSSA Variable __name__ |
+| has_defined_all_indirection | __package__ | has_defined_all_indirection.py:0:0:0:0 | GSSA Variable __package__ |
+| has_defined_all_indirection | all_defined_foo_copy | has_defined_all_copy.py:6:1:6:20 | GSSA Variable all_defined_foo_copy |
+| has_defined_all_indirection | exit | has_defined_all_indirection.py:6:1:6:4 | ControlFlowNode for exit |
+| if_then_else | __name__ | if_then_else.py:0:0:0:0 | GSSA Variable __name__ |
+| if_then_else | __package__ | if_then_else.py:0:0:0:0 | GSSA Variable __package__ |
+| if_then_else | enter | if_then_else.py:2:1:2:5 | ControlFlowNode for enter |
+| if_then_else | eval | if_then_else.py:11:8:11:11 | ControlFlowNode for eval |
+| if_then_else | exit | if_then_else.py:16:1:16:4 | ControlFlowNode for exit |
+| if_then_else | if_then_else_defined | if_then_else.py:7:5:7:24 | GSSA Variable if_then_else_defined |
+| if_then_else | if_then_else_defined | if_then_else.py:12:9:12:28 | GSSA Variable if_then_else_defined |
+| if_then_else | if_then_else_defined | if_then_else.py:14:9:14:28 | GSSA Variable if_then_else_defined |
+| if_then_else_refined | SOURCE | if_then_else_refined.py:11:11:11:16 | ControlFlowNode for SOURCE |
+| if_then_else_refined | SOURCE | if_then_else_refined.py:13:11:13:16 | ControlFlowNode for SOURCE |
+| if_then_else_refined | __name__ | if_then_else_refined.py:0:0:0:0 | GSSA Variable __name__ |
+| if_then_else_refined | __package__ | if_then_else_refined.py:0:0:0:0 | GSSA Variable __package__ |
+| if_then_else_refined | check | if_then_else_refined.py:17:1:17:5 | ControlFlowNode for check |
+| if_then_else_refined | enter | if_then_else_refined.py:4:1:4:5 | ControlFlowNode for enter |
+| if_then_else_refined | eval | if_then_else_refined.py:10:4:10:7 | ControlFlowNode for eval |
+| if_then_else_refined | exit | if_then_else_refined.py:19:1:19:4 | ControlFlowNode for exit |
+| if_then_else_refined | globals | if_then_else_refined.py:17:24:17:30 | ControlFlowNode for globals |
+| if_then_else_refined | src | if_then_else_refined.py:17:19:17:21 | ControlFlowNode for src |
+| namespace_package.namespace_module | __name__ | namespace_package/namespace_module.py:0:0:0:0 | GSSA Variable __name__ |
+| namespace_package.namespace_module | __package__ | namespace_package/namespace_module.py:0:0:0:0 | GSSA Variable __package__ |
+| namespace_package.namespace_module | enter | namespace_package/namespace_module.py:2:1:2:5 | ControlFlowNode for enter |
+| namespace_package.namespace_module | exit | namespace_package/namespace_module.py:6:1:6:4 | ControlFlowNode for exit |
+| namespace_package.namespace_module | namespace_module_attr | namespace_package/namespace_module.py:4:1:4:21 | GSSA Variable namespace_module_attr |
+| package.__init__ | __name__ | package/__init__.py:0:0:0:0 | GSSA Variable __name__ |
+| package.__init__ | __package__ | package/__init__.py:0:0:0:0 | GSSA Variable __package__ |
+| package.__init__ | attr_used_in_subpackage | package/__init__.py:4:1:4:23 | GSSA Variable attr_used_in_subpackage |
+| package.__init__ | enter | package/__init__.py:2:1:2:5 | ControlFlowNode for enter |
+| package.__init__ | exit | package/__init__.py:7:1:7:4 | ControlFlowNode for exit |
+| package.__init__ | package_attr | package/__init__.py:5:1:5:12 | GSSA Variable package_attr |
+| package.subpackage2.__init__ | __name__ | package/subpackage2/__init__.py:0:0:0:0 | GSSA Variable __name__ |
+| package.subpackage2.__init__ | __package__ | package/subpackage2/__init__.py:0:0:0:0 | GSSA Variable __package__ |
+| package.subpackage2.__init__ | enter | package/subpackage2/__init__.py:2:1:2:5 | ControlFlowNode for enter |
+| package.subpackage2.__init__ | exit | package/subpackage2/__init__.py:6:1:6:4 | ControlFlowNode for exit |
+| package.subpackage2.__init__ | subpackage2_attr | package/subpackage2/__init__.py:4:1:4:16 | GSSA Variable subpackage2_attr |
+| package.subpackage.__init__ | __name__ | package/subpackage/__init__.py:0:0:0:0 | GSSA Variable __name__ |
+| package.subpackage.__init__ | __package__ | package/subpackage/__init__.py:0:0:0:0 | GSSA Variable __package__ |
+| package.subpackage.__init__ | check | package/subpackage/__init__.py:12:1:12:5 | ControlFlowNode for check |
+| package.subpackage.__init__ | enter | package/subpackage/__init__.py:2:1:2:5 | ControlFlowNode for enter |
+| package.subpackage.__init__ | exit | package/subpackage/__init__.py:14:1:14:4 | ControlFlowNode for exit |
+| package.subpackage.__init__ | globals | package/subpackage/__init__.py:12:79:12:85 | ControlFlowNode for globals |
| package.subpackage.__init__ | imported_attr | package/subpackage/__init__.py:7:16:7:55 | ControlFlowNode for ImportMember |
+| package.subpackage.__init__ | imported_attr | package/subpackage/__init__.py:8:24:8:36 | ControlFlowNode for imported_attr |
| package.subpackage.__init__ | irrelevant_attr | package/subpackage/__init__.py:11:24:11:38 | ControlFlowNode for ImportMember |
-| package.subpackage.__init__ | subpackage_attr | package/subpackage/__init__.py:4:19:4:35 | ControlFlowNode for Str |
-| package.subpackage.submodule | __file__ | package/subpackage/submodule.py:2:7:2:14 | ControlFlowNode for __file__ |
-| package.subpackage.submodule | __file__ | package/subpackage/submodule.py:7:6:7:13 | ControlFlowNode for __file__ |
-| package.subpackage.submodule | irrelevant_attr | package/subpackage/submodule.py:5:19:5:35 | ControlFlowNode for Str |
-| package.subpackage.submodule | submodule_attr | package/subpackage/submodule.py:4:18:4:33 | ControlFlowNode for Str |
-| refined | SOURCE | refined.py:4:1:4:21 | ControlFlowNode for ClassExpr |
-| refined | __file__ | refined.py:2:7:2:14 | ControlFlowNode for __file__ |
-| refined | __file__ | refined.py:14:6:14:13 | ControlFlowNode for __file__ |
-| simplistic_reexport | __file__ | simplistic_reexport.py:4:7:4:14 | ControlFlowNode for __file__ |
-| simplistic_reexport | __file__ | simplistic_reexport.py:19:6:19:13 | ControlFlowNode for __file__ |
+| package.subpackage.__init__ | irrelevant_attr | package/subpackage/__init__.py:11:24:11:38 | GSSA Variable irrelevant_attr |
+| package.subpackage.__init__ | submodule | package/subpackage/__init__.py:12:35:12:43 | ControlFlowNode for submodule |
+| package.subpackage.__init__ | subpackage_attr | package/subpackage/__init__.py:4:1:4:15 | GSSA Variable subpackage_attr |
+| package.subpackage.submodule | __name__ | package/subpackage/submodule.py:0:0:0:0 | GSSA Variable __name__ |
+| package.subpackage.submodule | __package__ | package/subpackage/submodule.py:0:0:0:0 | GSSA Variable __package__ |
+| package.subpackage.submodule | enter | package/subpackage/submodule.py:2:1:2:5 | ControlFlowNode for enter |
+| package.subpackage.submodule | exit | package/subpackage/submodule.py:7:1:7:4 | ControlFlowNode for exit |
+| package.subpackage.submodule | irrelevant_attr | package/subpackage/submodule.py:5:1:5:15 | GSSA Variable irrelevant_attr |
+| package.subpackage.submodule | submodule_attr | package/subpackage/submodule.py:4:1:4:14 | GSSA Variable submodule_attr |
+| refined | SOURCE | refined.py:12:25:12:30 | ControlFlowNode for SOURCE |
+| refined | __name__ | refined.py:0:0:0:0 | GSSA Variable __name__ |
+| refined | __package__ | refined.py:0:0:0:0 | GSSA Variable __package__ |
+| refined | check | refined.py:12:1:12:5 | ControlFlowNode for check |
+| refined | enter | refined.py:2:1:2:5 | ControlFlowNode for enter |
+| refined | exit | refined.py:14:1:14:4 | ControlFlowNode for exit |
+| refined | globals | refined.py:12:33:12:39 | ControlFlowNode for globals |
+| refined | object | refined.py:4:14:4:19 | ControlFlowNode for object |
+| simplistic_reexport | __name__ | simplistic_reexport.py:0:0:0:0 | GSSA Variable __name__ |
+| simplistic_reexport | __package__ | simplistic_reexport.py:0:0:0:0 | GSSA Variable __package__ |
| simplistic_reexport | bar_attr | simplistic_reexport.py:6:17:6:24 | ControlFlowNode for ImportMember |
-| simplistic_reexport | bar_attr | simplistic_reexport.py:9:12:9:24 | ControlFlowNode for Str |
-| simplistic_reexport | baz_attr | baz.py:4:12:4:21 | ControlFlowNode for Str |
-| simplistic_reexport | baz_attr | simplistic_reexport.py:16:12:16:24 | ControlFlowNode for Str |
+| simplistic_reexport | baz_attr | baz.py:4:1:4:8 | GSSA Variable baz_attr |
+| simplistic_reexport | baz_attr | simplistic_reexport.py:17:19:17:26 | ControlFlowNode for baz_attr |
+| simplistic_reexport | check | simplistic_reexport.py:17:1:17:5 | ControlFlowNode for check |
+| simplistic_reexport | enter | baz.py:2:1:2:5 | ControlFlowNode for enter |
+| simplistic_reexport | exit | baz.py:6:1:6:4 | ControlFlowNode for exit |
+| simplistic_reexport | exit | simplistic_reexport.py:19:1:19:4 | ControlFlowNode for exit |
+| simplistic_reexport | globals | simplistic_reexport.py:17:44:17:50 | ControlFlowNode for globals |
diff --git a/python/ql/test/experimental/import-resolution/main.py b/python/ql/test/experimental/import-resolution/main.py
index 31a4f60d56d..4d868437964 100644
--- a/python/ql/test/experimental/import-resolution/main.py
+++ b/python/ql/test/experimental/import-resolution/main.py
@@ -81,7 +81,7 @@ if sys.version_info[0] == 3:
from attr_clash import clashing_attr, non_clashing_submodule #$ imports=attr_clash.clashing_attr as=clashing_attr imports=attr_clash.non_clashing_submodule as=non_clashing_submodule
-check("clashing_attr", clashing_attr, "clashing_attr", globals()) #$ prints=clashing_attr SPURIOUS: prints=""
+check("clashing_attr", clashing_attr, "clashing_attr", globals()) #$ prints=clashing_attr SPURIOUS: prints="" SPURIOUS: prints="