From 9b7df354e63b6249ec843fad3368456bda3233d1 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Thu, 29 Sep 2022 09:41:28 -0400
Subject: [PATCH 001/249] move files
---
.../Security/CWE/CWE-326/InsufficientKeySize.java | 0
.../Security/CWE/CWE-326/InsufficientKeySize.qhelp | 10 +++++-----
.../Security/CWE/CWE-326/InsufficientKeySize.ql | 0
.../security/CWE-326/InsufficientKeySize.qlref | 1 -
.../security/CWE-326/InsufficientKeySize.expected | 0
.../security/CWE-326/InsufficientKeySize.java | 0
.../security/CWE-326/InsufficientKeySize.qlref | 1 +
7 files changed, 6 insertions(+), 6 deletions(-)
rename java/ql/src/{experimental => }/Security/CWE/CWE-326/InsufficientKeySize.java (100%)
rename java/ql/src/{experimental => }/Security/CWE/CWE-326/InsufficientKeySize.qhelp (91%)
rename java/ql/src/{experimental => }/Security/CWE/CWE-326/InsufficientKeySize.ql (100%)
delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-326/InsufficientKeySize.qlref
rename java/ql/test/{experimental => }/query-tests/security/CWE-326/InsufficientKeySize.expected (100%)
rename java/ql/test/{experimental => }/query-tests/security/CWE-326/InsufficientKeySize.java (100%)
create mode 100644 java/ql/test/query-tests/security/CWE-326/InsufficientKeySize.qlref
diff --git a/java/ql/src/experimental/Security/CWE/CWE-326/InsufficientKeySize.java b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.java
similarity index 100%
rename from java/ql/src/experimental/Security/CWE/CWE-326/InsufficientKeySize.java
rename to java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.java
diff --git a/java/ql/src/experimental/Security/CWE/CWE-326/InsufficientKeySize.qhelp b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.qhelp
similarity index 91%
rename from java/ql/src/experimental/Security/CWE/CWE-326/InsufficientKeySize.qhelp
rename to java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.qhelp
index 4d4ec76f060..4e5faa0ddf4 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-326/InsufficientKeySize.qhelp
+++ b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.qhelp
@@ -1,15 +1,15 @@
- This rule finds uses of encryption algorithms with too small a key size. Encryption algorithms
+
This rule finds uses of encryption algorithms with too small a key size. Encryption algorithms
are vulnerable to brute force attack when too small a key size is used.
- The key should be at least 2048 bits long when using RSA and DSA encryption, 256 bits long when using EC encryption, and 128 bits long when using
+
The key should be at least 2048 bits long when using RSA and DSA encryption, 256 bits long when using EC encryption, and 128 bits long when using
symmetric encryption.
-
+
@@ -24,6 +24,6 @@ symmetric encryption.
CWE.
CWE-326: Inadequate Encryption Strength
-
+
-
\ No newline at end of file
+
diff --git a/java/ql/src/experimental/Security/CWE/CWE-326/InsufficientKeySize.ql b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
similarity index 100%
rename from java/ql/src/experimental/Security/CWE/CWE-326/InsufficientKeySize.ql
rename to java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
diff --git a/java/ql/test/experimental/query-tests/security/CWE-326/InsufficientKeySize.qlref b/java/ql/test/experimental/query-tests/security/CWE-326/InsufficientKeySize.qlref
deleted file mode 100644
index 2b35cd6921e..00000000000
--- a/java/ql/test/experimental/query-tests/security/CWE-326/InsufficientKeySize.qlref
+++ /dev/null
@@ -1 +0,0 @@
-experimental/Security/CWE/CWE-326/InsufficientKeySize.ql
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-326/InsufficientKeySize.expected b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySize.expected
similarity index 100%
rename from java/ql/test/experimental/query-tests/security/CWE-326/InsufficientKeySize.expected
rename to java/ql/test/query-tests/security/CWE-326/InsufficientKeySize.expected
diff --git a/java/ql/test/experimental/query-tests/security/CWE-326/InsufficientKeySize.java b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySize.java
similarity index 100%
rename from java/ql/test/experimental/query-tests/security/CWE-326/InsufficientKeySize.java
rename to java/ql/test/query-tests/security/CWE-326/InsufficientKeySize.java
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySize.qlref b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySize.qlref
new file mode 100644
index 00000000000..7831abd3b6b
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySize.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-326/InsufficientKeySize.ql
From 3643c9e658b0b0ce69de826192f1ba8cc03e18cb Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Thu, 29 Sep 2022 14:09:39 -0400
Subject: [PATCH 002/249] update metadata
---
.../src/Security/CWE/CWE-326/InsufficientKeySize.ql | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
index 4510bbbc869..5607ec1bb87 100644
--- a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
+++ b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
@@ -1,9 +1,10 @@
/**
- * @name Weak encryption: Insufficient key size
- * @description Finds uses of encryption algorithms with too small a key size
- * @kind problem
- * @problem.severity warning
- * @precision medium
+ * @name Use of a cryptographic algorithm with insufficient key size
+ * @description Using cryptographic algorithms with too small a key size can
+ * allow an attacker to compromise security.
+ * @kind path-problem
+ * @problem.severity error
+ * @precision high
* @id java/insufficient-key-size
* @tags security
* external/cwe/cwe-326
From 657e1e62ca07932f7ae50d06ee3350af9ed0d5f4 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Thu, 29 Sep 2022 15:25:08 -0400
Subject: [PATCH 003/249] start refactoring query logic into lib file
---
.../semmle/code/java/security/Encryption.qll | 4 +-
.../security/InsufficientKeySizeQuery.qll | 145 ++++++++++++++++++
.../CWE/CWE-326/InsufficientKeySize.ql | 136 +---------------
.../CWE-326/InsufficientKeySize.qlref | 2 +-
4 files changed, 151 insertions(+), 136 deletions(-)
create mode 100644 java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
diff --git a/java/ql/lib/semmle/code/java/security/Encryption.qll b/java/ql/lib/semmle/code/java/security/Encryption.qll
index 39e3f2d2110..9659fb92843 100644
--- a/java/ql/lib/semmle/code/java/security/Encryption.qll
+++ b/java/ql/lib/semmle/code/java/security/Encryption.qll
@@ -249,7 +249,9 @@ string getASecureAlgorithmName() {
result =
[
"RSA", "SHA256", "SHA512", "CCM", "GCM", "AES(?)",
- "Blowfish", "ECIES"
+ "Blowfish", "ECIES" // ! Blowfish not actually secure based on https://rules.sonarsource.com/java/type/Vulnerability/RSPEC-4426 ??
+ // ! hmm, other sources imply that it is secure...
+ // ! also no DH here, etc.?
]
}
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
new file mode 100644
index 00000000000..5e883eca216
--- /dev/null
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
@@ -0,0 +1,145 @@
+import semmle.code.java.security.Encryption
+import semmle.code.java.dataflow.TaintTracking
+
+/** The Java class `java.security.spec.ECGenParameterSpec`. */
+class ECGenParameterSpec extends RefType {
+ ECGenParameterSpec() { this.hasQualifiedName("java.security.spec", "ECGenParameterSpec") }
+}
+
+/** The `init` method declared in `javax.crypto.KeyGenerator`. */
+class KeyGeneratorInitMethod extends Method {
+ KeyGeneratorInitMethod() {
+ this.getDeclaringType() instanceof KeyGenerator and
+ this.hasName("init")
+ }
+}
+
+/** The `initialize` method declared in `java.security.KeyPairGenerator`. */
+class KeyPairGeneratorInitMethod extends Method {
+ KeyPairGeneratorInitMethod() {
+ this.getDeclaringType() instanceof KeyPairGenerator and
+ this.hasName("initialize")
+ }
+}
+
+/** Returns the key size in the EC algorithm string */
+bindingset[algorithm]
+int getECKeySize(string algorithm) {
+ algorithm.matches("sec%") and // specification such as "secp256r1"
+ result = algorithm.regexpCapture("sec[p|t](\\d+)[a-zA-Z].*", 1).toInt()
+ or
+ algorithm.matches("X9.62%") and //specification such as "X9.62 prime192v2"
+ result = algorithm.regexpCapture("X9\\.62 .*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
+ or
+ (algorithm.matches("prime%") or algorithm.matches("c2tnb%")) and //specification such as "prime192v2"
+ result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
+}
+
+/** Taint configuration tracking flow from a key generator to a `init` method call. */
+class KeyGeneratorInitConfiguration extends TaintTracking::Configuration {
+ KeyGeneratorInitConfiguration() { this = "KeyGeneratorInitConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) {
+ source.asExpr() instanceof JavaxCryptoKeyGenerator
+ }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(MethodAccess ma |
+ ma.getMethod() instanceof KeyGeneratorInitMethod and
+ sink.asExpr() = ma.getQualifier()
+ )
+ }
+}
+
+/** Taint configuration tracking flow from a keypair generator to a `initialize` method call. */
+class KeyPairGeneratorInitConfiguration extends TaintTracking::Configuration {
+ KeyPairGeneratorInitConfiguration() { this = "KeyPairGeneratorInitConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) {
+ source.asExpr() instanceof JavaSecurityKeyPairGenerator
+ }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(MethodAccess ma |
+ ma.getMethod() instanceof KeyPairGeneratorInitMethod and
+ sink.asExpr() = ma.getQualifier()
+ )
+ }
+}
+
+/** Holds if a symmetric `KeyGenerator` implementing encryption algorithm `type` and initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
+bindingset[type]
+predicate hasShortSymmetricKey(MethodAccess ma, string msg, string type) {
+ ma.getMethod() instanceof KeyGeneratorInitMethod and
+ exists(
+ JavaxCryptoKeyGenerator jcg, KeyGeneratorInitConfiguration cc, DataFlow::PathNode source,
+ DataFlow::PathNode dest
+ |
+ jcg.getAlgoSpec().(StringLiteral).getValue() = type and
+ source.getNode().asExpr() = jcg and
+ dest.getNode().asExpr() = ma.getQualifier() and
+ cc.hasFlowPath(source, dest)
+ ) and
+ ma.getArgument(0).(IntegerLiteral).getIntValue() < 128 and
+ msg = "Key size should be at least 128 bits for " + type + " encryption."
+}
+
+/** Holds if an AES `KeyGenerator` initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
+predicate hasShortAESKey(MethodAccess ma, string msg) { hasShortSymmetricKey(ma, msg, "AES") }
+
+/** Holds if an asymmetric `KeyPairGenerator` implementing encryption algorithm `type` and initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
+bindingset[type]
+predicate hasShortAsymmetricKeyPair(MethodAccess ma, string msg, string type) {
+ ma.getMethod() instanceof KeyPairGeneratorInitMethod and
+ exists(
+ JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kc,
+ DataFlow::PathNode source, DataFlow::PathNode dest
+ |
+ jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase() = type and
+ source.getNode().asExpr() = jpg and
+ dest.getNode().asExpr() = ma.getQualifier() and
+ kc.hasFlowPath(source, dest)
+ ) and
+ ma.getArgument(0).(IntegerLiteral).getIntValue() < 2048 and
+ msg = "Key size should be at least 2048 bits for " + type + " encryption."
+}
+
+/** Holds if a DSA `KeyPairGenerator` initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
+predicate hasShortDsaKeyPair(MethodAccess ma, string msg) {
+ hasShortAsymmetricKeyPair(ma, msg, "DSA") or hasShortAsymmetricKeyPair(ma, msg, "DH")
+}
+
+/** Holds if a RSA `KeyPairGenerator` initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
+predicate hasShortRsaKeyPair(MethodAccess ma, string msg) {
+ hasShortAsymmetricKeyPair(ma, msg, "RSA")
+}
+
+/** Holds if an EC `KeyPairGenerator` initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
+predicate hasShortECKeyPair(MethodAccess ma, string msg) {
+ ma.getMethod() instanceof KeyPairGeneratorInitMethod and
+ exists(
+ JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kc,
+ DataFlow::PathNode source, DataFlow::PathNode dest, ClassInstanceExpr cie
+ |
+ jpg.getAlgoSpec().(StringLiteral).getValue().matches("EC%") and // ECC variants such as ECDH and ECDSA
+ source.getNode().asExpr() = jpg and
+ dest.getNode().asExpr() = ma.getQualifier() and
+ kc.hasFlowPath(source, dest) and
+ DataFlow::localExprFlow(cie, ma.getArgument(0)) and
+ ma.getArgument(0).getType() instanceof ECGenParameterSpec and
+ getECKeySize(cie.getArgument(0).(StringLiteral).getValue()) < 256
+ ) and
+ msg = "Key size should be at least 256 bits for EC encryption."
+}
+// ! refactor to something like the below,
+// ! need to adjust select clause then...
+// ! see C# and C++ queries for ideas
+// class EncryptionAlgorithm extends
+// predicate hasInsufficientKeySize() {
+// exists(Expr e, string msg |
+// hasShortAESKey(e, msg) or
+// hasShortDsaKeyPair(e, msg) or
+// hasShortRsaKeyPair(e, msg) or
+// hasShortECKeyPair(e, msg)
+// )
+// }
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
index 5607ec1bb87..baccf3b42c3 100644
--- a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
+++ b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
@@ -2,7 +2,7 @@
* @name Use of a cryptographic algorithm with insufficient key size
* @description Using cryptographic algorithms with too small a key size can
* allow an attacker to compromise security.
- * @kind path-problem
+ * @kind problem
* @problem.severity error
* @precision high
* @id java/insufficient-key-size
@@ -11,139 +11,7 @@
*/
import java
-import semmle.code.java.security.Encryption
-import semmle.code.java.dataflow.TaintTracking
-
-/** The Java class `java.security.spec.ECGenParameterSpec`. */
-class ECGenParameterSpec extends RefType {
- ECGenParameterSpec() { this.hasQualifiedName("java.security.spec", "ECGenParameterSpec") }
-}
-
-/** The `init` method declared in `javax.crypto.KeyGenerator`. */
-class KeyGeneratorInitMethod extends Method {
- KeyGeneratorInitMethod() {
- this.getDeclaringType() instanceof KeyGenerator and
- this.hasName("init")
- }
-}
-
-/** The `initialize` method declared in `java.security.KeyPairGenerator`. */
-class KeyPairGeneratorInitMethod extends Method {
- KeyPairGeneratorInitMethod() {
- this.getDeclaringType() instanceof KeyPairGenerator and
- this.hasName("initialize")
- }
-}
-
-/** Returns the key size in the EC algorithm string */
-bindingset[algorithm]
-int getECKeySize(string algorithm) {
- algorithm.matches("sec%") and // specification such as "secp256r1"
- result = algorithm.regexpCapture("sec[p|t](\\d+)[a-zA-Z].*", 1).toInt()
- or
- algorithm.matches("X9.62%") and //specification such as "X9.62 prime192v2"
- result = algorithm.regexpCapture("X9\\.62 .*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
- or
- (algorithm.matches("prime%") or algorithm.matches("c2tnb%")) and //specification such as "prime192v2"
- result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
-}
-
-/** Taint configuration tracking flow from a key generator to a `init` method call. */
-class KeyGeneratorInitConfiguration extends TaintTracking::Configuration {
- KeyGeneratorInitConfiguration() { this = "KeyGeneratorInitConfiguration" }
-
- override predicate isSource(DataFlow::Node source) {
- source.asExpr() instanceof JavaxCryptoKeyGenerator
- }
-
- override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma |
- ma.getMethod() instanceof KeyGeneratorInitMethod and
- sink.asExpr() = ma.getQualifier()
- )
- }
-}
-
-/** Taint configuration tracking flow from a keypair generator to a `initialize` method call. */
-class KeyPairGeneratorInitConfiguration extends TaintTracking::Configuration {
- KeyPairGeneratorInitConfiguration() { this = "KeyPairGeneratorInitConfiguration" }
-
- override predicate isSource(DataFlow::Node source) {
- source.asExpr() instanceof JavaSecurityKeyPairGenerator
- }
-
- override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma |
- ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- sink.asExpr() = ma.getQualifier()
- )
- }
-}
-
-/** Holds if a symmetric `KeyGenerator` implementing encryption algorithm `type` and initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
-bindingset[type]
-predicate hasShortSymmetricKey(MethodAccess ma, string msg, string type) {
- ma.getMethod() instanceof KeyGeneratorInitMethod and
- exists(
- JavaxCryptoKeyGenerator jcg, KeyGeneratorInitConfiguration cc, DataFlow::PathNode source,
- DataFlow::PathNode dest
- |
- jcg.getAlgoSpec().(StringLiteral).getValue() = type and
- source.getNode().asExpr() = jcg and
- dest.getNode().asExpr() = ma.getQualifier() and
- cc.hasFlowPath(source, dest)
- ) and
- ma.getArgument(0).(IntegerLiteral).getIntValue() < 128 and
- msg = "Key size should be at least 128 bits for " + type + " encryption."
-}
-
-/** Holds if an AES `KeyGenerator` initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
-predicate hasShortAESKey(MethodAccess ma, string msg) { hasShortSymmetricKey(ma, msg, "AES") }
-
-/** Holds if an asymmetric `KeyPairGenerator` implementing encryption algorithm `type` and initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
-bindingset[type]
-predicate hasShortAsymmetricKeyPair(MethodAccess ma, string msg, string type) {
- ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- exists(
- JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kc,
- DataFlow::PathNode source, DataFlow::PathNode dest
- |
- jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase() = type and
- source.getNode().asExpr() = jpg and
- dest.getNode().asExpr() = ma.getQualifier() and
- kc.hasFlowPath(source, dest)
- ) and
- ma.getArgument(0).(IntegerLiteral).getIntValue() < 2048 and
- msg = "Key size should be at least 2048 bits for " + type + " encryption."
-}
-
-/** Holds if a DSA `KeyPairGenerator` initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
-predicate hasShortDsaKeyPair(MethodAccess ma, string msg) {
- hasShortAsymmetricKeyPair(ma, msg, "DSA") or hasShortAsymmetricKeyPair(ma, msg, "DH")
-}
-
-/** Holds if a RSA `KeyPairGenerator` initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
-predicate hasShortRsaKeyPair(MethodAccess ma, string msg) {
- hasShortAsymmetricKeyPair(ma, msg, "RSA")
-}
-
-/** Holds if an EC `KeyPairGenerator` initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
-predicate hasShortECKeyPair(MethodAccess ma, string msg) {
- ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- exists(
- JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kc,
- DataFlow::PathNode source, DataFlow::PathNode dest, ClassInstanceExpr cie
- |
- jpg.getAlgoSpec().(StringLiteral).getValue().matches("EC%") and // ECC variants such as ECDH and ECDSA
- source.getNode().asExpr() = jpg and
- dest.getNode().asExpr() = ma.getQualifier() and
- kc.hasFlowPath(source, dest) and
- DataFlow::localExprFlow(cie, ma.getArgument(0)) and
- ma.getArgument(0).getType() instanceof ECGenParameterSpec and
- getECKeySize(cie.getArgument(0).(StringLiteral).getValue()) < 256
- ) and
- msg = "Key size should be at least 256 bits for EC encryption."
-}
+import semmle.code.java.security.InsufficientKeySizeQuery
from Expr e, string msg
where
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySize.qlref b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySize.qlref
index 7831abd3b6b..040c083a1a1 100644
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySize.qlref
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySize.qlref
@@ -1 +1 @@
-experimental/Security/CWE/CWE-326/InsufficientKeySize.ql
+Security/CWE/CWE-326/InsufficientKeySize.ql
From 9eb45c378731c15f58315a01d3c799da04e96c73 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Mon, 3 Oct 2022 15:39:09 -0400
Subject: [PATCH 004/249] refactor tests and code, update help file
---
.../security/InsufficientKeySizeQuery.qll | 91 ++++++++++++-------
.../CWE/CWE-326/InsufficientKeySize.qhelp | 75 ++++++++++++---
.../CWE/CWE-326/InsufficientKeySize.ql | 10 +-
.../CWE/CWE-326/InsufficientKeySizeBad.java | 16 ++++
.../CWE/CWE-326/InsufficientKeySizeGood.java | 16 ++++
...Size.java => InsufficientKeySize_OLD.java} | 0
.../CWE-326/InsufficientKeySize.qlref | 1 -
.../CWE-326/InsufficientKeySizeTest.expected | 0
...Size.java => InsufficientKeySizeTest.java} | 44 ++++-----
.../CWE-326/InsufficientKeySizeTest.ql | 18 ++++
...xpected => InsufficientKeySizeTestOLD.txt} | 0
11 files changed, 196 insertions(+), 75 deletions(-)
create mode 100644 java/ql/src/Security/CWE/CWE-326/InsufficientKeySizeBad.java
create mode 100644 java/ql/src/Security/CWE/CWE-326/InsufficientKeySizeGood.java
rename java/ql/src/Security/CWE/CWE-326/{InsufficientKeySize.java => InsufficientKeySize_OLD.java} (100%)
delete mode 100644 java/ql/test/query-tests/security/CWE-326/InsufficientKeySize.qlref
create mode 100644 java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.expected
rename java/ql/test/query-tests/security/CWE-326/{InsufficientKeySize.java => InsufficientKeySizeTest.java} (71%)
create mode 100644 java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
rename java/ql/test/query-tests/security/CWE-326/{InsufficientKeySize.expected => InsufficientKeySizeTestOLD.txt} (100%)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
index 5e883eca216..eaba8977c23 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
@@ -2,12 +2,12 @@ import semmle.code.java.security.Encryption
import semmle.code.java.dataflow.TaintTracking
/** The Java class `java.security.spec.ECGenParameterSpec`. */
-class ECGenParameterSpec extends RefType {
+private class ECGenParameterSpec extends RefType {
ECGenParameterSpec() { this.hasQualifiedName("java.security.spec", "ECGenParameterSpec") }
}
/** The `init` method declared in `javax.crypto.KeyGenerator`. */
-class KeyGeneratorInitMethod extends Method {
+private class KeyGeneratorInitMethod extends Method {
KeyGeneratorInitMethod() {
this.getDeclaringType() instanceof KeyGenerator and
this.hasName("init")
@@ -15,7 +15,7 @@ class KeyGeneratorInitMethod extends Method {
}
/** The `initialize` method declared in `java.security.KeyPairGenerator`. */
-class KeyPairGeneratorInitMethod extends Method {
+private class KeyPairGeneratorInitMethod extends Method {
KeyPairGeneratorInitMethod() {
this.getDeclaringType() instanceof KeyPairGenerator and
this.hasName("initialize")
@@ -24,7 +24,7 @@ class KeyPairGeneratorInitMethod extends Method {
/** Returns the key size in the EC algorithm string */
bindingset[algorithm]
-int getECKeySize(string algorithm) {
+private int getECKeySize(string algorithm) {
algorithm.matches("sec%") and // specification such as "secp256r1"
result = algorithm.regexpCapture("sec[p|t](\\d+)[a-zA-Z].*", 1).toInt()
or
@@ -36,7 +36,7 @@ int getECKeySize(string algorithm) {
}
/** Taint configuration tracking flow from a key generator to a `init` method call. */
-class KeyGeneratorInitConfiguration extends TaintTracking::Configuration {
+private class KeyGeneratorInitConfiguration extends TaintTracking::Configuration {
KeyGeneratorInitConfiguration() { this = "KeyGeneratorInitConfiguration" }
override predicate isSource(DataFlow::Node source) {
@@ -51,8 +51,11 @@ class KeyGeneratorInitConfiguration extends TaintTracking::Configuration {
}
}
-/** Taint configuration tracking flow from a keypair generator to a `initialize` method call. */
-class KeyPairGeneratorInitConfiguration extends TaintTracking::Configuration {
+/**
+ * Taint configuration tracking flow from a keypair generator to
+ * an `initialize` method call.
+ */
+private class KeyPairGeneratorInitConfiguration extends TaintTracking::Configuration {
KeyPairGeneratorInitConfiguration() { this = "KeyPairGeneratorInitConfiguration" }
override predicate isSource(DataFlow::Node source) {
@@ -67,9 +70,14 @@ class KeyPairGeneratorInitConfiguration extends TaintTracking::Configuration {
}
}
-/** Holds if a symmetric `KeyGenerator` implementing encryption algorithm `type` and initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
+/**
+ * Holds if a symmetric `KeyGenerator` implementing encryption algorithm
+ * `type` and initialized by `ma` uses an insufficient key size.
+ *
+ * `msg` provides a human-readable description of the problem.
+ */
bindingset[type]
-predicate hasShortSymmetricKey(MethodAccess ma, string msg, string type) {
+private predicate hasShortSymmetricKey(MethodAccess ma, string msg, string type) {
ma.getMethod() instanceof KeyGeneratorInitMethod and
exists(
JavaxCryptoKeyGenerator jcg, KeyGeneratorInitConfiguration cc, DataFlow::PathNode source,
@@ -84,12 +92,22 @@ predicate hasShortSymmetricKey(MethodAccess ma, string msg, string type) {
msg = "Key size should be at least 128 bits for " + type + " encryption."
}
-/** Holds if an AES `KeyGenerator` initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
-predicate hasShortAESKey(MethodAccess ma, string msg) { hasShortSymmetricKey(ma, msg, "AES") }
+/**
+ * Holds if an AES `KeyGenerator` initialized by `ma` uses an insufficient key size.
+ * `msg` provides a human-readable description of the problem.
+ */
+private predicate hasShortAESKey(MethodAccess ma, string msg) {
+ hasShortSymmetricKey(ma, msg, "AES")
+}
-/** Holds if an asymmetric `KeyPairGenerator` implementing encryption algorithm `type` and initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
+/**
+ * Holds if an asymmetric `KeyPairGenerator` implementing encryption algorithm
+ * `type` and initialized by `ma` uses an insufficient key size.
+ *
+ * `msg` provides a human-readable description of the problem.
+ */
bindingset[type]
-predicate hasShortAsymmetricKeyPair(MethodAccess ma, string msg, string type) {
+private predicate hasShortAsymmetricKeyPair(MethodAccess ma, string msg, string type) {
ma.getMethod() instanceof KeyPairGeneratorInitMethod and
exists(
JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kc,
@@ -104,18 +122,31 @@ predicate hasShortAsymmetricKeyPair(MethodAccess ma, string msg, string type) {
msg = "Key size should be at least 2048 bits for " + type + " encryption."
}
-/** Holds if a DSA `KeyPairGenerator` initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
-predicate hasShortDsaKeyPair(MethodAccess ma, string msg) {
- hasShortAsymmetricKeyPair(ma, msg, "DSA") or hasShortAsymmetricKeyPair(ma, msg, "DH")
+/**
+ * Holds if a DSA `KeyPairGenerator` initialized by `ma` uses an insufficient key size.
+ *
+ * `msg` provides a human-readable description of the problem.
+ */
+private predicate hasShortDsaKeyPair(MethodAccess ma, string msg) {
+ hasShortAsymmetricKeyPair(ma, msg, "DSA") or
+ hasShortAsymmetricKeyPair(ma, msg, "DH")
}
-/** Holds if a RSA `KeyPairGenerator` initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
-predicate hasShortRsaKeyPair(MethodAccess ma, string msg) {
+/**
+ * Holds if a RSA `KeyPairGenerator` initialized by `ma` uses an insufficient key size.
+ *
+ * `msg` provides a human-readable description of the problem.
+ */
+private predicate hasShortRsaKeyPair(MethodAccess ma, string msg) {
hasShortAsymmetricKeyPair(ma, msg, "RSA")
}
-/** Holds if an EC `KeyPairGenerator` initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
-predicate hasShortECKeyPair(MethodAccess ma, string msg) {
+/**
+ * Holds if an EC `KeyPairGenerator` initialized by `ma` uses an insufficient key size.
+ *
+ * `msg` provides a human-readable description of the problem.
+ */
+private predicate hasShortECKeyPair(MethodAccess ma, string msg) {
ma.getMethod() instanceof KeyPairGeneratorInitMethod and
exists(
JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kc,
@@ -131,15 +162,11 @@ predicate hasShortECKeyPair(MethodAccess ma, string msg) {
) and
msg = "Key size should be at least 256 bits for EC encryption."
}
-// ! refactor to something like the below,
-// ! need to adjust select clause then...
-// ! see C# and C++ queries for ideas
-// class EncryptionAlgorithm extends
-// predicate hasInsufficientKeySize() {
-// exists(Expr e, string msg |
-// hasShortAESKey(e, msg) or
-// hasShortDsaKeyPair(e, msg) or
-// hasShortRsaKeyPair(e, msg) or
-// hasShortECKeyPair(e, msg)
-// )
-// }
+
+// ! refactor this so can use 'path-problem' select clause instead?
+predicate hasInsufficientKeySize(Expr e, string msg) {
+ hasShortAESKey(e, msg) or
+ hasShortDsaKeyPair(e, msg) or
+ hasShortRsaKeyPair(e, msg) or
+ hasShortECKeyPair(e, msg)
+}
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.qhelp b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.qhelp
index 4e5faa0ddf4..47ef1124624 100644
--- a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.qhelp
+++ b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.qhelp
@@ -1,29 +1,78 @@
-
+
+
- This rule finds uses of encryption algorithms with too small a key size. Encryption algorithms
-are vulnerable to brute force attack when too small a key size is used.
+ Modern encryption relies on the computational infeasibility of breaking a cipher and decoding its
+ message without the key. As computational power increases, the ability to break ciphers grows, and key
+ sizes need to become larger as a result. Encryption algorithms that use too small of a key size are
+ vulnerable to brute force attacks, which can reveal sensitive data.
- The key should be at least 2048 bits long when using RSA and DSA encryption, 256 bits long when using EC encryption, and 128 bits long when using
-symmetric encryption.
+ Use a key of the recommended size or larger. The key size should be at least 2048 bits for RSA or
+ DSA encryption, 256 bits for elliptic curve (EC) encryption, and 128 bits for symmetric encryption,
+ such as AES.
+
+
+
+ The following code uses encryption with insufficient key sizes.
+
+
+
+
+
+ To fix the code, change the key sizes to be the recommended size or
+ larger for each algorithm.
+
+
+
+
+
+
-
- Wikipedia.
- Key size
+ Wikipedia:
+ Key size.
- SonarSource.
- Cryptographic keys should be robust
+ Wikipedia: Strong cryptography.
+
+
+
+ OWASP:
+ Cryptographic Storage Cheat Sheet.
+
+
+ OWASP:
+ Testing for Weak Encryption.
+
+
+ NIST:
+
+ Transitioning the Use of Cryptographic Algorithms and Key Lengths.
-
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
index baccf3b42c3..2787fac00bf 100644
--- a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
+++ b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
@@ -1,6 +1,6 @@
/**
- * @name Use of a cryptographic algorithm with insufficient key size
- * @description Using cryptographic algorithms with too small a key size can
+ * @name Insufficient key size used with a cryptographic algorithm
+ * @description Using cryptographic algorithms with too small of a key size can
* allow an attacker to compromise security.
* @kind problem
* @problem.severity error
@@ -14,9 +14,5 @@ import java
import semmle.code.java.security.InsufficientKeySizeQuery
from Expr e, string msg
-where
- hasShortAESKey(e, msg) or
- hasShortDsaKeyPair(e, msg) or
- hasShortRsaKeyPair(e, msg) or
- hasShortECKeyPair(e, msg)
+where hasInsufficientKeySize(e, msg)
select e, msg
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySizeBad.java b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySizeBad.java
new file mode 100644
index 00000000000..641543ca964
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySizeBad.java
@@ -0,0 +1,16 @@
+ KeyPairGenerator keyPairGen1 = KeyPairGenerator.getInstance("RSA");
+ // BAD: Key size is less than 2048
+ keyPairGen1.initialize(1024);
+
+ KeyPairGenerator keyPairGen2 = KeyPairGenerator.getInstance("DSA");
+ // BAD: Key size is less than 2048
+ keyPairGen2.initialize(1024);
+
+ KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("EC");
+ // BAD: Key size is less than 256
+ ECGenParameterSpec ecSpec1 = new ECGenParameterSpec("secp112r1");
+ keyPairGen3.initialize(ecSpec1);
+
+ KeyGenerator keyGen = KeyGenerator.getInstance("AES");
+ // BAD: Key size is less than 128
+ keyGen.init(64);
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySizeGood.java b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySizeGood.java
new file mode 100644
index 00000000000..051f7dd2597
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySizeGood.java
@@ -0,0 +1,16 @@
+ KeyPairGenerator keyPairGen1 = KeyPairGenerator.getInstance("RSA");
+ // GOOD: Key size is no less than 2048
+ keyPairGen1.initialize(2048);
+
+ KeyPairGenerator keyPairGen2 = KeyPairGenerator.getInstance("DSA");
+ // GOOD: Key size is no less than 2048
+ keyPairGen2.initialize(2048);
+
+ KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("EC");
+ // GOOD: Key size is no less than 256
+ ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256r1");
+ keyPairGen3.initialize(ecSpec);
+
+ KeyGenerator keyGen = KeyGenerator.getInstance("AES");
+ // GOOD: Key size is no less than 128
+ keyGen.init(128);
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.java b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize_OLD.java
similarity index 100%
rename from java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.java
rename to java/ql/src/Security/CWE/CWE-326/InsufficientKeySize_OLD.java
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySize.qlref b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySize.qlref
deleted file mode 100644
index 040c083a1a1..00000000000
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySize.qlref
+++ /dev/null
@@ -1 +0,0 @@
-Security/CWE/CWE-326/InsufficientKeySize.ql
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.expected b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.expected
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySize.java b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
similarity index 71%
rename from java/ql/test/query-tests/security/CWE-326/InsufficientKeySize.java
rename to java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
index a606017a27e..fbff6187855 100644
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySize.java
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
@@ -2,92 +2,92 @@ import java.security.KeyPairGenerator;
import java.security.spec.ECGenParameterSpec;
import javax.crypto.KeyGenerator;
-public class InsufficientKeySize {
+public class InsufficientKeySizeTest {
public void CryptoMethod() throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
KeyGenerator keyGen1 = KeyGenerator.getInstance("AES");
// BAD: Key size is less than 128
- keyGen1.init(64);
+ keyGen1.init(64); // $ hasInsufficientKeySize
KeyGenerator keyGen2 = KeyGenerator.getInstance("AES");
// GOOD: Key size is no less than 128
- keyGen2.init(128);
+ keyGen2.init(128); // Safe
KeyPairGenerator keyPairGen1 = KeyPairGenerator.getInstance("RSA");
// BAD: Key size is less than 2048
- keyPairGen1.initialize(1024);
+ keyPairGen1.initialize(1024); // $ hasInsufficientKeySize
KeyPairGenerator keyPairGen2 = KeyPairGenerator.getInstance("RSA");
// GOOD: Key size is no less than 2048
- keyPairGen2.initialize(2048);
+ keyPairGen2.initialize(2048); // Safe
KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("DSA");
// BAD: Key size is less than 2048
- keyPairGen3.initialize(1024);
+ keyPairGen3.initialize(1024); // $ hasInsufficientKeySize
KeyPairGenerator keyPairGen4 = KeyPairGenerator.getInstance("DSA");
// GOOD: Key size is no less than 2048
- keyPairGen4.initialize(2048);
+ keyPairGen4.initialize(2048); // Safe
KeyPairGenerator keyPairGen5 = KeyPairGenerator.getInstance("EC");
// BAD: Key size is less than 256
ECGenParameterSpec ecSpec1 = new ECGenParameterSpec("secp112r1");
- keyPairGen5.initialize(ecSpec1);
+ keyPairGen5.initialize(ecSpec1); // $ hasInsufficientKeySize
KeyPairGenerator keyPairGen6 = KeyPairGenerator.getInstance("EC");
// BAD: Key size is less than 256
- keyPairGen6.initialize(new ECGenParameterSpec("secp112r1"));
+ keyPairGen6.initialize(new ECGenParameterSpec("secp112r1")); // $ hasInsufficientKeySize
KeyPairGenerator keyPairGen7 = KeyPairGenerator.getInstance("EC");
// GOOD: Key size is no less than 256
ECGenParameterSpec ecSpec2 = new ECGenParameterSpec("secp256r1");
- keyPairGen7.initialize(ecSpec2);
+ keyPairGen7.initialize(ecSpec2); // Safe
KeyPairGenerator keyPairGen8 = KeyPairGenerator.getInstance("EC");
// BAD: Key size is less than 256
ECGenParameterSpec ecSpec3 = new ECGenParameterSpec("X9.62 prime192v2");
- keyPairGen8.initialize(ecSpec3);
+ keyPairGen8.initialize(ecSpec3); // $ hasInsufficientKeySize
KeyPairGenerator keyPairGen9 = KeyPairGenerator.getInstance("EC");
// BAD: Key size is less than 256
ECGenParameterSpec ecSpec4 = new ECGenParameterSpec("X9.62 c2tnb191v3");
- keyPairGen9.initialize(ecSpec4);
+ keyPairGen9.initialize(ecSpec4); // $ hasInsufficientKeySize
KeyPairGenerator keyPairGen10 = KeyPairGenerator.getInstance("EC");
// BAD: Key size is less than 256
ECGenParameterSpec ecSpec5 = new ECGenParameterSpec("sect163k1");
- keyPairGen10.initialize(ecSpec5);
+ keyPairGen10.initialize(ecSpec5); // $ hasInsufficientKeySize
KeyPairGenerator keyPairGen11 = KeyPairGenerator.getInstance("EC");
// GOOD: Key size is no less than 256
ECGenParameterSpec ecSpec6 = new ECGenParameterSpec("X9.62 c2tnb359v1");
- keyPairGen11.initialize(ecSpec6);
+ keyPairGen11.initialize(ecSpec6); // Safe
KeyPairGenerator keyPairGen12 = KeyPairGenerator.getInstance("EC");
// BAD: Key size is less than 256
ECGenParameterSpec ecSpec7 = new ECGenParameterSpec("prime192v2");
- keyPairGen12.initialize(ecSpec7);
+ keyPairGen12.initialize(ecSpec7); // $ hasInsufficientKeySize
KeyPairGenerator keyPairGen13 = KeyPairGenerator.getInstance("EC");
- // BAD: Key size is no less than 256
+ // BAD: Key size is no less than 256 // ! I think this comment is wrong - double-check
ECGenParameterSpec ecSpec8 = new ECGenParameterSpec("prime256v1");
- keyPairGen13.initialize(ecSpec8);
+ keyPairGen13.initialize(ecSpec8); // Safe
KeyPairGenerator keyPairGen14 = KeyPairGenerator.getInstance("EC");
// BAD: Key size is less than 256
ECGenParameterSpec ecSpec9 = new ECGenParameterSpec("c2tnb191v1");
- keyPairGen14.initialize(ecSpec9);
+ keyPairGen14.initialize(ecSpec9); // $ hasInsufficientKeySize
KeyPairGenerator keyPairGen15 = KeyPairGenerator.getInstance("EC");
- // BAD: Key size is no less than 256
+ // BAD: Key size is no less than 256 // ! I think this comment is wrong - double-check
ECGenParameterSpec ecSpec10 = new ECGenParameterSpec("c2tnb431r1");
- keyPairGen15.initialize(ecSpec10);
+ keyPairGen15.initialize(ecSpec10); // Safe
KeyPairGenerator keyPairGen16 = KeyPairGenerator.getInstance("dh");
// BAD: Key size is less than 2048
- keyPairGen16.initialize(1024);
+ keyPairGen16.initialize(1024); // $ hasInsufficientKeySize
KeyPairGenerator keyPairGen17 = KeyPairGenerator.getInstance("DH");
// GOOD: Key size is no less than 2048
- keyPairGen17.initialize(2048);
+ keyPairGen17.initialize(2048); // Safe
}
}
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
new file mode 100644
index 00000000000..b8133b154e4
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
@@ -0,0 +1,18 @@
+import java
+import TestUtilities.InlineExpectationsTest
+import semmle.code.java.security.InsufficientKeySizeQuery
+
+class InsufficientKeySizeTest extends InlineExpectationsTest {
+ InsufficientKeySizeTest() { this = "InsufficientKeySize" }
+
+ override string getARelevantTag() { result = "hasInsufficientKeySize" }
+
+ override predicate hasActualResult(Location location, string element, string tag, string value) {
+ tag = "hasInsufficientKeySize" and
+ exists(Expr e, string msg | hasInsufficientKeySize(e, msg) |
+ e.getLocation() = location and
+ element = e.toString() and
+ value = ""
+ )
+ }
+}
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySize.expected b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTestOLD.txt
similarity index 100%
rename from java/ql/test/query-tests/security/CWE-326/InsufficientKeySize.expected
rename to java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTestOLD.txt
From 7d94590d79c263cd5b9f59897f8e55b3b59ab1a5 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Mon, 3 Oct 2022 15:45:11 -0400
Subject: [PATCH 005/249] add change note
---
java/ql/src/change-notes/2022-10-03-insufficient-key-size.md | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 java/ql/src/change-notes/2022-10-03-insufficient-key-size.md
diff --git a/java/ql/src/change-notes/2022-10-03-insufficient-key-size.md b/java/ql/src/change-notes/2022-10-03-insufficient-key-size.md
new file mode 100644
index 00000000000..b0cf92e1e5a
--- /dev/null
+++ b/java/ql/src/change-notes/2022-10-03-insufficient-key-size.md
@@ -0,0 +1,4 @@
+---
+category: newQuery
+---
+* Added a new query, `java/insufficient-key-size`, to detect the use of cryptographic algorithms with insufficient key sizes.
From 75794ec7a7041c8ae3d5774c1d7fe601b2f4efe2 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Tue, 4 Oct 2022 11:59:20 -0400
Subject: [PATCH 006/249] false negative testing - before rewrite for variable
dataflow
---
.../security/InsufficientKeySizeQuery.qll | 14 +++++++++--
.../CWE-326/InsufficientKeySizeTest.java | 23 ++++++++++++++++++-
2 files changed, 34 insertions(+), 3 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
index eaba8977c23..d6943adebc5 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
@@ -1,5 +1,6 @@
import semmle.code.java.security.Encryption
import semmle.code.java.dataflow.TaintTracking
+import semmle.code.java.dataflow.DataFlow3
/** The Java class `java.security.spec.ECGenParameterSpec`. */
private class ECGenParameterSpec extends RefType {
@@ -86,9 +87,18 @@ private predicate hasShortSymmetricKey(MethodAccess ma, string msg, string type)
jcg.getAlgoSpec().(StringLiteral).getValue() = type and
source.getNode().asExpr() = jcg and
dest.getNode().asExpr() = ma.getQualifier() and
- cc.hasFlowPath(source, dest)
+ //ma.getArgument(0) = var and // ! me
+ //var.getVariable().getInitializer().getUnderlyingExpr() instanceof IntegerLiteral and // ! me
+ cc.hasFlowPath(source, dest) //and
+ //var.getVariable().getInitializer().getUnderlyingExpr().toString().toInt() < 128 // ! me
+ ) and
+ exists(VarAccess var |
+ var.getVariable().getInitializer().getUnderlyingExpr() instanceof IntegerLiteral and
+ var.getVariable().getInitializer().getUnderlyingExpr().toString().toInt() < 128 and
+ //DataFlow3::localExprFlow(var, ma.getArgument(0)) and
+ ma.getArgument(0) = var
+ //ma.getArgument(0).(IntegerLiteral).getIntValue() < 128
) and
- ma.getArgument(0).(IntegerLiteral).getIntValue() < 128 and
msg = "Key size should be at least 128 bits for " + type + " encryption."
}
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
index fbff6187855..d070eaf3d76 100644
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
@@ -3,7 +3,7 @@ import java.security.spec.ECGenParameterSpec;
import javax.crypto.KeyGenerator;
public class InsufficientKeySizeTest {
- public void CryptoMethod() throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
+ public void cryptoMethod() throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
KeyGenerator keyGen1 = KeyGenerator.getInstance("AES");
// BAD: Key size is less than 128
keyGen1.init(64); // $ hasInsufficientKeySize
@@ -89,5 +89,26 @@ public class InsufficientKeySizeTest {
KeyPairGenerator keyPairGen17 = KeyPairGenerator.getInstance("DH");
// GOOD: Key size is no less than 2048
keyPairGen17.initialize(2048); // Safe
+
+
+ // FN: Test with variables as numbers
+ final int size1 = 64;
+ KeyGenerator keyGen3 = KeyGenerator.getInstance("AES");
+ // BAD: Key size is less than 128
+ keyGen3.init(size1); // $ hasInsufficientKeySize
+
+ int size2 = 1024;
+ KeyPairGenerator keyPairGen18 = KeyPairGenerator.getInstance("RSA");
+ // BAD: Key size is less than 2048
+ keyPairGen18.initialize(size2); // $ hasInsufficientKeySize
+
+ int keysize = 64;
+ test(keysize);
+ }
+
+ public void test(int keySize) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
+ KeyGenerator keyGen4 = KeyGenerator.getInstance("AES");
+ // BAD: Key size is less than 128
+ keyGen4.init(keySize); // $ hasInsufficientKeySize
}
}
From 7de9c05c9d3d9b5b1359ac982d5f6e103c3f725b Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Tue, 4 Oct 2022 14:39:39 -0400
Subject: [PATCH 007/249] use CompileTimeConstantExpr for FN with VarAccess,
and remove KeyGeneratorInitConfiguration
---
.../security/InsufficientKeySizeQuery.qll | 68 +++----
...ientKeySizeQuery_BeforeDataFlowRewrite.qll | 182 ++++++++++++++++++
.../CWE-326/InsufficientKeySizeTest.java | 22 +--
3 files changed, 227 insertions(+), 45 deletions(-)
create mode 100644 java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery_BeforeDataFlowRewrite.qll
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
index d6943adebc5..8cc91e4ce0a 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
@@ -36,22 +36,19 @@ private int getECKeySize(string algorithm) {
result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
}
-/** Taint configuration tracking flow from a key generator to a `init` method call. */
-private class KeyGeneratorInitConfiguration extends TaintTracking::Configuration {
- KeyGeneratorInitConfiguration() { this = "KeyGeneratorInitConfiguration" }
-
- override predicate isSource(DataFlow::Node source) {
- source.asExpr() instanceof JavaxCryptoKeyGenerator
- }
-
- override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma |
- ma.getMethod() instanceof KeyGeneratorInitMethod and
- sink.asExpr() = ma.getQualifier()
- )
- }
-}
-
+// /** Taint configuration tracking flow from a key generator to a `init` method call. */
+// private class KeyGeneratorInitConfiguration extends TaintTracking::Configuration {
+// KeyGeneratorInitConfiguration() { this = "KeyGeneratorInitConfiguration" }
+// override predicate isSource(DataFlow::Node source) {
+// source.asExpr() instanceof JavaxCryptoKeyGenerator
+// }
+// override predicate isSink(DataFlow::Node sink) {
+// exists(MethodAccess ma |
+// ma.getMethod() instanceof KeyGeneratorInitMethod and
+// sink.asExpr() = ma.getQualifier()
+// )
+// }
+// }
/**
* Taint configuration tracking flow from a keypair generator to
* an `initialize` method call.
@@ -80,24 +77,27 @@ private class KeyPairGeneratorInitConfiguration extends TaintTracking::Configura
bindingset[type]
private predicate hasShortSymmetricKey(MethodAccess ma, string msg, string type) {
ma.getMethod() instanceof KeyGeneratorInitMethod and
- exists(
- JavaxCryptoKeyGenerator jcg, KeyGeneratorInitConfiguration cc, DataFlow::PathNode source,
- DataFlow::PathNode dest
- |
- jcg.getAlgoSpec().(StringLiteral).getValue() = type and
- source.getNode().asExpr() = jcg and
- dest.getNode().asExpr() = ma.getQualifier() and
- //ma.getArgument(0) = var and // ! me
- //var.getVariable().getInitializer().getUnderlyingExpr() instanceof IntegerLiteral and // ! me
- cc.hasFlowPath(source, dest) //and
- //var.getVariable().getInitializer().getUnderlyingExpr().toString().toInt() < 128 // ! me
- ) and
- exists(VarAccess var |
- var.getVariable().getInitializer().getUnderlyingExpr() instanceof IntegerLiteral and
- var.getVariable().getInitializer().getUnderlyingExpr().toString().toInt() < 128 and
- //DataFlow3::localExprFlow(var, ma.getArgument(0)) and
- ma.getArgument(0) = var
- //ma.getArgument(0).(IntegerLiteral).getIntValue() < 128
+ // exists(JavaxCryptoKeyGenerator jcg, DataFlow::PathNode source, DataFlow::PathNode dest |
+ // jcg.getAlgoSpec().(StringLiteral).getValue() = type //and
+ // //source.getNode().asExpr() = jcg and
+ // //dest.getNode().asExpr() = ma.getQualifier() //and
+ // //cc.hasFlowPath(source, dest)
+ // ) and
+ (
+ // exists(VarAccess var |
+ // var.getVariable().getInitializer().getUnderlyingExpr() instanceof IntegerLiteral and
+ // var.getVariable().getInitializer().getUnderlyingExpr().toString().toInt() < 128 and
+ // ma.getArgument(0) = var
+ // )
+ // or
+ // below is better than above?
+ exists(CompileTimeConstantExpr var |
+ //var.getUnderlyingExpr() instanceof IntegerLiteral and // can't include this...
+ var.getIntValue() < 128 and
+ ma.getArgument(0) = var
+ )
+ or
+ ma.getArgument(0).(IntegerLiteral).getIntValue() < 128
) and
msg = "Key size should be at least 128 bits for " + type + " encryption."
}
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery_BeforeDataFlowRewrite.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery_BeforeDataFlowRewrite.qll
new file mode 100644
index 00000000000..d6943adebc5
--- /dev/null
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery_BeforeDataFlowRewrite.qll
@@ -0,0 +1,182 @@
+import semmle.code.java.security.Encryption
+import semmle.code.java.dataflow.TaintTracking
+import semmle.code.java.dataflow.DataFlow3
+
+/** The Java class `java.security.spec.ECGenParameterSpec`. */
+private class ECGenParameterSpec extends RefType {
+ ECGenParameterSpec() { this.hasQualifiedName("java.security.spec", "ECGenParameterSpec") }
+}
+
+/** The `init` method declared in `javax.crypto.KeyGenerator`. */
+private class KeyGeneratorInitMethod extends Method {
+ KeyGeneratorInitMethod() {
+ this.getDeclaringType() instanceof KeyGenerator and
+ this.hasName("init")
+ }
+}
+
+/** The `initialize` method declared in `java.security.KeyPairGenerator`. */
+private class KeyPairGeneratorInitMethod extends Method {
+ KeyPairGeneratorInitMethod() {
+ this.getDeclaringType() instanceof KeyPairGenerator and
+ this.hasName("initialize")
+ }
+}
+
+/** Returns the key size in the EC algorithm string */
+bindingset[algorithm]
+private int getECKeySize(string algorithm) {
+ algorithm.matches("sec%") and // specification such as "secp256r1"
+ result = algorithm.regexpCapture("sec[p|t](\\d+)[a-zA-Z].*", 1).toInt()
+ or
+ algorithm.matches("X9.62%") and //specification such as "X9.62 prime192v2"
+ result = algorithm.regexpCapture("X9\\.62 .*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
+ or
+ (algorithm.matches("prime%") or algorithm.matches("c2tnb%")) and //specification such as "prime192v2"
+ result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
+}
+
+/** Taint configuration tracking flow from a key generator to a `init` method call. */
+private class KeyGeneratorInitConfiguration extends TaintTracking::Configuration {
+ KeyGeneratorInitConfiguration() { this = "KeyGeneratorInitConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) {
+ source.asExpr() instanceof JavaxCryptoKeyGenerator
+ }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(MethodAccess ma |
+ ma.getMethod() instanceof KeyGeneratorInitMethod and
+ sink.asExpr() = ma.getQualifier()
+ )
+ }
+}
+
+/**
+ * Taint configuration tracking flow from a keypair generator to
+ * an `initialize` method call.
+ */
+private class KeyPairGeneratorInitConfiguration extends TaintTracking::Configuration {
+ KeyPairGeneratorInitConfiguration() { this = "KeyPairGeneratorInitConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) {
+ source.asExpr() instanceof JavaSecurityKeyPairGenerator
+ }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(MethodAccess ma |
+ ma.getMethod() instanceof KeyPairGeneratorInitMethod and
+ sink.asExpr() = ma.getQualifier()
+ )
+ }
+}
+
+/**
+ * Holds if a symmetric `KeyGenerator` implementing encryption algorithm
+ * `type` and initialized by `ma` uses an insufficient key size.
+ *
+ * `msg` provides a human-readable description of the problem.
+ */
+bindingset[type]
+private predicate hasShortSymmetricKey(MethodAccess ma, string msg, string type) {
+ ma.getMethod() instanceof KeyGeneratorInitMethod and
+ exists(
+ JavaxCryptoKeyGenerator jcg, KeyGeneratorInitConfiguration cc, DataFlow::PathNode source,
+ DataFlow::PathNode dest
+ |
+ jcg.getAlgoSpec().(StringLiteral).getValue() = type and
+ source.getNode().asExpr() = jcg and
+ dest.getNode().asExpr() = ma.getQualifier() and
+ //ma.getArgument(0) = var and // ! me
+ //var.getVariable().getInitializer().getUnderlyingExpr() instanceof IntegerLiteral and // ! me
+ cc.hasFlowPath(source, dest) //and
+ //var.getVariable().getInitializer().getUnderlyingExpr().toString().toInt() < 128 // ! me
+ ) and
+ exists(VarAccess var |
+ var.getVariable().getInitializer().getUnderlyingExpr() instanceof IntegerLiteral and
+ var.getVariable().getInitializer().getUnderlyingExpr().toString().toInt() < 128 and
+ //DataFlow3::localExprFlow(var, ma.getArgument(0)) and
+ ma.getArgument(0) = var
+ //ma.getArgument(0).(IntegerLiteral).getIntValue() < 128
+ ) and
+ msg = "Key size should be at least 128 bits for " + type + " encryption."
+}
+
+/**
+ * Holds if an AES `KeyGenerator` initialized by `ma` uses an insufficient key size.
+ * `msg` provides a human-readable description of the problem.
+ */
+private predicate hasShortAESKey(MethodAccess ma, string msg) {
+ hasShortSymmetricKey(ma, msg, "AES")
+}
+
+/**
+ * Holds if an asymmetric `KeyPairGenerator` implementing encryption algorithm
+ * `type` and initialized by `ma` uses an insufficient key size.
+ *
+ * `msg` provides a human-readable description of the problem.
+ */
+bindingset[type]
+private predicate hasShortAsymmetricKeyPair(MethodAccess ma, string msg, string type) {
+ ma.getMethod() instanceof KeyPairGeneratorInitMethod and
+ exists(
+ JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kc,
+ DataFlow::PathNode source, DataFlow::PathNode dest
+ |
+ jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase() = type and
+ source.getNode().asExpr() = jpg and
+ dest.getNode().asExpr() = ma.getQualifier() and
+ kc.hasFlowPath(source, dest)
+ ) and
+ ma.getArgument(0).(IntegerLiteral).getIntValue() < 2048 and
+ msg = "Key size should be at least 2048 bits for " + type + " encryption."
+}
+
+/**
+ * Holds if a DSA `KeyPairGenerator` initialized by `ma` uses an insufficient key size.
+ *
+ * `msg` provides a human-readable description of the problem.
+ */
+private predicate hasShortDsaKeyPair(MethodAccess ma, string msg) {
+ hasShortAsymmetricKeyPair(ma, msg, "DSA") or
+ hasShortAsymmetricKeyPair(ma, msg, "DH")
+}
+
+/**
+ * Holds if a RSA `KeyPairGenerator` initialized by `ma` uses an insufficient key size.
+ *
+ * `msg` provides a human-readable description of the problem.
+ */
+private predicate hasShortRsaKeyPair(MethodAccess ma, string msg) {
+ hasShortAsymmetricKeyPair(ma, msg, "RSA")
+}
+
+/**
+ * Holds if an EC `KeyPairGenerator` initialized by `ma` uses an insufficient key size.
+ *
+ * `msg` provides a human-readable description of the problem.
+ */
+private predicate hasShortECKeyPair(MethodAccess ma, string msg) {
+ ma.getMethod() instanceof KeyPairGeneratorInitMethod and
+ exists(
+ JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kc,
+ DataFlow::PathNode source, DataFlow::PathNode dest, ClassInstanceExpr cie
+ |
+ jpg.getAlgoSpec().(StringLiteral).getValue().matches("EC%") and // ECC variants such as ECDH and ECDSA
+ source.getNode().asExpr() = jpg and
+ dest.getNode().asExpr() = ma.getQualifier() and
+ kc.hasFlowPath(source, dest) and
+ DataFlow::localExprFlow(cie, ma.getArgument(0)) and
+ ma.getArgument(0).getType() instanceof ECGenParameterSpec and
+ getECKeySize(cie.getArgument(0).(StringLiteral).getValue()) < 256
+ ) and
+ msg = "Key size should be at least 256 bits for EC encryption."
+}
+
+// ! refactor this so can use 'path-problem' select clause instead?
+predicate hasInsufficientKeySize(Expr e, string msg) {
+ hasShortAESKey(e, msg) or
+ hasShortDsaKeyPair(e, msg) or
+ hasShortRsaKeyPair(e, msg) or
+ hasShortECKeyPair(e, msg)
+}
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
index d070eaf3d76..dfffe740a3f 100644
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
@@ -97,18 +97,18 @@ public class InsufficientKeySizeTest {
// BAD: Key size is less than 128
keyGen3.init(size1); // $ hasInsufficientKeySize
- int size2 = 1024;
- KeyPairGenerator keyPairGen18 = KeyPairGenerator.getInstance("RSA");
- // BAD: Key size is less than 2048
- keyPairGen18.initialize(size2); // $ hasInsufficientKeySize
+ // int size2 = 1024;
+ // KeyPairGenerator keyPairGen18 = KeyPairGenerator.getInstance("RSA");
+ // // BAD: Key size is less than 2048
+ // keyPairGen18.initialize(size2); // $ hasInsufficientKeySize
- int keysize = 64;
- test(keysize);
+ // int keysize = 64;
+ // test(keysize);
}
- public void test(int keySize) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
- KeyGenerator keyGen4 = KeyGenerator.getInstance("AES");
- // BAD: Key size is less than 128
- keyGen4.init(keySize); // $ hasInsufficientKeySize
- }
+ // public void test(int keySize) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
+ // KeyGenerator keyGen4 = KeyGenerator.getInstance("AES");
+ // // BAD: Key size is less than 128
+ // keyGen4.init(keySize); // $ hasInsufficientKeySize
+ // }
}
From d3b1a04c133548e01f341df306d01f527ae6cbf7 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Tue, 4 Oct 2022 20:46:55 -0400
Subject: [PATCH 008/249] handle FN case with simple VarAccess; add draft of
dataflow config to handle complex VarAccess
---
.../security/InsufficientKeySizeQuery.qll | 117 +++++++++++++-----
.../CWE/CWE-326/InsufficientKeySize.ql | 11 +-
.../CWE-326/InsufficientKeySizeTest.java | 22 ++--
3 files changed, 104 insertions(+), 46 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
index 8cc91e4ce0a..39ded30d012 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
@@ -1,6 +1,32 @@
import semmle.code.java.security.Encryption
import semmle.code.java.dataflow.TaintTracking
-import semmle.code.java.dataflow.DataFlow3
+import semmle.code.java.dataflow.DataFlow
+
+//import DataFlow::PathGraph
+/**
+ * Asymmetric (RSA, DSA, DH) key length data flow tracking configuration.
+ */
+class AsymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
+ AsymmetricKeyTrackingConfiguration() { this = "AsymmetricKeyTrackingConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) {
+ exists(IntegerLiteral integer, VarAccess var |
+ integer.getIntValue() < 2048 and
+ source.asExpr() = integer
+ or
+ var.getVariable().getInitializer().getUnderlyingExpr() instanceof IntegerLiteral and
+ var.getVariable().getInitializer().getUnderlyingExpr().toString().toInt() < 2048 and
+ source.asExpr() = var.getVariable().getInitializer()
+ )
+ }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(MethodAccess ma |
+ ma.getMethod() instanceof KeyPairGeneratorInitMethod and
+ sink.asExpr() = ma.getArgument(0)
+ )
+ }
+}
/** The Java class `java.security.spec.ECGenParameterSpec`. */
private class ECGenParameterSpec extends RefType {
@@ -36,19 +62,22 @@ private int getECKeySize(string algorithm) {
result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
}
-// /** Taint configuration tracking flow from a key generator to a `init` method call. */
-// private class KeyGeneratorInitConfiguration extends TaintTracking::Configuration {
-// KeyGeneratorInitConfiguration() { this = "KeyGeneratorInitConfiguration" }
-// override predicate isSource(DataFlow::Node source) {
-// source.asExpr() instanceof JavaxCryptoKeyGenerator
-// }
-// override predicate isSink(DataFlow::Node sink) {
-// exists(MethodAccess ma |
-// ma.getMethod() instanceof KeyGeneratorInitMethod and
-// sink.asExpr() = ma.getQualifier()
-// )
-// }
-// }
+/** Taint configuration tracking flow from a key generator to a `init` method call. */
+private class KeyGeneratorInitConfiguration extends TaintTracking::Configuration {
+ KeyGeneratorInitConfiguration() { this = "KeyGeneratorInitConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) {
+ source.asExpr() instanceof JavaxCryptoKeyGenerator
+ }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(MethodAccess ma |
+ ma.getMethod() instanceof KeyGeneratorInitMethod and
+ sink.asExpr() = ma.getQualifier()
+ )
+ }
+}
+
/**
* Taint configuration tracking flow from a keypair generator to
* an `initialize` method call.
@@ -77,26 +106,33 @@ private class KeyPairGeneratorInitConfiguration extends TaintTracking::Configura
bindingset[type]
private predicate hasShortSymmetricKey(MethodAccess ma, string msg, string type) {
ma.getMethod() instanceof KeyGeneratorInitMethod and
- // exists(JavaxCryptoKeyGenerator jcg, DataFlow::PathNode source, DataFlow::PathNode dest |
- // jcg.getAlgoSpec().(StringLiteral).getValue() = type //and
- // //source.getNode().asExpr() = jcg and
- // //dest.getNode().asExpr() = ma.getQualifier() //and
- // //cc.hasFlowPath(source, dest)
- // ) and
+ // flow needed to correctly determine algorithm type and
+ // not match to ANY symmetric algorithm (although doesn't really matter since only have AES currently...)
+ exists(
+ JavaxCryptoKeyGenerator jcg, KeyGeneratorInitConfiguration cc, DataFlow::PathNode source,
+ DataFlow::PathNode dest
+ |
+ jcg.getAlgoSpec().(StringLiteral).getValue() = type and
+ source.getNode().asExpr() = jcg and
+ dest.getNode().asExpr() = ma.getQualifier() and
+ cc.hasFlowPath(source, dest)
+ ) and
(
- // exists(VarAccess var |
- // var.getVariable().getInitializer().getUnderlyingExpr() instanceof IntegerLiteral and
- // var.getVariable().getInitializer().getUnderlyingExpr().toString().toInt() < 128 and
- // ma.getArgument(0) = var
- // )
- // or
- // below is better than above?
- exists(CompileTimeConstantExpr var |
- //var.getUnderlyingExpr() instanceof IntegerLiteral and // can't include this...
- var.getIntValue() < 128 and
+ // VarAccess case needed to handle FN of key-size stored in a variable
+ // Note: cannot use CompileTimeConstantExpr since will miss cases when variable is not a compile-time constant
+ // (e.g. not declared `final` in Java)
+ exists(VarAccess var |
+ var.getVariable().getInitializer().getUnderlyingExpr() instanceof IntegerLiteral and
+ var.getVariable().getInitializer().getUnderlyingExpr().toString().toInt() < 128 and
ma.getArgument(0) = var
)
or
+ // exists(CompileTimeConstantExpr var |
+ // //var.getUnderlyingExpr() instanceof IntegerLiteral and // can't include this...
+ // var.getIntValue() < 128 and
+ // ma.getArgument(0) = var
+ // )
+ // or
ma.getArgument(0).(IntegerLiteral).getIntValue() < 128
) and
msg = "Key size should be at least 128 bits for " + type + " encryption."
@@ -119,6 +155,8 @@ private predicate hasShortAESKey(MethodAccess ma, string msg) {
bindingset[type]
private predicate hasShortAsymmetricKeyPair(MethodAccess ma, string msg, string type) {
ma.getMethod() instanceof KeyPairGeneratorInitMethod and
+ // flow needed to correctly determine algorithm type and
+ // not match to ANY asymmetric algorithm
exists(
JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kc,
DataFlow::PathNode source, DataFlow::PathNode dest
@@ -128,7 +166,24 @@ private predicate hasShortAsymmetricKeyPair(MethodAccess ma, string msg, string
dest.getNode().asExpr() = ma.getQualifier() and
kc.hasFlowPath(source, dest)
) and
- ma.getArgument(0).(IntegerLiteral).getIntValue() < 2048 and
+ // VarAccess case needed to handle FN of key-size stored in a variable
+ // Note: cannot use CompileTimeConstantExpr since will miss cases when variable is not a compile-time constant
+ // (e.g. not declared `final` in Java)
+ (
+ exists(VarAccess var |
+ var.getVariable().getInitializer().getUnderlyingExpr() instanceof IntegerLiteral and
+ var.getVariable().getInitializer().getUnderlyingExpr().toString().toInt() < 2048 and
+ ma.getArgument(0) = var
+ )
+ or
+ ma.getArgument(0).(IntegerLiteral).getIntValue() < 2048
+ or
+ exists(
+ AsymmetricKeyTrackingConfiguration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
+ |
+ cfg.hasFlowPath(source, sink)
+ )
+ ) and
msg = "Key size should be at least 2048 bits for " + type + " encryption."
}
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
index 2787fac00bf..149e2d40675 100644
--- a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
+++ b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
@@ -2,7 +2,7 @@
* @name Insufficient key size used with a cryptographic algorithm
* @description Using cryptographic algorithms with too small of a key size can
* allow an attacker to compromise security.
- * @kind problem
+ * @kind path-problem
* @problem.severity error
* @precision high
* @id java/insufficient-key-size
@@ -13,6 +13,9 @@
import java
import semmle.code.java.security.InsufficientKeySizeQuery
-from Expr e, string msg
-where hasInsufficientKeySize(e, msg)
-select e, msg
+// from Expr e, string msg
+// where hasInsufficientKeySize(e, msg)
+// select e, msg
+from AsymmetricKeyTrackingConfiguration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
+where cfg.hasFlowPath(source, sink)
+select sink, source, sink, "The size of this RSA key should be at least 2048 bits."
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
index dfffe740a3f..4bd29e2b3e8 100644
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
@@ -97,18 +97,18 @@ public class InsufficientKeySizeTest {
// BAD: Key size is less than 128
keyGen3.init(size1); // $ hasInsufficientKeySize
- // int size2 = 1024;
- // KeyPairGenerator keyPairGen18 = KeyPairGenerator.getInstance("RSA");
- // // BAD: Key size is less than 2048
- // keyPairGen18.initialize(size2); // $ hasInsufficientKeySize
+ int size2 = 1024;
+ KeyPairGenerator keyPairGen18 = KeyPairGenerator.getInstance("RSA");
+ // BAD: Key size is less than 2048
+ keyPairGen18.initialize(size2); // $ hasInsufficientKeySize
- // int keysize = 64;
- // test(keysize);
+ int keysize = 1024;
+ test(keysize);
}
- // public void test(int keySize) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
- // KeyGenerator keyGen4 = KeyGenerator.getInstance("AES");
- // // BAD: Key size is less than 128
- // keyGen4.init(keySize); // $ hasInsufficientKeySize
- // }
+ public void test(int keySize) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
+ KeyPairGenerator keyPairGen19 = KeyPairGenerator.getInstance("RSA");
+ // BAD: Key size is less than 128
+ keyPairGen19.initialize(keySize); // $ hasInsufficientKeySize
+ }
}
From 8ffd2522e79ad22470e33e0e43a70e99f38cd287 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Tue, 4 Oct 2022 23:21:15 -0400
Subject: [PATCH 009/249] add draft code to find algo type to replace
tainttracking configs
---
.../semmle/code/java/security/Encryption.qll | 1 +
.../security/InsufficientKeySizeQuery.qll | 20 +++++++++++++++++++
.../CWE/CWE-326/InsufficientKeySize.ql | 5 ++++-
.../CWE-326/InsufficientKeySizeTest.java | 2 +-
4 files changed, 26 insertions(+), 2 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/Encryption.qll b/java/ql/lib/semmle/code/java/security/Encryption.qll
index 9659fb92843..c19d51a2f4b 100644
--- a/java/ql/lib/semmle/code/java/security/Encryption.qll
+++ b/java/ql/lib/semmle/code/java/security/Encryption.qll
@@ -252,6 +252,7 @@ string getASecureAlgorithmName() {
"Blowfish", "ECIES" // ! Blowfish not actually secure based on https://rules.sonarsource.com/java/type/Vulnerability/RSPEC-4426 ??
// ! hmm, other sources imply that it is secure...
// ! also no DH here, etc.?
+ // ! also is ECB matched with AES?
]
}
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
index 39ded30d012..9aef9b44c6b 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
@@ -155,6 +155,26 @@ private predicate hasShortAESKey(MethodAccess ma, string msg) {
bindingset[type]
private predicate hasShortAsymmetricKeyPair(MethodAccess ma, string msg, string type) {
ma.getMethod() instanceof KeyPairGeneratorInitMethod and
+ ma.getQualifier() instanceof JavaSecurityKeyPairGenerator and
+ ma.getQualifier().getBasicBlock() instanceof JavaSecurityKeyPairGenerator and
+ //ma.getQualifier().getBasicBlock().getNode(2) instanceof JavaSecurityKeyPairGenerator and
+ // ma.getQualifier()
+ // .getBasicBlock()
+ // .getANode()
+ // .(JavaSecurityKeyPairGenerator)
+ // .getAlgoSpec()
+ // .(StringLiteral)
+ // .getValue()
+ // .toUpperCase() = type and
+ //ma.getQualifier().getBasicBlock().getAPredecessor() instanceof JavaSecurityKeyPairGenerator and
+ ma.getQualifier()
+ .getBasicBlock()
+ .getAPredecessor()
+ .(JavaSecurityKeyPairGenerator)
+ .getAlgoSpec()
+ .(StringLiteral)
+ .getValue()
+ .toUpperCase() = type and
// flow needed to correctly determine algorithm type and
// not match to ANY asymmetric algorithm
exists(
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
index 149e2d40675..66bf459f5f7 100644
--- a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
+++ b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
@@ -4,6 +4,7 @@
* allow an attacker to compromise security.
* @kind path-problem
* @problem.severity error
+ * @security-severity 7.5
* @precision high
* @id java/insufficient-key-size
* @tags security
@@ -12,10 +13,12 @@
import java
import semmle.code.java.security.InsufficientKeySizeQuery
+import DataFlow::PathGraph
// from Expr e, string msg
// where hasInsufficientKeySize(e, msg)
// select e, msg
from AsymmetricKeyTrackingConfiguration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
-select sink, source, sink, "The size of this RSA key should be at least 2048 bits."
+select sink.getNode(), source, sink, "The $@ of an asymmetric key should be at least 2048 bits.",
+ sink.getNode(), "size"
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
index 4bd29e2b3e8..c5f151128fa 100644
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
@@ -106,7 +106,7 @@ public class InsufficientKeySizeTest {
test(keysize);
}
- public void test(int keySize) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
+ public static void test(int keySize) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
KeyPairGenerator keyPairGen19 = KeyPairGenerator.getInstance("RSA");
// BAD: Key size is less than 128
keyPairGen19.initialize(keySize); // $ hasInsufficientKeySize
From ac707198d5437a69e1b1154865b848d4dc01ab82 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Wed, 5 Oct 2022 09:48:51 -0400
Subject: [PATCH 010/249] commit before adding taint flow back (since no taint
flow doesn't capture all cases)
---
.../security/InsufficientKeySizeQuery.qll | 41 +++++++++++--------
.../CWE-326/InsufficientKeySizeTest.java | 7 +++-
2 files changed, 29 insertions(+), 19 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
index 9aef9b44c6b..720ed112981 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
@@ -155,8 +155,11 @@ private predicate hasShortAESKey(MethodAccess ma, string msg) {
bindingset[type]
private predicate hasShortAsymmetricKeyPair(MethodAccess ma, string msg, string type) {
ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- ma.getQualifier() instanceof JavaSecurityKeyPairGenerator and
- ma.getQualifier().getBasicBlock() instanceof JavaSecurityKeyPairGenerator and
+ //ma.getQualifier() instanceof JavaSecurityKeyPairGenerator and
+ //ma.getQualifier().getBasicBlock() instanceof JavaSecurityKeyPairGenerator and
+ // * USE BELOW
+ ma.getQualifier().getBasicBlock().getAPredecessor() instanceof JavaSecurityKeyPairGenerator and
+ // * USE ABOVE
//ma.getQualifier().getBasicBlock().getNode(2) instanceof JavaSecurityKeyPairGenerator and
// ma.getQualifier()
// .getBasicBlock()
@@ -167,6 +170,7 @@ private predicate hasShortAsymmetricKeyPair(MethodAccess ma, string msg, string
// .getValue()
// .toUpperCase() = type and
//ma.getQualifier().getBasicBlock().getAPredecessor() instanceof JavaSecurityKeyPairGenerator and
+ // * USE BELOW
ma.getQualifier()
.getBasicBlock()
.getAPredecessor()
@@ -175,17 +179,20 @@ private predicate hasShortAsymmetricKeyPair(MethodAccess ma, string msg, string
.(StringLiteral)
.getValue()
.toUpperCase() = type and
+ // * USE ABOVE
// flow needed to correctly determine algorithm type and
// not match to ANY asymmetric algorithm
- exists(
- JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kc,
- DataFlow::PathNode source, DataFlow::PathNode dest
- |
- jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase() = type and
- source.getNode().asExpr() = jpg and
- dest.getNode().asExpr() = ma.getQualifier() and
- kc.hasFlowPath(source, dest)
- ) and
+ // * REMOVE BELOW
+ // exists(
+ // JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kc,
+ // DataFlow::PathNode source, DataFlow::PathNode dest
+ // |
+ // jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase() = type and
+ // source.getNode().asExpr() = jpg and
+ // dest.getNode().asExpr() = ma.getQualifier() and
+ // kc.hasFlowPath(source, dest)
+ // ) and
+ // * REMOVE ABOVE
// VarAccess case needed to handle FN of key-size stored in a variable
// Note: cannot use CompileTimeConstantExpr since will miss cases when variable is not a compile-time constant
// (e.g. not declared `final` in Java)
@@ -197,12 +204,12 @@ private predicate hasShortAsymmetricKeyPair(MethodAccess ma, string msg, string
)
or
ma.getArgument(0).(IntegerLiteral).getIntValue() < 2048
- or
- exists(
- AsymmetricKeyTrackingConfiguration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
- |
- cfg.hasFlowPath(source, sink)
- )
+ // or
+ // exists(
+ // AsymmetricKeyTrackingConfiguration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
+ // |
+ // cfg.hasFlowPath(source, sink)
+ // )
) and
msg = "Key size should be at least 2048 bits for " + type + " encryption."
}
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
index c5f151128fa..9d9f5688520 100644
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
@@ -103,12 +103,15 @@ public class InsufficientKeySizeTest {
keyPairGen18.initialize(size2); // $ hasInsufficientKeySize
int keysize = 1024;
- test(keysize);
+ KeyPairGenerator keyPairGen20 = KeyPairGenerator.getInstance("DSA");
+ test(keysize, keyPairGen20);
}
- public static void test(int keySize) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
+ public static void test(int keySize, KeyPairGenerator kpg) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
KeyPairGenerator keyPairGen19 = KeyPairGenerator.getInstance("RSA");
// BAD: Key size is less than 128
keyPairGen19.initialize(keySize); // $ hasInsufficientKeySize
+
+ kpg.initialize(1024); // $ hasInsufficientKeySize
}
}
From 5e2ef660148f2840c3497f4ea3f483d6cbb35ae2 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Thu, 6 Oct 2022 00:40:42 -0400
Subject: [PATCH 011/249] refactoring to use both dataflow configs; commit
before deleting unused code
---
.../security/InsufficientKeySizeQuery.qll | 141 +++++++++-
...BeforeAddingTaintFlowBackForAsymmetric.qll | 264 ++++++++++++++++++
.../CWE/CWE-326/InsufficientKeySize.ql | 18 +-
.../CWE-326/InsufficientKeySizeTest.java | 245 ++++++++++------
4 files changed, 579 insertions(+), 89 deletions(-)
create mode 100644 java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery_BeforeAddingTaintFlowBackForAsymmetric.qll
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
index 720ed112981..869f3c7cb0f 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
@@ -2,7 +2,7 @@ import semmle.code.java.security.Encryption
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.DataFlow
-//import DataFlow::PathGraph
+// ******* DATAFLOW *******************************************************************************
/**
* Asymmetric (RSA, DSA, DH) key length data flow tracking configuration.
*/
@@ -14,6 +14,8 @@ class AsymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
integer.getIntValue() < 2048 and
source.asExpr() = integer
or
+ // The below only handles cases when variables are used (both locally in a method and between methods)
+ // The above adds handling for direct use of integers as well
var.getVariable().getInitializer().getUnderlyingExpr() instanceof IntegerLiteral and
var.getVariable().getInitializer().getUnderlyingExpr().toString().toInt() < 2048 and
source.asExpr() = var.getVariable().getInitializer()
@@ -28,11 +30,101 @@ class AsymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
}
}
+/**
+ * Symmetric (AES) key length data flow tracking configuration.
+ */
+class SymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
+ SymmetricKeyTrackingConfiguration() { this = "SymmetricKeyTrackingConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) {
+ exists(IntegerLiteral integer, VarAccess var |
+ integer.getIntValue() < 128 and
+ source.asExpr() = integer
+ or
+ // The below only handles cases when variables are used (both locally in a method and between methods)
+ // The above adds handling for direct use of integers as well
+ var.getVariable().getInitializer().getUnderlyingExpr() instanceof IntegerLiteral and
+ var.getVariable().getInitializer().getUnderlyingExpr().toString().toInt() < 128 and
+ source.asExpr() = var.getVariable().getInitializer()
+ )
+ }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(MethodAccess ma |
+ ma.getMethod() instanceof KeyGeneratorInitMethod and
+ sink.asExpr() = ma.getArgument(0)
+ )
+ }
+}
+
+/**
+ * Symmetric (AES) key length data flow tracking configuration.
+ */
+class SymmetricKeyTrackingConfiguration2 extends DataFlow::Configuration {
+ SymmetricKeyTrackingConfiguration2() { this = "SymmetricKeyTrackingConfiguration2" }
+
+ override predicate isSource(DataFlow::Node source) {
+ source.asExpr() instanceof IntegerLiteral and
+ source.toString().toInt() < 128
+ }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(MethodAccess ma |
+ ma.getMethod() instanceof KeyGeneratorInitMethod and
+ sink.asExpr() = ma.getArgument(0)
+ )
+ }
+}
+
+class UnsafeSymmetricKeySize extends IntegerLiteral {
+ UnsafeSymmetricKeySize() { this.getIntValue() < 128 }
+}
+
+class UnsafeAsymmetricKeySize extends IntegerLiteral {
+ UnsafeAsymmetricKeySize() { this.getIntValue() < 2048 }
+}
+
+class UnsafeKeySize extends IntegerLiteral {
+ UnsafeKeySize() {
+ this instanceof UnsafeAsymmetricKeySize and
+ exists(MethodAccess ma | ma.getMethod() instanceof KeyPairGeneratorInitMethod)
+ or
+ this instanceof UnsafeSymmetricKeySize and
+ exists(MethodAccess ma | ma.getMethod() instanceof KeyGeneratorInitMethod)
+ }
+}
+
+class KeyInitMethod extends Method {
+ KeyInitMethod() {
+ this instanceof KeyGeneratorInitMethod or
+ this instanceof KeyPairGeneratorInitMethod
+ }
+}
+
+/**
+ * key length data flow tracking configuration.
+ */
+class KeyTrackingConfiguration extends DataFlow::Configuration {
+ KeyTrackingConfiguration() { this = "KeyTrackingConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) { source.asExpr() instanceof UnsafeKeySize }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(MethodAccess ma |
+ ma.getMethod() instanceof KeyInitMethod and
+ sink.asExpr() = ma.getArgument(0)
+ )
+ }
+}
+
+// ******* DATAFLOW *******************************************************************************
+// ! move to Encryption.qll?
/** The Java class `java.security.spec.ECGenParameterSpec`. */
private class ECGenParameterSpec extends RefType {
ECGenParameterSpec() { this.hasQualifiedName("java.security.spec", "ECGenParameterSpec") }
}
+// ! move to Encryption.qll?
/** The `init` method declared in `javax.crypto.KeyGenerator`. */
private class KeyGeneratorInitMethod extends Method {
KeyGeneratorInitMethod() {
@@ -41,6 +133,7 @@ private class KeyGeneratorInitMethod extends Method {
}
}
+// ! move to Encryption.qll?
/** The `initialize` method declared in `java.security.KeyPairGenerator`. */
private class KeyPairGeneratorInitMethod extends Method {
KeyPairGeneratorInitMethod() {
@@ -97,6 +190,46 @@ private class KeyPairGeneratorInitConfiguration extends TaintTracking::Configura
}
}
+/**
+ * Holds if a symmetric `KeyGenerator` implementing encryption algorithm
+ * `type` and initialized by `ma` uses an insufficient key size.
+ *
+ * `msg` provides a human-readable description of the problem.
+ */
+//bindingset[type]
+private predicate hasShortSymmetricKey_TEST() {
+ exists(
+ SymmetricKeyTrackingConfiguration2 cfg, DataFlow::PathNode source, DataFlow::PathNode sink
+ |
+ cfg.hasFlowPath(source, sink)
+ )
+ // ma.getMethod() instanceof KeyGeneratorInitMethod and
+ // // flow needed to correctly determine algorithm type and
+ // // not match to ANY symmetric algorithm (although doesn't really matter since only have AES currently...)
+ // exists(
+ // JavaxCryptoKeyGenerator jcg, KeyGeneratorInitConfiguration cc, DataFlow::PathNode source,
+ // DataFlow::PathNode dest
+ // |
+ // jcg.getAlgoSpec().(StringLiteral).getValue() = type and
+ // source.getNode().asExpr() = jcg and
+ // dest.getNode().asExpr() = ma.getQualifier() and
+ // cc.hasFlowPath(source, dest)
+ // ) and
+ // (
+ // // VarAccess case needed to handle FN of key-size stored in a variable
+ // // Note: cannot use CompileTimeConstantExpr since will miss cases when variable is not a compile-time constant
+ // // (e.g. not declared `final` in Java)
+ // exists(VarAccess var |
+ // var.getVariable().getInitializer().getUnderlyingExpr() instanceof IntegerLiteral and
+ // var.getVariable().getInitializer().getUnderlyingExpr().toString().toInt() < 128 and
+ // ma.getArgument(0) = var
+ // )
+ // or
+ // ma.getArgument(0).(IntegerLiteral).getIntValue() < 128
+ // ) and
+ // msg = "Key size should be at least 128 bits for " + type + " encryption."
+}
+
/**
* Holds if a symmetric `KeyGenerator` implementing encryption algorithm
* `type` and initialized by `ma` uses an insufficient key size.
@@ -262,3 +395,9 @@ predicate hasInsufficientKeySize(Expr e, string msg) {
hasShortRsaKeyPair(e, msg) or
hasShortECKeyPair(e, msg)
}
+
+predicate hasInsufficientKeySize2(DataFlow::PathNode source, DataFlow::PathNode sink) {
+ exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink))
+ or
+ exists(SymmetricKeyTrackingConfiguration2 config2 | config2.hasFlowPath(source, sink))
+}
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery_BeforeAddingTaintFlowBackForAsymmetric.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery_BeforeAddingTaintFlowBackForAsymmetric.qll
new file mode 100644
index 00000000000..720ed112981
--- /dev/null
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery_BeforeAddingTaintFlowBackForAsymmetric.qll
@@ -0,0 +1,264 @@
+import semmle.code.java.security.Encryption
+import semmle.code.java.dataflow.TaintTracking
+import semmle.code.java.dataflow.DataFlow
+
+//import DataFlow::PathGraph
+/**
+ * Asymmetric (RSA, DSA, DH) key length data flow tracking configuration.
+ */
+class AsymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
+ AsymmetricKeyTrackingConfiguration() { this = "AsymmetricKeyTrackingConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) {
+ exists(IntegerLiteral integer, VarAccess var |
+ integer.getIntValue() < 2048 and
+ source.asExpr() = integer
+ or
+ var.getVariable().getInitializer().getUnderlyingExpr() instanceof IntegerLiteral and
+ var.getVariable().getInitializer().getUnderlyingExpr().toString().toInt() < 2048 and
+ source.asExpr() = var.getVariable().getInitializer()
+ )
+ }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(MethodAccess ma |
+ ma.getMethod() instanceof KeyPairGeneratorInitMethod and
+ sink.asExpr() = ma.getArgument(0)
+ )
+ }
+}
+
+/** The Java class `java.security.spec.ECGenParameterSpec`. */
+private class ECGenParameterSpec extends RefType {
+ ECGenParameterSpec() { this.hasQualifiedName("java.security.spec", "ECGenParameterSpec") }
+}
+
+/** The `init` method declared in `javax.crypto.KeyGenerator`. */
+private class KeyGeneratorInitMethod extends Method {
+ KeyGeneratorInitMethod() {
+ this.getDeclaringType() instanceof KeyGenerator and
+ this.hasName("init")
+ }
+}
+
+/** The `initialize` method declared in `java.security.KeyPairGenerator`. */
+private class KeyPairGeneratorInitMethod extends Method {
+ KeyPairGeneratorInitMethod() {
+ this.getDeclaringType() instanceof KeyPairGenerator and
+ this.hasName("initialize")
+ }
+}
+
+/** Returns the key size in the EC algorithm string */
+bindingset[algorithm]
+private int getECKeySize(string algorithm) {
+ algorithm.matches("sec%") and // specification such as "secp256r1"
+ result = algorithm.regexpCapture("sec[p|t](\\d+)[a-zA-Z].*", 1).toInt()
+ or
+ algorithm.matches("X9.62%") and //specification such as "X9.62 prime192v2"
+ result = algorithm.regexpCapture("X9\\.62 .*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
+ or
+ (algorithm.matches("prime%") or algorithm.matches("c2tnb%")) and //specification such as "prime192v2"
+ result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
+}
+
+/** Taint configuration tracking flow from a key generator to a `init` method call. */
+private class KeyGeneratorInitConfiguration extends TaintTracking::Configuration {
+ KeyGeneratorInitConfiguration() { this = "KeyGeneratorInitConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) {
+ source.asExpr() instanceof JavaxCryptoKeyGenerator
+ }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(MethodAccess ma |
+ ma.getMethod() instanceof KeyGeneratorInitMethod and
+ sink.asExpr() = ma.getQualifier()
+ )
+ }
+}
+
+/**
+ * Taint configuration tracking flow from a keypair generator to
+ * an `initialize` method call.
+ */
+private class KeyPairGeneratorInitConfiguration extends TaintTracking::Configuration {
+ KeyPairGeneratorInitConfiguration() { this = "KeyPairGeneratorInitConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) {
+ source.asExpr() instanceof JavaSecurityKeyPairGenerator
+ }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(MethodAccess ma |
+ ma.getMethod() instanceof KeyPairGeneratorInitMethod and
+ sink.asExpr() = ma.getQualifier()
+ )
+ }
+}
+
+/**
+ * Holds if a symmetric `KeyGenerator` implementing encryption algorithm
+ * `type` and initialized by `ma` uses an insufficient key size.
+ *
+ * `msg` provides a human-readable description of the problem.
+ */
+bindingset[type]
+private predicate hasShortSymmetricKey(MethodAccess ma, string msg, string type) {
+ ma.getMethod() instanceof KeyGeneratorInitMethod and
+ // flow needed to correctly determine algorithm type and
+ // not match to ANY symmetric algorithm (although doesn't really matter since only have AES currently...)
+ exists(
+ JavaxCryptoKeyGenerator jcg, KeyGeneratorInitConfiguration cc, DataFlow::PathNode source,
+ DataFlow::PathNode dest
+ |
+ jcg.getAlgoSpec().(StringLiteral).getValue() = type and
+ source.getNode().asExpr() = jcg and
+ dest.getNode().asExpr() = ma.getQualifier() and
+ cc.hasFlowPath(source, dest)
+ ) and
+ (
+ // VarAccess case needed to handle FN of key-size stored in a variable
+ // Note: cannot use CompileTimeConstantExpr since will miss cases when variable is not a compile-time constant
+ // (e.g. not declared `final` in Java)
+ exists(VarAccess var |
+ var.getVariable().getInitializer().getUnderlyingExpr() instanceof IntegerLiteral and
+ var.getVariable().getInitializer().getUnderlyingExpr().toString().toInt() < 128 and
+ ma.getArgument(0) = var
+ )
+ or
+ // exists(CompileTimeConstantExpr var |
+ // //var.getUnderlyingExpr() instanceof IntegerLiteral and // can't include this...
+ // var.getIntValue() < 128 and
+ // ma.getArgument(0) = var
+ // )
+ // or
+ ma.getArgument(0).(IntegerLiteral).getIntValue() < 128
+ ) and
+ msg = "Key size should be at least 128 bits for " + type + " encryption."
+}
+
+/**
+ * Holds if an AES `KeyGenerator` initialized by `ma` uses an insufficient key size.
+ * `msg` provides a human-readable description of the problem.
+ */
+private predicate hasShortAESKey(MethodAccess ma, string msg) {
+ hasShortSymmetricKey(ma, msg, "AES")
+}
+
+/**
+ * Holds if an asymmetric `KeyPairGenerator` implementing encryption algorithm
+ * `type` and initialized by `ma` uses an insufficient key size.
+ *
+ * `msg` provides a human-readable description of the problem.
+ */
+bindingset[type]
+private predicate hasShortAsymmetricKeyPair(MethodAccess ma, string msg, string type) {
+ ma.getMethod() instanceof KeyPairGeneratorInitMethod and
+ //ma.getQualifier() instanceof JavaSecurityKeyPairGenerator and
+ //ma.getQualifier().getBasicBlock() instanceof JavaSecurityKeyPairGenerator and
+ // * USE BELOW
+ ma.getQualifier().getBasicBlock().getAPredecessor() instanceof JavaSecurityKeyPairGenerator and
+ // * USE ABOVE
+ //ma.getQualifier().getBasicBlock().getNode(2) instanceof JavaSecurityKeyPairGenerator and
+ // ma.getQualifier()
+ // .getBasicBlock()
+ // .getANode()
+ // .(JavaSecurityKeyPairGenerator)
+ // .getAlgoSpec()
+ // .(StringLiteral)
+ // .getValue()
+ // .toUpperCase() = type and
+ //ma.getQualifier().getBasicBlock().getAPredecessor() instanceof JavaSecurityKeyPairGenerator and
+ // * USE BELOW
+ ma.getQualifier()
+ .getBasicBlock()
+ .getAPredecessor()
+ .(JavaSecurityKeyPairGenerator)
+ .getAlgoSpec()
+ .(StringLiteral)
+ .getValue()
+ .toUpperCase() = type and
+ // * USE ABOVE
+ // flow needed to correctly determine algorithm type and
+ // not match to ANY asymmetric algorithm
+ // * REMOVE BELOW
+ // exists(
+ // JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kc,
+ // DataFlow::PathNode source, DataFlow::PathNode dest
+ // |
+ // jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase() = type and
+ // source.getNode().asExpr() = jpg and
+ // dest.getNode().asExpr() = ma.getQualifier() and
+ // kc.hasFlowPath(source, dest)
+ // ) and
+ // * REMOVE ABOVE
+ // VarAccess case needed to handle FN of key-size stored in a variable
+ // Note: cannot use CompileTimeConstantExpr since will miss cases when variable is not a compile-time constant
+ // (e.g. not declared `final` in Java)
+ (
+ exists(VarAccess var |
+ var.getVariable().getInitializer().getUnderlyingExpr() instanceof IntegerLiteral and
+ var.getVariable().getInitializer().getUnderlyingExpr().toString().toInt() < 2048 and
+ ma.getArgument(0) = var
+ )
+ or
+ ma.getArgument(0).(IntegerLiteral).getIntValue() < 2048
+ // or
+ // exists(
+ // AsymmetricKeyTrackingConfiguration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
+ // |
+ // cfg.hasFlowPath(source, sink)
+ // )
+ ) and
+ msg = "Key size should be at least 2048 bits for " + type + " encryption."
+}
+
+/**
+ * Holds if a DSA `KeyPairGenerator` initialized by `ma` uses an insufficient key size.
+ *
+ * `msg` provides a human-readable description of the problem.
+ */
+private predicate hasShortDsaKeyPair(MethodAccess ma, string msg) {
+ hasShortAsymmetricKeyPair(ma, msg, "DSA") or
+ hasShortAsymmetricKeyPair(ma, msg, "DH")
+}
+
+/**
+ * Holds if a RSA `KeyPairGenerator` initialized by `ma` uses an insufficient key size.
+ *
+ * `msg` provides a human-readable description of the problem.
+ */
+private predicate hasShortRsaKeyPair(MethodAccess ma, string msg) {
+ hasShortAsymmetricKeyPair(ma, msg, "RSA")
+}
+
+/**
+ * Holds if an EC `KeyPairGenerator` initialized by `ma` uses an insufficient key size.
+ *
+ * `msg` provides a human-readable description of the problem.
+ */
+private predicate hasShortECKeyPair(MethodAccess ma, string msg) {
+ ma.getMethod() instanceof KeyPairGeneratorInitMethod and
+ exists(
+ JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kc,
+ DataFlow::PathNode source, DataFlow::PathNode dest, ClassInstanceExpr cie
+ |
+ jpg.getAlgoSpec().(StringLiteral).getValue().matches("EC%") and // ECC variants such as ECDH and ECDSA
+ source.getNode().asExpr() = jpg and
+ dest.getNode().asExpr() = ma.getQualifier() and
+ kc.hasFlowPath(source, dest) and
+ DataFlow::localExprFlow(cie, ma.getArgument(0)) and
+ ma.getArgument(0).getType() instanceof ECGenParameterSpec and
+ getECKeySize(cie.getArgument(0).(StringLiteral).getValue()) < 256
+ ) and
+ msg = "Key size should be at least 256 bits for EC encryption."
+}
+
+// ! refactor this so can use 'path-problem' select clause instead?
+predicate hasInsufficientKeySize(Expr e, string msg) {
+ hasShortAESKey(e, msg) or
+ hasShortDsaKeyPair(e, msg) or
+ hasShortRsaKeyPair(e, msg) or
+ hasShortECKeyPair(e, msg)
+}
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
index 66bf459f5f7..2979c9116c4 100644
--- a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
+++ b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
@@ -18,7 +18,17 @@ import DataFlow::PathGraph
// from Expr e, string msg
// where hasInsufficientKeySize(e, msg)
// select e, msg
-from AsymmetricKeyTrackingConfiguration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
-where cfg.hasFlowPath(source, sink)
-select sink.getNode(), source, sink, "The $@ of an asymmetric key should be at least 2048 bits.",
- sink.getNode(), "size"
+// from
+// AsymmetricKeyTrackingConfiguration cfg, DataFlow::PathNode source, DataFlow::PathNode sink,
+// KeyTrackingConfiguration cfg2 //, DataFlow::PathNode source2, DataFlow::PathNode sink2
+// where
+// //cfg.hasFlowPath(source, sink) //or
+// cfg2.hasFlowPath(source, sink)
+// select sink.getNode(), source, sink, "The $@ of an asymmetric key should be at least 2048 bits.",
+// sink.getNode(), "size"
+from DataFlow::PathNode source, DataFlow::PathNode sink
+where
+ //hasInsufficientKeySize2(source, sink)
+ exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink)) or
+ exists(SymmetricKeyTrackingConfiguration2 config2 | config2.hasFlowPath(source, sink))
+select sink.getNode(), source, sink, "This $@ is too small.", sink.getNode(), "key size"
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
index 9d9f5688520..68c3e7f322e 100644
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
@@ -3,115 +3,192 @@ import java.security.spec.ECGenParameterSpec;
import javax.crypto.KeyGenerator;
public class InsufficientKeySizeTest {
- public void cryptoMethod() throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
- KeyGenerator keyGen1 = KeyGenerator.getInstance("AES");
- // BAD: Key size is less than 128
- keyGen1.init(64); // $ hasInsufficientKeySize
+ public void keySizeTesting() throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
- KeyGenerator keyGen2 = KeyGenerator.getInstance("AES");
- // GOOD: Key size is no less than 128
- keyGen2.init(128); // Safe
+ // Test basic key generation for all algos
- KeyPairGenerator keyPairGen1 = KeyPairGenerator.getInstance("RSA");
- // BAD: Key size is less than 2048
- keyPairGen1.initialize(1024); // $ hasInsufficientKeySize
+ // AES (Symmetric)
+ {
+ // BAD: Key size is less than 128
+ KeyGenerator keyGen1 = KeyGenerator.getInstance("AES");
+ keyGen1.init(64); // $ hasInsufficientKeySize
- KeyPairGenerator keyPairGen2 = KeyPairGenerator.getInstance("RSA");
- // GOOD: Key size is no less than 2048
- keyPairGen2.initialize(2048); // Safe
+ // GOOD: Key size is no less than 128
+ KeyGenerator keyGen2 = KeyGenerator.getInstance("AES");
+ keyGen2.init(128); // Safe
+ }
- KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("DSA");
- // BAD: Key size is less than 2048
- keyPairGen3.initialize(1024); // $ hasInsufficientKeySize
+ // RSA (Asymmetric)
+ {
+ // BAD: Key size is less than 2048
+ KeyPairGenerator keyPairGen1 = KeyPairGenerator.getInstance("RSA");
+ keyPairGen1.initialize(1024); // $ hasInsufficientKeySize
- KeyPairGenerator keyPairGen4 = KeyPairGenerator.getInstance("DSA");
- // GOOD: Key size is no less than 2048
- keyPairGen4.initialize(2048); // Safe
+ // GOOD: Key size is no less than 2048
+ KeyPairGenerator keyPairGen2 = KeyPairGenerator.getInstance("RSA");
+ keyPairGen2.initialize(2048); // Safe
+ }
- KeyPairGenerator keyPairGen5 = KeyPairGenerator.getInstance("EC");
- // BAD: Key size is less than 256
- ECGenParameterSpec ecSpec1 = new ECGenParameterSpec("secp112r1");
- keyPairGen5.initialize(ecSpec1); // $ hasInsufficientKeySize
+ // DSA (Asymmetric)
+ {
+ // BAD: Key size is less than 2048
+ KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("DSA");
+ keyPairGen3.initialize(1024); // $ hasInsufficientKeySize
- KeyPairGenerator keyPairGen6 = KeyPairGenerator.getInstance("EC");
- // BAD: Key size is less than 256
- keyPairGen6.initialize(new ECGenParameterSpec("secp112r1")); // $ hasInsufficientKeySize
+ // GOOD: Key size is no less than 2048
+ KeyPairGenerator keyPairGen4 = KeyPairGenerator.getInstance("DSA");
+ keyPairGen4.initialize(2048); // Safe
+ }
- KeyPairGenerator keyPairGen7 = KeyPairGenerator.getInstance("EC");
- // GOOD: Key size is no less than 256
- ECGenParameterSpec ecSpec2 = new ECGenParameterSpec("secp256r1");
- keyPairGen7.initialize(ecSpec2); // Safe
+ // DH (Asymmetric)
+ {
+ // BAD: Key size is less than 2048
+ KeyPairGenerator keyPairGen16 = KeyPairGenerator.getInstance("dh");
+ keyPairGen16.initialize(1024); // $ hasInsufficientKeySize
- KeyPairGenerator keyPairGen8 = KeyPairGenerator.getInstance("EC");
- // BAD: Key size is less than 256
- ECGenParameterSpec ecSpec3 = new ECGenParameterSpec("X9.62 prime192v2");
- keyPairGen8.initialize(ecSpec3); // $ hasInsufficientKeySize
+ // GOOD: Key size is no less than 2048
+ KeyPairGenerator keyPairGen17 = KeyPairGenerator.getInstance("DH");
+ keyPairGen17.initialize(2048); // Safe
+ }
- KeyPairGenerator keyPairGen9 = KeyPairGenerator.getInstance("EC");
- // BAD: Key size is less than 256
- ECGenParameterSpec ecSpec4 = new ECGenParameterSpec("X9.62 c2tnb191v3");
- keyPairGen9.initialize(ecSpec4); // $ hasInsufficientKeySize
+ // EC (Asymmetric)
+ // ! Check if I can re-use the same KeyPairGenerator instance with all of the below?
+ {
+ // BAD: Key size is less than 256
+ KeyPairGenerator keyPairGen5 = KeyPairGenerator.getInstance("EC");
+ ECGenParameterSpec ecSpec1 = new ECGenParameterSpec("secp112r1");
+ keyPairGen5.initialize(ecSpec1); // $ hasInsufficientKeySize
- KeyPairGenerator keyPairGen10 = KeyPairGenerator.getInstance("EC");
- // BAD: Key size is less than 256
- ECGenParameterSpec ecSpec5 = new ECGenParameterSpec("sect163k1");
- keyPairGen10.initialize(ecSpec5); // $ hasInsufficientKeySize
+ // BAD: Key size is less than 256
+ KeyPairGenerator keyPairGen6 = KeyPairGenerator.getInstance("EC");
+ keyPairGen6.initialize(new ECGenParameterSpec("secp112r1")); // $ hasInsufficientKeySize
- KeyPairGenerator keyPairGen11 = KeyPairGenerator.getInstance("EC");
- // GOOD: Key size is no less than 256
- ECGenParameterSpec ecSpec6 = new ECGenParameterSpec("X9.62 c2tnb359v1");
- keyPairGen11.initialize(ecSpec6); // Safe
+ // GOOD: Key size is no less than 256
+ KeyPairGenerator keyPairGen7 = KeyPairGenerator.getInstance("EC");
+ ECGenParameterSpec ecSpec2 = new ECGenParameterSpec("secp256r1");
+ keyPairGen7.initialize(ecSpec2); // Safe
- KeyPairGenerator keyPairGen12 = KeyPairGenerator.getInstance("EC");
- // BAD: Key size is less than 256
- ECGenParameterSpec ecSpec7 = new ECGenParameterSpec("prime192v2");
- keyPairGen12.initialize(ecSpec7); // $ hasInsufficientKeySize
+ // BAD: Key size is less than 256
+ KeyPairGenerator keyPairGen8 = KeyPairGenerator.getInstance("EC");
+ ECGenParameterSpec ecSpec3 = new ECGenParameterSpec("X9.62 prime192v2");
+ keyPairGen8.initialize(ecSpec3); // $ hasInsufficientKeySize
- KeyPairGenerator keyPairGen13 = KeyPairGenerator.getInstance("EC");
- // BAD: Key size is no less than 256 // ! I think this comment is wrong - double-check
- ECGenParameterSpec ecSpec8 = new ECGenParameterSpec("prime256v1");
- keyPairGen13.initialize(ecSpec8); // Safe
+ // BAD: Key size is less than 256
+ KeyPairGenerator keyPairGen9 = KeyPairGenerator.getInstance("EC");
+ ECGenParameterSpec ecSpec4 = new ECGenParameterSpec("X9.62 c2tnb191v3");
+ keyPairGen9.initialize(ecSpec4); // $ hasInsufficientKeySize
- KeyPairGenerator keyPairGen14 = KeyPairGenerator.getInstance("EC");
- // BAD: Key size is less than 256
- ECGenParameterSpec ecSpec9 = new ECGenParameterSpec("c2tnb191v1");
- keyPairGen14.initialize(ecSpec9); // $ hasInsufficientKeySize
+ // BAD: Key size is less than 256
+ KeyPairGenerator keyPairGen10 = KeyPairGenerator.getInstance("EC");
+ ECGenParameterSpec ecSpec5 = new ECGenParameterSpec("sect163k1");
+ keyPairGen10.initialize(ecSpec5); // $ hasInsufficientKeySize
- KeyPairGenerator keyPairGen15 = KeyPairGenerator.getInstance("EC");
- // BAD: Key size is no less than 256 // ! I think this comment is wrong - double-check
- ECGenParameterSpec ecSpec10 = new ECGenParameterSpec("c2tnb431r1");
- keyPairGen15.initialize(ecSpec10); // Safe
+ // GOOD: Key size is no less than 256
+ KeyPairGenerator keyPairGen11 = KeyPairGenerator.getInstance("EC");
+ ECGenParameterSpec ecSpec6 = new ECGenParameterSpec("X9.62 c2tnb359v1");
+ keyPairGen11.initialize(ecSpec6); // Safe
- KeyPairGenerator keyPairGen16 = KeyPairGenerator.getInstance("dh");
- // BAD: Key size is less than 2048
- keyPairGen16.initialize(1024); // $ hasInsufficientKeySize
+ // BAD: Key size is less than 256
+ KeyPairGenerator keyPairGen12 = KeyPairGenerator.getInstance("EC");
+ ECGenParameterSpec ecSpec7 = new ECGenParameterSpec("prime192v2");
+ keyPairGen12.initialize(ecSpec7); // $ hasInsufficientKeySize
- KeyPairGenerator keyPairGen17 = KeyPairGenerator.getInstance("DH");
- // GOOD: Key size is no less than 2048
- keyPairGen17.initialize(2048); // Safe
+ // GOOD: Key size is no less than 256
+ KeyPairGenerator keyPairGen13 = KeyPairGenerator.getInstance("EC");
+ ECGenParameterSpec ecSpec8 = new ECGenParameterSpec("prime256v1");
+ keyPairGen13.initialize(ecSpec8); // Safe
+
+ // BAD: Key size is less than 256
+ KeyPairGenerator keyPairGen14 = KeyPairGenerator.getInstance("EC");
+ ECGenParameterSpec ecSpec9 = new ECGenParameterSpec("c2tnb191v1");
+ keyPairGen14.initialize(ecSpec9); // $ hasInsufficientKeySize
+
+ // GOOD: Key size is no less than 256
+ KeyPairGenerator keyPairGen15 = KeyPairGenerator.getInstance("EC");
+ ECGenParameterSpec ecSpec10 = new ECGenParameterSpec("c2tnb431r1");
+ keyPairGen15.initialize(ecSpec10); // Safe
+ }
+
+ // ! FN Testing Additions:
+
+ // Test local variable usage - Symmetric
+ {
+ final int size1 = 64; // compile-time constant
+ int size2 = 64; // NOT a compile-time constant
+
+ // BAD: Key size is less than 128
+ KeyGenerator keyGen3 = KeyGenerator.getInstance("AES");
+ keyGen3.init(size1); // $ hasInsufficientKeySize
+
+ // BAD: Key size is less than 128
+ KeyGenerator keyGen4 = KeyGenerator.getInstance("AES");
+ keyGen4.init(size2); // $ hasInsufficientKeySize
+ }
+
+ // Test local variable usage - Asymmetric, Not EC
+ {
+ final int size1 = 1024; // compile-time constant
+ int size2 = 1024; // NOT a compile-time constant
+
+ // BAD: Key size is less than 2048
+ KeyPairGenerator keyPairGen18 = KeyPairGenerator.getInstance("RSA");
+ keyPairGen18.initialize(size1); // $ hasInsufficientKeySize
+
+ // BAD: Key size is less than 2048
+ KeyPairGenerator keyPairGen19 = KeyPairGenerator.getInstance("RSA");
+ keyPairGen19.initialize(size2); // $ hasInsufficientKeySize
+ }
- // FN: Test with variables as numbers
- final int size1 = 64;
- KeyGenerator keyGen3 = KeyGenerator.getInstance("AES");
- // BAD: Key size is less than 128
- keyGen3.init(size1); // $ hasInsufficientKeySize
+ // Test variable passed to other method(s) - Symmetric
+ {
+ int size = 64; // test integer variable
+ KeyGenerator keyGen = KeyGenerator.getInstance("AES"); // test KeyGenerator variable
+ testSymmetric(size, keyGen);
+ }
- int size2 = 1024;
- KeyPairGenerator keyPairGen18 = KeyPairGenerator.getInstance("RSA");
- // BAD: Key size is less than 2048
- keyPairGen18.initialize(size2); // $ hasInsufficientKeySize
- int keysize = 1024;
- KeyPairGenerator keyPairGen20 = KeyPairGenerator.getInstance("DSA");
- test(keysize, keyPairGen20);
+ // Test variables passed to other method(s) - Asymmetric, Not EC
+ {
+ int size = 1024; // test integer variable
+ KeyPairGenerator keyPairGen21 = KeyPairGenerator.getInstance("RSA"); // test KeyPairGenerator variable
+ testAsymmetricNonEC(size, keyPairGen21);
+ }
+
+ // Test variable passed to other method(s) - Asymmetric, EC
+ {
+ ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp112r1"); // test ECGenParameterSpec variable
+ KeyPairGenerator keyPairGen22 = KeyPairGenerator.getInstance("EC"); // test KeyPairGenerator variable
+ testAsymmetricEC(ecSpec, keyPairGen22);
+ }
+
}
- public static void test(int keySize, KeyPairGenerator kpg) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
- KeyPairGenerator keyPairGen19 = KeyPairGenerator.getInstance("RSA");
- // BAD: Key size is less than 128
- keyPairGen19.initialize(keySize); // $ hasInsufficientKeySize
+ public static void testSymmetric(int keySize, KeyGenerator kg) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
+ // BAD: Key size is less than 2048
+ KeyGenerator keyGen = KeyGenerator.getInstance("AES");
+ keyGen.init(keySize); // $ hasInsufficientKeySize
+ // BAD: Key size is less than 2048
+ kg.init(64); // $ hasInsufficientKeySize
+ }
+
+ public static void testAsymmetricNonEC(int keySize, KeyPairGenerator kpg) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
+ // BAD: Key size is less than 2048
+ KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
+ keyPairGen.initialize(keySize); // $ hasInsufficientKeySize
+
+ // BAD: Key size is less than 2048
kpg.initialize(1024); // $ hasInsufficientKeySize
}
+
+ public static void testAsymmetricEC(ECGenParameterSpec spec, KeyPairGenerator kpg) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
+ // BAD: Key size is less than 256
+ KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("EC");
+ keyPairGen.initialize(spec); // $ hasInsufficientKeySize
+
+ // BAD: Key size is less than 256
+ ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp112r1");
+ kpg.initialize(ecSpec); // $ hasInsufficientKeySize
+ }
}
From c414ee0e254d0a303a6fe187ef7381ea8b60ec57 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Thu, 6 Oct 2022 01:32:10 -0400
Subject: [PATCH 012/249] add ECC dataflow config; passes all test cases; still
don't have algo name tracking
---
.../security/InsufficientKeySizeQuery.qll | 337 +++---------------
.../CWE/CWE-326/InsufficientKeySize.ql | 3 +-
.../CWE-326/InsufficientKeySizeTest.java | 20 +-
.../CWE-326/InsufficientKeySizeTest.ql | 10 +-
4 files changed, 74 insertions(+), 296 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
index 869f3c7cb0f..4cf274f3263 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
@@ -2,7 +2,7 @@ import semmle.code.java.security.Encryption
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.DataFlow
-// ******* DATAFLOW *******************************************************************************
+// ******* DATAFLOW BELOW *************************************************************************
/**
* Asymmetric (RSA, DSA, DH) key length data flow tracking configuration.
*/
@@ -10,16 +10,8 @@ class AsymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
AsymmetricKeyTrackingConfiguration() { this = "AsymmetricKeyTrackingConfiguration" }
override predicate isSource(DataFlow::Node source) {
- exists(IntegerLiteral integer, VarAccess var |
- integer.getIntValue() < 2048 and
- source.asExpr() = integer
- or
- // The below only handles cases when variables are used (both locally in a method and between methods)
- // The above adds handling for direct use of integers as well
- var.getVariable().getInitializer().getUnderlyingExpr() instanceof IntegerLiteral and
- var.getVariable().getInitializer().getUnderlyingExpr().toString().toInt() < 2048 and
- source.asExpr() = var.getVariable().getInitializer()
- )
+ source.asExpr() instanceof IntegerLiteral and // ! this works with current test cases, but reconsider IntegerLiteral when variables are used
+ source.toString().toInt() < 2048
}
override predicate isSink(DataFlow::Node sink) {
@@ -31,27 +23,22 @@ class AsymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
}
/**
- * Symmetric (AES) key length data flow tracking configuration.
+ * Asymmetric (RSA, DSA, DH) key length data flow tracking configuration.
*/
-class SymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
- SymmetricKeyTrackingConfiguration() { this = "SymmetricKeyTrackingConfiguration" }
+class AsymmetricECCKeyTrackingConfiguration extends DataFlow::Configuration {
+ AsymmetricECCKeyTrackingConfiguration() { this = "AsymmetricECCKeyTrackingConfiguration" }
override predicate isSource(DataFlow::Node source) {
- exists(IntegerLiteral integer, VarAccess var |
- integer.getIntValue() < 128 and
- source.asExpr() = integer
- or
- // The below only handles cases when variables are used (both locally in a method and between methods)
- // The above adds handling for direct use of integers as well
- var.getVariable().getInitializer().getUnderlyingExpr() instanceof IntegerLiteral and
- var.getVariable().getInitializer().getUnderlyingExpr().toString().toInt() < 128 and
- source.asExpr() = var.getVariable().getInitializer()
+ exists(ClassInstanceExpr ecGenParamSpec |
+ getECKeySize(ecGenParamSpec.getArgument(0).(StringLiteral).getValue()) < 256 and
+ source.asExpr() = ecGenParamSpec
)
}
override predicate isSink(DataFlow::Node sink) {
exists(MethodAccess ma |
- ma.getMethod() instanceof KeyGeneratorInitMethod and
+ ma.getMethod() instanceof KeyPairGeneratorInitMethod and
+ ma.getArgument(0).getType() instanceof ECGenParameterSpec and
sink.asExpr() = ma.getArgument(0)
)
}
@@ -60,11 +47,11 @@ class SymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
/**
* Symmetric (AES) key length data flow tracking configuration.
*/
-class SymmetricKeyTrackingConfiguration2 extends DataFlow::Configuration {
- SymmetricKeyTrackingConfiguration2() { this = "SymmetricKeyTrackingConfiguration2" }
+class SymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
+ SymmetricKeyTrackingConfiguration() { this = "SymmetricKeyTrackingConfiguration2" }
override predicate isSource(DataFlow::Node source) {
- source.asExpr() instanceof IntegerLiteral and
+ source.asExpr() instanceof IntegerLiteral and // ! this works with current test cases, but reconsider IntegerLiteral when variables are used
source.toString().toInt() < 128
}
@@ -76,54 +63,34 @@ class SymmetricKeyTrackingConfiguration2 extends DataFlow::Configuration {
}
}
-class UnsafeSymmetricKeySize extends IntegerLiteral {
- UnsafeSymmetricKeySize() { this.getIntValue() < 128 }
+// ! below doesn't work for some reason...
+predicate hasInsufficientKeySize2(DataFlow::PathNode source, DataFlow::PathNode sink) {
+ exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink))
+ or
+ exists(SymmetricKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink))
}
-class UnsafeAsymmetricKeySize extends IntegerLiteral {
- UnsafeAsymmetricKeySize() { this.getIntValue() < 2048 }
-}
-
-class UnsafeKeySize extends IntegerLiteral {
- UnsafeKeySize() {
- this instanceof UnsafeAsymmetricKeySize and
- exists(MethodAccess ma | ma.getMethod() instanceof KeyPairGeneratorInitMethod)
- or
- this instanceof UnsafeSymmetricKeySize and
- exists(MethodAccess ma | ma.getMethod() instanceof KeyGeneratorInitMethod)
- }
-}
-
-class KeyInitMethod extends Method {
- KeyInitMethod() {
- this instanceof KeyGeneratorInitMethod or
- this instanceof KeyPairGeneratorInitMethod
- }
-}
-
-/**
- * key length data flow tracking configuration.
- */
-class KeyTrackingConfiguration extends DataFlow::Configuration {
- KeyTrackingConfiguration() { this = "KeyTrackingConfiguration" }
-
- override predicate isSource(DataFlow::Node source) { source.asExpr() instanceof UnsafeKeySize }
-
- override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma |
- ma.getMethod() instanceof KeyInitMethod and
- sink.asExpr() = ma.getArgument(0)
- )
- }
-}
-
-// ******* DATAFLOW *******************************************************************************
+// ******** Need the below for the above ********
// ! move to Encryption.qll?
/** The Java class `java.security.spec.ECGenParameterSpec`. */
private class ECGenParameterSpec extends RefType {
ECGenParameterSpec() { this.hasQualifiedName("java.security.spec", "ECGenParameterSpec") }
}
+// ! move to Encryption.qll?
+/** Returns the key size in the EC algorithm string */
+bindingset[algorithm]
+private int getECKeySize(string algorithm) {
+ algorithm.matches("sec%") and // specification such as "secp256r1"
+ result = algorithm.regexpCapture("sec[p|t](\\d+)[a-zA-Z].*", 1).toInt()
+ or
+ algorithm.matches("X9.62%") and //specification such as "X9.62 prime192v2"
+ result = algorithm.regexpCapture("X9\\.62 .*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
+ or
+ (algorithm.matches("prime%") or algorithm.matches("c2tnb%")) and //specification such as "prime192v2"
+ result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
+}
+
// ! move to Encryption.qll?
/** The `init` method declared in `javax.crypto.KeyGenerator`. */
private class KeyGeneratorInitMethod extends Method {
@@ -142,17 +109,19 @@ private class KeyPairGeneratorInitMethod extends Method {
}
}
-/** Returns the key size in the EC algorithm string */
-bindingset[algorithm]
-private int getECKeySize(string algorithm) {
- algorithm.matches("sec%") and // specification such as "secp256r1"
- result = algorithm.regexpCapture("sec[p|t](\\d+)[a-zA-Z].*", 1).toInt()
- or
- algorithm.matches("X9.62%") and //specification such as "X9.62 prime192v2"
- result = algorithm.regexpCapture("X9\\.62 .*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
- or
- (algorithm.matches("prime%") or algorithm.matches("c2tnb%")) and //specification such as "prime192v2"
- result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
+// ******* DATAFLOW ABOVE *************************************************************************
+// ************************************************************************************************
+// ************************************************************************************************
+// ************************************************************************************************
+// ************************************************************************************************
+// ************************************************************************************************
+// ******* OLD/UNUSED OR EXPERIMENTAL CODE BELOW **************************************************
+class UnsafeSymmetricKeySize extends IntegerLiteral {
+ UnsafeSymmetricKeySize() { this.getIntValue() < 128 }
+}
+
+class UnsafeAsymmetricKeySize extends IntegerLiteral {
+ UnsafeAsymmetricKeySize() { this.getIntValue() < 2048 }
}
/** Taint configuration tracking flow from a key generator to a `init` method call. */
@@ -189,215 +158,3 @@ private class KeyPairGeneratorInitConfiguration extends TaintTracking::Configura
)
}
}
-
-/**
- * Holds if a symmetric `KeyGenerator` implementing encryption algorithm
- * `type` and initialized by `ma` uses an insufficient key size.
- *
- * `msg` provides a human-readable description of the problem.
- */
-//bindingset[type]
-private predicate hasShortSymmetricKey_TEST() {
- exists(
- SymmetricKeyTrackingConfiguration2 cfg, DataFlow::PathNode source, DataFlow::PathNode sink
- |
- cfg.hasFlowPath(source, sink)
- )
- // ma.getMethod() instanceof KeyGeneratorInitMethod and
- // // flow needed to correctly determine algorithm type and
- // // not match to ANY symmetric algorithm (although doesn't really matter since only have AES currently...)
- // exists(
- // JavaxCryptoKeyGenerator jcg, KeyGeneratorInitConfiguration cc, DataFlow::PathNode source,
- // DataFlow::PathNode dest
- // |
- // jcg.getAlgoSpec().(StringLiteral).getValue() = type and
- // source.getNode().asExpr() = jcg and
- // dest.getNode().asExpr() = ma.getQualifier() and
- // cc.hasFlowPath(source, dest)
- // ) and
- // (
- // // VarAccess case needed to handle FN of key-size stored in a variable
- // // Note: cannot use CompileTimeConstantExpr since will miss cases when variable is not a compile-time constant
- // // (e.g. not declared `final` in Java)
- // exists(VarAccess var |
- // var.getVariable().getInitializer().getUnderlyingExpr() instanceof IntegerLiteral and
- // var.getVariable().getInitializer().getUnderlyingExpr().toString().toInt() < 128 and
- // ma.getArgument(0) = var
- // )
- // or
- // ma.getArgument(0).(IntegerLiteral).getIntValue() < 128
- // ) and
- // msg = "Key size should be at least 128 bits for " + type + " encryption."
-}
-
-/**
- * Holds if a symmetric `KeyGenerator` implementing encryption algorithm
- * `type` and initialized by `ma` uses an insufficient key size.
- *
- * `msg` provides a human-readable description of the problem.
- */
-bindingset[type]
-private predicate hasShortSymmetricKey(MethodAccess ma, string msg, string type) {
- ma.getMethod() instanceof KeyGeneratorInitMethod and
- // flow needed to correctly determine algorithm type and
- // not match to ANY symmetric algorithm (although doesn't really matter since only have AES currently...)
- exists(
- JavaxCryptoKeyGenerator jcg, KeyGeneratorInitConfiguration cc, DataFlow::PathNode source,
- DataFlow::PathNode dest
- |
- jcg.getAlgoSpec().(StringLiteral).getValue() = type and
- source.getNode().asExpr() = jcg and
- dest.getNode().asExpr() = ma.getQualifier() and
- cc.hasFlowPath(source, dest)
- ) and
- (
- // VarAccess case needed to handle FN of key-size stored in a variable
- // Note: cannot use CompileTimeConstantExpr since will miss cases when variable is not a compile-time constant
- // (e.g. not declared `final` in Java)
- exists(VarAccess var |
- var.getVariable().getInitializer().getUnderlyingExpr() instanceof IntegerLiteral and
- var.getVariable().getInitializer().getUnderlyingExpr().toString().toInt() < 128 and
- ma.getArgument(0) = var
- )
- or
- // exists(CompileTimeConstantExpr var |
- // //var.getUnderlyingExpr() instanceof IntegerLiteral and // can't include this...
- // var.getIntValue() < 128 and
- // ma.getArgument(0) = var
- // )
- // or
- ma.getArgument(0).(IntegerLiteral).getIntValue() < 128
- ) and
- msg = "Key size should be at least 128 bits for " + type + " encryption."
-}
-
-/**
- * Holds if an AES `KeyGenerator` initialized by `ma` uses an insufficient key size.
- * `msg` provides a human-readable description of the problem.
- */
-private predicate hasShortAESKey(MethodAccess ma, string msg) {
- hasShortSymmetricKey(ma, msg, "AES")
-}
-
-/**
- * Holds if an asymmetric `KeyPairGenerator` implementing encryption algorithm
- * `type` and initialized by `ma` uses an insufficient key size.
- *
- * `msg` provides a human-readable description of the problem.
- */
-bindingset[type]
-private predicate hasShortAsymmetricKeyPair(MethodAccess ma, string msg, string type) {
- ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- //ma.getQualifier() instanceof JavaSecurityKeyPairGenerator and
- //ma.getQualifier().getBasicBlock() instanceof JavaSecurityKeyPairGenerator and
- // * USE BELOW
- ma.getQualifier().getBasicBlock().getAPredecessor() instanceof JavaSecurityKeyPairGenerator and
- // * USE ABOVE
- //ma.getQualifier().getBasicBlock().getNode(2) instanceof JavaSecurityKeyPairGenerator and
- // ma.getQualifier()
- // .getBasicBlock()
- // .getANode()
- // .(JavaSecurityKeyPairGenerator)
- // .getAlgoSpec()
- // .(StringLiteral)
- // .getValue()
- // .toUpperCase() = type and
- //ma.getQualifier().getBasicBlock().getAPredecessor() instanceof JavaSecurityKeyPairGenerator and
- // * USE BELOW
- ma.getQualifier()
- .getBasicBlock()
- .getAPredecessor()
- .(JavaSecurityKeyPairGenerator)
- .getAlgoSpec()
- .(StringLiteral)
- .getValue()
- .toUpperCase() = type and
- // * USE ABOVE
- // flow needed to correctly determine algorithm type and
- // not match to ANY asymmetric algorithm
- // * REMOVE BELOW
- // exists(
- // JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kc,
- // DataFlow::PathNode source, DataFlow::PathNode dest
- // |
- // jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase() = type and
- // source.getNode().asExpr() = jpg and
- // dest.getNode().asExpr() = ma.getQualifier() and
- // kc.hasFlowPath(source, dest)
- // ) and
- // * REMOVE ABOVE
- // VarAccess case needed to handle FN of key-size stored in a variable
- // Note: cannot use CompileTimeConstantExpr since will miss cases when variable is not a compile-time constant
- // (e.g. not declared `final` in Java)
- (
- exists(VarAccess var |
- var.getVariable().getInitializer().getUnderlyingExpr() instanceof IntegerLiteral and
- var.getVariable().getInitializer().getUnderlyingExpr().toString().toInt() < 2048 and
- ma.getArgument(0) = var
- )
- or
- ma.getArgument(0).(IntegerLiteral).getIntValue() < 2048
- // or
- // exists(
- // AsymmetricKeyTrackingConfiguration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
- // |
- // cfg.hasFlowPath(source, sink)
- // )
- ) and
- msg = "Key size should be at least 2048 bits for " + type + " encryption."
-}
-
-/**
- * Holds if a DSA `KeyPairGenerator` initialized by `ma` uses an insufficient key size.
- *
- * `msg` provides a human-readable description of the problem.
- */
-private predicate hasShortDsaKeyPair(MethodAccess ma, string msg) {
- hasShortAsymmetricKeyPair(ma, msg, "DSA") or
- hasShortAsymmetricKeyPair(ma, msg, "DH")
-}
-
-/**
- * Holds if a RSA `KeyPairGenerator` initialized by `ma` uses an insufficient key size.
- *
- * `msg` provides a human-readable description of the problem.
- */
-private predicate hasShortRsaKeyPair(MethodAccess ma, string msg) {
- hasShortAsymmetricKeyPair(ma, msg, "RSA")
-}
-
-/**
- * Holds if an EC `KeyPairGenerator` initialized by `ma` uses an insufficient key size.
- *
- * `msg` provides a human-readable description of the problem.
- */
-private predicate hasShortECKeyPair(MethodAccess ma, string msg) {
- ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- exists(
- JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kc,
- DataFlow::PathNode source, DataFlow::PathNode dest, ClassInstanceExpr cie
- |
- jpg.getAlgoSpec().(StringLiteral).getValue().matches("EC%") and // ECC variants such as ECDH and ECDSA
- source.getNode().asExpr() = jpg and
- dest.getNode().asExpr() = ma.getQualifier() and
- kc.hasFlowPath(source, dest) and
- DataFlow::localExprFlow(cie, ma.getArgument(0)) and
- ma.getArgument(0).getType() instanceof ECGenParameterSpec and
- getECKeySize(cie.getArgument(0).(StringLiteral).getValue()) < 256
- ) and
- msg = "Key size should be at least 256 bits for EC encryption."
-}
-
-// ! refactor this so can use 'path-problem' select clause instead?
-predicate hasInsufficientKeySize(Expr e, string msg) {
- hasShortAESKey(e, msg) or
- hasShortDsaKeyPair(e, msg) or
- hasShortRsaKeyPair(e, msg) or
- hasShortECKeyPair(e, msg)
-}
-
-predicate hasInsufficientKeySize2(DataFlow::PathNode source, DataFlow::PathNode sink) {
- exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink))
- or
- exists(SymmetricKeyTrackingConfiguration2 config2 | config2.hasFlowPath(source, sink))
-}
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
index 2979c9116c4..93411a101b2 100644
--- a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
+++ b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
@@ -30,5 +30,6 @@ from DataFlow::PathNode source, DataFlow::PathNode sink
where
//hasInsufficientKeySize2(source, sink)
exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink)) or
- exists(SymmetricKeyTrackingConfiguration2 config2 | config2.hasFlowPath(source, sink))
+ exists(AsymmetricECCKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink)) or
+ exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlowPath(source, sink))
select sink.getNode(), source, sink, "This $@ is too small.", sink.getNode(), "key size"
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
index 68c3e7f322e..50dc7b4637f 100644
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
@@ -144,7 +144,8 @@ public class InsufficientKeySizeTest {
{
int size = 64; // test integer variable
KeyGenerator keyGen = KeyGenerator.getInstance("AES"); // test KeyGenerator variable
- testSymmetric(size, keyGen);
+ testSymmetric(size, keyGen); // test with variable as key size
+ testSymmetric2(64); // test with int constant as key size
}
@@ -159,7 +160,8 @@ public class InsufficientKeySizeTest {
{
ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp112r1"); // test ECGenParameterSpec variable
KeyPairGenerator keyPairGen22 = KeyPairGenerator.getInstance("EC"); // test KeyPairGenerator variable
- testAsymmetricEC(ecSpec, keyPairGen22);
+ testAsymmetricEC(ecSpec, keyPairGen22); // test with variable as key size
+ testAsymmetricNonEC2(1024); // test with int constant as key size
}
}
@@ -173,6 +175,13 @@ public class InsufficientKeySizeTest {
kg.init(64); // $ hasInsufficientKeySize
}
+ //! refactor this to use expected-value tag and combine with above method
+ public static void testSymmetric2(int keySize) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
+ // BAD: Key size is less than 2048
+ KeyGenerator keyGen = KeyGenerator.getInstance("AES");
+ keyGen.init(keySize); // $ hasInsufficientKeySize
+ }
+
public static void testAsymmetricNonEC(int keySize, KeyPairGenerator kpg) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
// BAD: Key size is less than 2048
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
@@ -182,6 +191,13 @@ public class InsufficientKeySizeTest {
kpg.initialize(1024); // $ hasInsufficientKeySize
}
+ //! refactor this to use expected-value tag and combine with above method
+ public static void testAsymmetricNonEC2(int keySize) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
+ // BAD: Key size is less than 2048
+ KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
+ keyPairGen.initialize(keySize); // $ hasInsufficientKeySize
+ }
+
public static void testAsymmetricEC(ECGenParameterSpec spec, KeyPairGenerator kpg) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
// BAD: Key size is less than 256
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("EC");
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
index b8133b154e4..8514fcd2098 100644
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
@@ -9,9 +9,13 @@ class InsufficientKeySizeTest extends InlineExpectationsTest {
override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "hasInsufficientKeySize" and
- exists(Expr e, string msg | hasInsufficientKeySize(e, msg) |
- e.getLocation() = location and
- element = e.toString() and
+ exists(DataFlow::PathNode source, DataFlow::PathNode sink |
+ exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink)) or
+ exists(AsymmetricECCKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink)) or
+ exists(SymmetricKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink))
+ |
+ sink.getNode().getLocation() = location and
+ element = sink.getNode().toString() and
value = ""
)
}
From cdac0e2b529ef1630c5cd16fd2fc35066f7cb015 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Fri, 7 Oct 2022 15:53:02 -0400
Subject: [PATCH 013/249] add local algo name tracking, still need to add
ability to track algo name when KeyGen obj is param to other method
---
.../security/InsufficientKeySizeQuery.qll | 61 ++++++++++++++++---
.../CWE/CWE-326/InsufficientKeySize.ql | 1 -
.../CWE-326/InsufficientKeySizeTest.java | 20 ++++--
3 files changed, 68 insertions(+), 14 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
index 4cf274f3263..c7cb3a10331 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
@@ -2,6 +2,11 @@ import semmle.code.java.security.Encryption
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.DataFlow
+// TODO:
+// todo #1: make representation of source that can be shared across the configs
+// todo #2: make representation of sink that can be shared across the configs
+// todo #3: finish adding tracking for algo type/name... need flow/taint-tracking for across methods??
+// todo #3a: make list of algo names more easily reusable (either as constant-type variable at top of file, or model as own class to share, etc.)
// ******* DATAFLOW BELOW *************************************************************************
/**
* Asymmetric (RSA, DSA, DH) key length data flow tracking configuration.
@@ -10,13 +15,27 @@ class AsymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
AsymmetricKeyTrackingConfiguration() { this = "AsymmetricKeyTrackingConfiguration" }
override predicate isSource(DataFlow::Node source) {
- source.asExpr() instanceof IntegerLiteral and // ! this works with current test cases, but reconsider IntegerLiteral when variables are used
- source.toString().toInt() < 2048
+ exists(ClassInstanceExpr rsaGenParamSpec |
+ rsaGenParamSpec.getConstructedType() instanceof RSAGenParameterSpec and // ! double-check if should just use getType() instead
+ rsaGenParamSpec.getArgument(0).(IntegerLiteral).getIntValue() < 2048 and
+ source.asExpr() = rsaGenParamSpec
+ )
+ or
+ source.asExpr().(IntegerLiteral).getIntValue() < 2048
}
override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma |
+ exists(MethodAccess ma, VarAccess va |
ma.getMethod() instanceof KeyPairGeneratorInitMethod and
+ va.getVariable()
+ .getAnAssignedValue()
+ .(JavaSecurityKeyPairGenerator)
+ .getAlgoSpec()
+ .(StringLiteral)
+ .getValue()
+ .toUpperCase()
+ .matches(["RSA", "DSA", "DH"]) and
+ ma.getQualifier() = va and
sink.asExpr() = ma.getArgument(0)
)
}
@@ -30,15 +49,26 @@ class AsymmetricECCKeyTrackingConfiguration extends DataFlow::Configuration {
override predicate isSource(DataFlow::Node source) {
exists(ClassInstanceExpr ecGenParamSpec |
- getECKeySize(ecGenParamSpec.getArgument(0).(StringLiteral).getValue()) < 256 and
+ getECKeySize(ecGenParamSpec.getArgument(0).(StringLiteral).getValue()) < 256 and // ! can generate EC with just the keysize and not the curve apparently... (based on netty/netty FP example)
source.asExpr() = ecGenParamSpec
)
+ or
+ source.asExpr().(IntegerLiteral).getIntValue() < 256
}
override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma |
+ exists(MethodAccess ma, VarAccess va |
ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- ma.getArgument(0).getType() instanceof ECGenParameterSpec and
+ //ma.getArgument(0).getType() instanceof ECGenParameterSpec and // ! can generate EC with just the keysize and not the curve apparently... (based on netty/netty FP example)
+ va.getVariable()
+ .getAnAssignedValue()
+ .(JavaSecurityKeyPairGenerator)
+ .getAlgoSpec()
+ .(StringLiteral)
+ .getValue()
+ .toUpperCase()
+ .matches(["EC%"]) and
+ ma.getQualifier() = va and
sink.asExpr() = ma.getArgument(0)
)
}
@@ -51,13 +81,21 @@ class SymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
SymmetricKeyTrackingConfiguration() { this = "SymmetricKeyTrackingConfiguration2" }
override predicate isSource(DataFlow::Node source) {
- source.asExpr() instanceof IntegerLiteral and // ! this works with current test cases, but reconsider IntegerLiteral when variables are used
- source.toString().toInt() < 128
+ source.asExpr().(IntegerLiteral).getIntValue() < 128
}
override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma |
+ exists(MethodAccess ma, VarAccess va |
ma.getMethod() instanceof KeyGeneratorInitMethod and
+ va.getVariable()
+ .getAnAssignedValue()
+ .(JavaxCryptoKeyGenerator)
+ .getAlgoSpec()
+ .(StringLiteral)
+ .getValue()
+ .toUpperCase()
+ .matches(["AES"]) and
+ ma.getQualifier() = va and
sink.asExpr() = ma.getArgument(0)
)
}
@@ -77,6 +115,11 @@ private class ECGenParameterSpec extends RefType {
ECGenParameterSpec() { this.hasQualifiedName("java.security.spec", "ECGenParameterSpec") }
}
+/** The Java class `java.security.spec.ECGenParameterSpec`. */
+private class RSAGenParameterSpec extends RefType {
+ RSAGenParameterSpec() { this.hasQualifiedName("java.security.spec", "RSAKeyGenParameterSpec") }
+}
+
// ! move to Encryption.qll?
/** Returns the key size in the EC algorithm string */
bindingset[algorithm]
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
index 93411a101b2..c6dce740588 100644
--- a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
+++ b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
@@ -28,7 +28,6 @@ import DataFlow::PathGraph
// sink.getNode(), "size"
from DataFlow::PathNode source, DataFlow::PathNode sink
where
- //hasInsufficientKeySize2(source, sink)
exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink)) or
exists(AsymmetricECCKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink)) or
exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlowPath(source, sink))
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
index 50dc7b4637f..b383892361b 100644
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
@@ -1,5 +1,6 @@
import java.security.KeyPairGenerator;
import java.security.spec.ECGenParameterSpec;
+import java.security.spec.RSAKeyGenParameterSpec;
import javax.crypto.KeyGenerator;
public class InsufficientKeySizeTest {
@@ -27,6 +28,16 @@ public class InsufficientKeySizeTest {
// GOOD: Key size is no less than 2048
KeyPairGenerator keyPairGen2 = KeyPairGenerator.getInstance("RSA");
keyPairGen2.initialize(2048); // Safe
+
+ // test with spec
+ // BAD: Key size is less than 2048
+ KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("RSA");
+ RSAKeyGenParameterSpec rsaSpec = new RSAKeyGenParameterSpec(1024, null);
+ keyPairGen3.initialize(rsaSpec); // $ hasInsufficientKeySize
+
+ // BAD: Key size is less than 2048
+ KeyPairGenerator keyPairGen4 = KeyPairGenerator.getInstance("RSA");
+ keyPairGen4.initialize(new RSAKeyGenParameterSpec(1024, null)); // $ hasInsufficientKeySize
}
// DSA (Asymmetric)
@@ -145,7 +156,7 @@ public class InsufficientKeySizeTest {
int size = 64; // test integer variable
KeyGenerator keyGen = KeyGenerator.getInstance("AES"); // test KeyGenerator variable
testSymmetric(size, keyGen); // test with variable as key size
- testSymmetric2(64); // test with int constant as key size
+ testSymmetric2(64); // test with int literal as key size
}
@@ -153,15 +164,16 @@ public class InsufficientKeySizeTest {
{
int size = 1024; // test integer variable
KeyPairGenerator keyPairGen21 = KeyPairGenerator.getInstance("RSA"); // test KeyPairGenerator variable
- testAsymmetricNonEC(size, keyPairGen21);
+ testAsymmetricNonEC(size, keyPairGen21); // test with variable as key size
+ testAsymmetricNonEC2(1024); // test with int literal as key size
}
// Test variable passed to other method(s) - Asymmetric, EC
{
ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp112r1"); // test ECGenParameterSpec variable
KeyPairGenerator keyPairGen22 = KeyPairGenerator.getInstance("EC"); // test KeyPairGenerator variable
- testAsymmetricEC(ecSpec, keyPairGen22); // test with variable as key size
- testAsymmetricNonEC2(1024); // test with int constant as key size
+ testAsymmetricEC(ecSpec, keyPairGen22);
+
}
}
From b7123c17f8f0c871e1cf53a51027e5abc6c69a77 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Fri, 7 Oct 2022 23:42:23 -0400
Subject: [PATCH 014/249] draft of adding kpg tracking into dataflow config
---
.../security/InsufficientKeySizeQuery.qll | 42 ++--
.../CWE/CWE-326/InsufficientKeySize.ql | 20 +-
.../CWE-326/InsufficientKeySizeTest.java | 26 ++-
.../security/CWE-326/SignatureTest.java | 196 ++++++++++++++++++
4 files changed, 261 insertions(+), 23 deletions(-)
create mode 100644 java/ql/test/query-tests/security/CWE-326/SignatureTest.java
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
index c7cb3a10331..b10bc83c64a 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
@@ -1,4 +1,5 @@
import semmle.code.java.security.Encryption
+import semmle.code.java.dataflow.TaintTracking2
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.DataFlow
@@ -11,7 +12,7 @@ import semmle.code.java.dataflow.DataFlow
/**
* Asymmetric (RSA, DSA, DH) key length data flow tracking configuration.
*/
-class AsymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
+class AsymmetricKeyTrackingConfiguration extends TaintTracking2::Configuration {
AsymmetricKeyTrackingConfiguration() { this = "AsymmetricKeyTrackingConfiguration" }
override predicate isSource(DataFlow::Node source) {
@@ -27,15 +28,25 @@ class AsymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
override predicate isSink(DataFlow::Node sink) {
exists(MethodAccess ma, VarAccess va |
ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- va.getVariable()
- .getAnAssignedValue()
- .(JavaSecurityKeyPairGenerator)
- .getAlgoSpec()
- .(StringLiteral)
- .getValue()
- .toUpperCase()
- .matches(["RSA", "DSA", "DH"]) and
- ma.getQualifier() = va and
+ ma.getFile().getBaseName().matches("SignatureTest.java") and
+ // va.getVariable()
+ // .getAnAssignedValue()
+ // .(JavaSecurityKeyPairGenerator)
+ // .getAlgoSpec()
+ // .(StringLiteral)
+ // .getValue()
+ // .toUpperCase()
+ // .matches(["RSA", "DSA", "DH"]) and
+ // ma.getQualifier() = va and
+ exists(
+ JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kpgConfig,
+ DataFlow::PathNode source, DataFlow::PathNode dest
+ |
+ jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches(["RSA", "DSA", "DH"]) and
+ source.getNode().asExpr() = jpg and
+ dest.getNode().asExpr() = ma.getQualifier() and
+ kpgConfig.hasFlowPath(source, dest)
+ ) and
sink.asExpr() = ma.getArgument(0)
)
}
@@ -102,12 +113,11 @@ class SymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
}
// ! below doesn't work for some reason...
-predicate hasInsufficientKeySize2(DataFlow::PathNode source, DataFlow::PathNode sink) {
- exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink))
- or
- exists(SymmetricKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink))
-}
-
+// predicate hasInsufficientKeySize2(DataFlow::PathNode source, DataFlow::PathNode sink) {
+// exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink))
+// or
+// exists(SymmetricKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink))
+// }
// ******** Need the below for the above ********
// ! move to Encryption.qll?
/** The Java class `java.security.spec.ECGenParameterSpec`. */
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
index c6dce740588..9b52707c594 100644
--- a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
+++ b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
@@ -26,9 +26,17 @@ import DataFlow::PathGraph
// cfg2.hasFlowPath(source, sink)
// select sink.getNode(), source, sink, "The $@ of an asymmetric key should be at least 2048 bits.",
// sink.getNode(), "size"
-from DataFlow::PathNode source, DataFlow::PathNode sink
-where
- exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink)) or
- exists(AsymmetricECCKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink)) or
- exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlowPath(source, sink))
-select sink.getNode(), source, sink, "This $@ is too small.", sink.getNode(), "key size"
+// * Use Below
+// from DataFlow::PathNode source, DataFlow::PathNode sink
+// where exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink)) //or
+// //exists(AsymmetricECCKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink)) //or
+// //exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlowPath(source, sink))
+// select sink.getNode(), source, sink, "This $@ is too small, and flows to $@.", source.getNode(),
+// "key size", sink.getNode(), "here"
+// * Use Above
+from DataFlow::Node source, DataFlow::Node sink
+where exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlow(source, sink)) //or
+//exists(AsymmetricECCKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink)) //or
+//exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlowPath(source, sink))
+select sink, source, sink, "This $@ is too small, and flows to $@.", source, "key size", sink,
+ "here"
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
index b383892361b..75ba5e36e76 100644
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
@@ -1,7 +1,11 @@
+import javax.crypto.KeyGenerator;
import java.security.KeyPairGenerator;
+
import java.security.spec.ECGenParameterSpec;
import java.security.spec.RSAKeyGenParameterSpec;
-import javax.crypto.KeyGenerator;
+import java.security.spec.DSAGenParameterSpec;
+import javax.crypto.spec.DHGenParameterSpec;
+
public class InsufficientKeySizeTest {
public void keySizeTesting() throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
@@ -49,6 +53,16 @@ public class InsufficientKeySizeTest {
// GOOD: Key size is no less than 2048
KeyPairGenerator keyPairGen4 = KeyPairGenerator.getInstance("DSA");
keyPairGen4.initialize(2048); // Safe
+
+ // test with spec?
+ // // BAD: Key size is less than 2048
+ // KeyPairGenerator keyPairGen5 = KeyPairGenerator.getInstance("DSA");
+ // DSAGenParameterSpec dsaSpec = new DSAGenParameterSpec(1024, null);
+ // keyPairGen5.initialize(dsaSpec); // $ hasInsufficientKeySize
+
+ // // BAD: Key size is less than 2048
+ // KeyPairGenerator keyPairGen6 = KeyPairGenerator.getInstance("DSA");
+ // keyPairGen6.initialize(new DSAGenParameterSpec(1024, null)); // $ hasInsufficientKeySize
}
// DH (Asymmetric)
@@ -60,6 +74,16 @@ public class InsufficientKeySizeTest {
// GOOD: Key size is no less than 2048
KeyPairGenerator keyPairGen17 = KeyPairGenerator.getInstance("DH");
keyPairGen17.initialize(2048); // Safe
+
+ // test with spec?
+ // // BAD: Key size is less than 2048
+ // KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("DH");
+ // DHGenParameterSpec dhSpec = new DHGenParameterSpec(1024, null);
+ // keyPairGen3.initialize(dhSpec); // $ hasInsufficientKeySize
+
+ // // BAD: Key size is less than 2048
+ // KeyPairGenerator keyPairGen4 = KeyPairGenerator.getInstance("DH");
+ // keyPairGen4.initialize(new DHGenParameterSpec(1024, null)); // $ hasInsufficientKeySize
}
// EC (Asymmetric)
diff --git a/java/ql/test/query-tests/security/CWE-326/SignatureTest.java b/java/ql/test/query-tests/security/CWE-326/SignatureTest.java
new file mode 100644
index 00000000000..016a62a41f8
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-326/SignatureTest.java
@@ -0,0 +1,196 @@
+//package org.bouncycastle.jce.provider.test;
+
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.Signature;
+
+// import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+// import org.bouncycastle.jce.provider.BouncyCastleProvider;
+// import org.bouncycastle.jce.spec.ECNamedCurveGenParameterSpec;
+// import org.bouncycastle.jce.spec.GOST3410ParameterSpec;
+// import org.bouncycastle.util.encoders.Hex;
+// import org.bouncycastle.util.test.SimpleTest;
+
+public class SignatureTest
+ //extends SimpleTest
+{
+ // private static final byte[] DATA = Hex.decode("00000000deadbeefbeefdeadffffffff00000000");
+
+ private void checkSig(KeyPair kp, String name)
+ throws Exception
+ {
+ // Signature sig = Signature.getInstance(name, "BC");
+
+ // sig.initSign(kp.getPrivate());
+ // sig.update(DATA);
+
+ // byte[] signature1 = sig.sign();
+
+ // sig.update(DATA);
+
+ // byte[] signature2 = sig.sign();
+
+ // sig.initVerify(kp.getPublic());
+
+ // sig.update(DATA);
+ // if (!sig.verify(signature1))
+ // {
+ // fail("did not verify: " + name);
+ // }
+
+ // // After verify, should be reusable as if we are after initVerify
+ // sig.update(DATA);
+ // if (!sig.verify(signature1))
+ // {
+ // fail("second verify failed: " + name);
+ // }
+
+ // sig.update(DATA);
+ // if (!sig.verify(signature2))
+ // {
+ // fail("second verify failed (2): " + name);
+ // }
+ }
+
+ public void performTest()
+ throws Exception
+ {
+ KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC");
+
+ kpGen.initialize(2048); // Safe
+
+ KeyPair kp = kpGen.generateKeyPair();
+
+ checkSig(kp, "SHA1withRSA");
+ checkSig(kp, "SHA224withRSA");
+ checkSig(kp, "SHA256withRSA");
+ checkSig(kp, "SHA384withRSA");
+ checkSig(kp, "SHA512withRSA");
+
+ checkSig(kp, "SHA3-224withRSA");
+ checkSig(kp, "SHA3-256withRSA");
+ checkSig(kp, "SHA3-384withRSA");
+ checkSig(kp, "SHA3-512withRSA");
+
+ checkSig(kp, "MD2withRSA");
+ checkSig(kp, "MD4withRSA");
+ checkSig(kp, "MD5withRSA");
+ checkSig(kp, "RIPEMD160withRSA");
+ checkSig(kp, "RIPEMD128withRSA");
+ checkSig(kp, "RIPEMD256withRSA");
+
+ checkSig(kp, "SHA1withRSAandMGF1");
+ checkSig(kp, "SHA1withRSAandMGF1");
+ checkSig(kp, "SHA224withRSAandMGF1");
+ checkSig(kp, "SHA256withRSAandMGF1");
+ checkSig(kp, "SHA384withRSAandMGF1");
+ checkSig(kp, "SHA512withRSAandMGF1");
+
+ checkSig(kp, "SHA1withRSAandSHAKE128");
+ checkSig(kp, "SHA1withRSAandSHAKE128");
+ checkSig(kp, "SHA224withRSAandSHAKE128");
+ checkSig(kp, "SHA256withRSAandSHAKE128");
+ checkSig(kp, "SHA384withRSAandSHAKE128");
+ checkSig(kp, "SHA512withRSAandSHAKE128");
+
+ checkSig(kp, "SHA1withRSAandSHAKE256");
+ checkSig(kp, "SHA1withRSAandSHAKE256");
+ checkSig(kp, "SHA224withRSAandSHAKE256");
+ checkSig(kp, "SHA256withRSAandSHAKE256");
+ checkSig(kp, "SHA384withRSAandSHAKE256");
+ checkSig(kp, "SHA512withRSAandSHAKE256");
+
+ checkSig(kp, "SHAKE128withRSAPSS");
+ checkSig(kp, "SHAKE256withRSAPSS");
+
+ checkSig(kp, "SHA1withRSA/ISO9796-2");
+ checkSig(kp, "MD5withRSA/ISO9796-2");
+ checkSig(kp, "RIPEMD160withRSA/ISO9796-2");
+
+// checkSig(kp, "SHA1withRSA/ISO9796-2PSS");
+// checkSig(kp, "MD5withRSA/ISO9796-2PSS");
+// checkSig(kp, "RIPEMD160withRSA/ISO9796-2PSS");
+
+ checkSig(kp, "RIPEMD128withRSA/X9.31");
+ checkSig(kp, "RIPEMD160withRSA/X9.31");
+ checkSig(kp, "SHA1withRSA/X9.31");
+ checkSig(kp, "SHA224withRSA/X9.31");
+ checkSig(kp, "SHA256withRSA/X9.31");
+ checkSig(kp, "SHA384withRSA/X9.31");
+ checkSig(kp, "SHA512withRSA/X9.31");
+ checkSig(kp, "WhirlpoolwithRSA/X9.31");
+
+ kpGen = KeyPairGenerator.getInstance("DSA", "BC");
+
+ kpGen.initialize(2048); // Safe
+
+ kp = kpGen.generateKeyPair();
+
+ checkSig(kp, "SHA1withDSA");
+ checkSig(kp, "SHA224withDSA");
+ checkSig(kp, "SHA256withDSA");
+ checkSig(kp, "SHA384withDSA");
+ checkSig(kp, "SHA512withDSA");
+ checkSig(kp, "NONEwithDSA");
+
+ kpGen = KeyPairGenerator.getInstance("EC", "BC");
+
+ kpGen.initialize(256); // Safe
+
+ kp = kpGen.generateKeyPair();
+
+ checkSig(kp, "SHA1withECDSA");
+ checkSig(kp, "SHA224withECDSA");
+ checkSig(kp, "SHA256withECDSA");
+ checkSig(kp, "SHA384withECDSA");
+ checkSig(kp, "SHA512withECDSA");
+ checkSig(kp, "RIPEMD160withECDSA");
+ checkSig(kp, "SHAKE128withECDSA");
+ checkSig(kp, "SHAKE256withECDSA");
+
+ kpGen = KeyPairGenerator.getInstance("EC", "BC");
+
+ kpGen.initialize(521); // Safe
+
+ kp = kpGen.generateKeyPair();
+
+ checkSig(kp, "SHA1withECNR");
+ checkSig(kp, "SHA224withECNR");
+ checkSig(kp, "SHA256withECNR");
+ checkSig(kp, "SHA384withECNR");
+ checkSig(kp, "SHA512withECNR");
+
+ // kpGen = KeyPairGenerator.getInstance("ECGOST3410", "BC");
+
+ // kpGen.initialize(new ECNamedCurveGenParameterSpec("GostR3410-2001-CryptoPro-A"), new SecureRandom());
+
+ // kp = kpGen.generateKeyPair();
+
+ // checkSig(kp, "GOST3411withECGOST3410");
+
+ // kpGen = KeyPairGenerator.getInstance("GOST3410", "BC");
+
+ // GOST3410ParameterSpec gost3410P = new GOST3410ParameterSpec(CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_A.getId());
+
+ // kpGen.initialize(gost3410P);
+
+ // kp = kpGen.generateKeyPair();
+
+ // checkSig(kp, "GOST3411withGOST3410");
+ }
+
+ public String getName()
+ {
+ return "SigNameTest";
+ }
+
+ // public static void main(
+ // String[] args)
+ // {
+ // //Security.addProvider(new BouncyCastleProvider());
+
+ // //runTest(new SignatureTest());
+ // }
+}
From b0af9f936cc97890ca0c2df398e176bd7cbe0aba Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Sun, 9 Oct 2022 22:59:45 -0400
Subject: [PATCH 015/249] added kg taintracking config to all
---
.../security/InsufficientKeySizeQuery.qll | 67 +++++++++++++------
.../CWE/CWE-326/InsufficientKeySize.ql | 16 +++--
2 files changed, 55 insertions(+), 28 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
index b10bc83c64a..a364d5dc155 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
@@ -28,7 +28,7 @@ class AsymmetricKeyTrackingConfiguration extends TaintTracking2::Configuration {
override predicate isSink(DataFlow::Node sink) {
exists(MethodAccess ma, VarAccess va |
ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- ma.getFile().getBaseName().matches("SignatureTest.java") and
+ //ma.getFile().getBaseName().matches("SignatureTest.java") and
// va.getVariable()
// .getAnAssignedValue()
// .(JavaSecurityKeyPairGenerator)
@@ -52,10 +52,17 @@ class AsymmetricKeyTrackingConfiguration extends TaintTracking2::Configuration {
}
}
+// predicate hasInsufficientKeySize(string msg) { hasShortAsymmetricKeyPair(msg) }
+// predicate hasShortAsymmetricKeyPair(string msg) {
+// exists(AsymmetricKeyTrackingConfiguration config1, DataFlow::Node source, DataFlow::Node sink |
+// config1.hasFlow(source, sink)
+// ) and
+// msg = "Key size should be at least 2048 bits for " + "___" + " encryption."
+// }
/**
* Asymmetric (RSA, DSA, DH) key length data flow tracking configuration.
*/
-class AsymmetricECCKeyTrackingConfiguration extends DataFlow::Configuration {
+class AsymmetricECCKeyTrackingConfiguration extends TaintTracking2::Configuration {
AsymmetricECCKeyTrackingConfiguration() { this = "AsymmetricECCKeyTrackingConfiguration" }
override predicate isSource(DataFlow::Node source) {
@@ -71,15 +78,24 @@ class AsymmetricECCKeyTrackingConfiguration extends DataFlow::Configuration {
exists(MethodAccess ma, VarAccess va |
ma.getMethod() instanceof KeyPairGeneratorInitMethod and
//ma.getArgument(0).getType() instanceof ECGenParameterSpec and // ! can generate EC with just the keysize and not the curve apparently... (based on netty/netty FP example)
- va.getVariable()
- .getAnAssignedValue()
- .(JavaSecurityKeyPairGenerator)
- .getAlgoSpec()
- .(StringLiteral)
- .getValue()
- .toUpperCase()
- .matches(["EC%"]) and
- ma.getQualifier() = va and
+ // va.getVariable()
+ // .getAnAssignedValue()
+ // .(JavaSecurityKeyPairGenerator)
+ // .getAlgoSpec()
+ // .(StringLiteral)
+ // .getValue()
+ // .toUpperCase()
+ // .matches(["EC%"]) and
+ // ma.getQualifier() = va and
+ exists(
+ JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kpgConfig,
+ DataFlow::PathNode source, DataFlow::PathNode dest
+ |
+ jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches("EC%") and
+ source.getNode().asExpr() = jpg and
+ dest.getNode().asExpr() = ma.getQualifier() and
+ kpgConfig.hasFlowPath(source, dest)
+ ) and
sink.asExpr() = ma.getArgument(0)
)
}
@@ -88,7 +104,7 @@ class AsymmetricECCKeyTrackingConfiguration extends DataFlow::Configuration {
/**
* Symmetric (AES) key length data flow tracking configuration.
*/
-class SymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
+class SymmetricKeyTrackingConfiguration extends TaintTracking2::Configuration {
SymmetricKeyTrackingConfiguration() { this = "SymmetricKeyTrackingConfiguration2" }
override predicate isSource(DataFlow::Node source) {
@@ -98,15 +114,24 @@ class SymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
override predicate isSink(DataFlow::Node sink) {
exists(MethodAccess ma, VarAccess va |
ma.getMethod() instanceof KeyGeneratorInitMethod and
- va.getVariable()
- .getAnAssignedValue()
- .(JavaxCryptoKeyGenerator)
- .getAlgoSpec()
- .(StringLiteral)
- .getValue()
- .toUpperCase()
- .matches(["AES"]) and
- ma.getQualifier() = va and
+ // va.getVariable()
+ // .getAnAssignedValue()
+ // .(JavaxCryptoKeyGenerator)
+ // .getAlgoSpec()
+ // .(StringLiteral)
+ // .getValue()
+ // .toUpperCase()
+ // .matches(["AES"]) and
+ // ma.getQualifier() = va and
+ exists(
+ JavaxCryptoKeyGenerator jcg, KeyGeneratorInitConfiguration kgConfig,
+ DataFlow::PathNode source, DataFlow::PathNode dest
+ |
+ jcg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches("AES") and
+ source.getNode().asExpr() = jcg and
+ dest.getNode().asExpr() = ma.getQualifier() and
+ kgConfig.hasFlowPath(source, dest)
+ ) and
sink.asExpr() = ma.getArgument(0)
)
}
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
index 9b52707c594..c3a406514ac 100644
--- a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
+++ b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
@@ -2,7 +2,7 @@
* @name Insufficient key size used with a cryptographic algorithm
* @description Using cryptographic algorithms with too small of a key size can
* allow an attacker to compromise security.
- * @kind path-problem
+ * @kind problem
* @problem.severity error
* @security-severity 7.5
* @precision high
@@ -13,8 +13,8 @@
import java
import semmle.code.java.security.InsufficientKeySizeQuery
-import DataFlow::PathGraph
+//import DataFlow::PathGraph
// from Expr e, string msg
// where hasInsufficientKeySize(e, msg)
// select e, msg
@@ -34,9 +34,11 @@ import DataFlow::PathGraph
// select sink.getNode(), source, sink, "This $@ is too small, and flows to $@.", source.getNode(),
// "key size", sink.getNode(), "here"
// * Use Above
+// * Use Below for taint-tracking with kpg
from DataFlow::Node source, DataFlow::Node sink
-where exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlow(source, sink)) //or
-//exists(AsymmetricECCKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink)) //or
-//exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlowPath(source, sink))
-select sink, source, sink, "This $@ is too small, and flows to $@.", source, "key size", sink,
- "here"
+where
+ exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlow(source, sink)) or
+ exists(AsymmetricECCKeyTrackingConfiguration config2 | config2.hasFlow(source, sink)) or
+ exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlow(source, sink))
+select sink, "This $@ is too small and creates a key $@.", source, "key size", sink, "here"
+// * Use Above for taint-tracking with kpg
From f5a2fef7a3b465c45104ded6bf3b80555e958b09 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Mon, 10 Oct 2022 10:13:28 -0400
Subject: [PATCH 016/249] update tests for non-path version
---
.../security/InsufficientKeySizeQuery.qll | 2 +-
.../CWE-326/InsufficientKeySizeTest.ql | 26 ++++++++++++++-----
2 files changed, 21 insertions(+), 7 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
index a364d5dc155..671ae64cd9d 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
@@ -60,7 +60,7 @@ class AsymmetricKeyTrackingConfiguration extends TaintTracking2::Configuration {
// msg = "Key size should be at least 2048 bits for " + "___" + " encryption."
// }
/**
- * Asymmetric (RSA, DSA, DH) key length data flow tracking configuration.
+ * Asymmetric (EC) key length data flow tracking configuration.
*/
class AsymmetricECCKeyTrackingConfiguration extends TaintTracking2::Configuration {
AsymmetricECCKeyTrackingConfiguration() { this = "AsymmetricECCKeyTrackingConfiguration" }
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
index 8514fcd2098..93ad76704c2 100644
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
@@ -7,15 +7,29 @@ class InsufficientKeySizeTest extends InlineExpectationsTest {
override string getARelevantTag() { result = "hasInsufficientKeySize" }
+ // * Path-problem
+ // override predicate hasActualResult(Location location, string element, string tag, string value) {
+ // tag = "hasInsufficientKeySize" and
+ // exists(DataFlow::PathNode source, DataFlow::PathNode sink |
+ // exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink)) or
+ // exists(AsymmetricECCKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink)) or
+ // exists(SymmetricKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink))
+ // |
+ // sink.getNode().getLocation() = location and
+ // element = sink.getNode().toString() and
+ // value = ""
+ // )
+ // }
+ // * Not path-problem
override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "hasInsufficientKeySize" and
- exists(DataFlow::PathNode source, DataFlow::PathNode sink |
- exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink)) or
- exists(AsymmetricECCKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink)) or
- exists(SymmetricKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink))
+ exists(DataFlow::Node source, DataFlow::Node sink |
+ exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlow(source, sink)) or
+ exists(AsymmetricECCKeyTrackingConfiguration config2 | config2.hasFlow(source, sink)) or
+ exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlow(source, sink))
|
- sink.getNode().getLocation() = location and
- element = sink.getNode().toString() and
+ sink.getLocation() = location and
+ element = sink.toString() and
value = ""
)
}
From 3cc7f143b23455c2b7499664b700f61d5bc39c17 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Mon, 10 Oct 2022 10:47:41 -0400
Subject: [PATCH 017/249] clean up code somewhat
---
.../security/InsufficientKeySizeQuery.qll | 175 +++++--------
...an-UpForDoubleTaintTracking-2022-10-10.qll | 239 ++++++++++++++++++
.../CWE/CWE-326/InsufficientKeySize.ql | 8 +-
.../CWE-326/InsufficientKeySizeTest.java | 8 +
4 files changed, 320 insertions(+), 110 deletions(-)
create mode 100644 java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery_BeforeClean-UpForDoubleTaintTracking-2022-10-10.qll
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
index 671ae64cd9d..c52ae2df550 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
@@ -3,11 +3,6 @@ import semmle.code.java.dataflow.TaintTracking2
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.DataFlow
-// TODO:
-// todo #1: make representation of source that can be shared across the configs
-// todo #2: make representation of sink that can be shared across the configs
-// todo #3: finish adding tracking for algo type/name... need flow/taint-tracking for across methods??
-// todo #3a: make list of algo names more easily reusable (either as constant-type variable at top of file, or model as own class to share, etc.)
// ******* DATAFLOW BELOW *************************************************************************
/**
* Asymmetric (RSA, DSA, DH) key length data flow tracking configuration.
@@ -16,8 +11,10 @@ class AsymmetricKeyTrackingConfiguration extends TaintTracking2::Configuration {
AsymmetricKeyTrackingConfiguration() { this = "AsymmetricKeyTrackingConfiguration" }
override predicate isSource(DataFlow::Node source) {
+ // ! may need to change below to still use `keysize` variable as the source, not the spec
+ // ! also need to look into specs for DSA and DH more
exists(ClassInstanceExpr rsaGenParamSpec |
- rsaGenParamSpec.getConstructedType() instanceof RSAGenParameterSpec and // ! double-check if should just use getType() instead
+ rsaGenParamSpec.getConstructedType() instanceof RSAGenParameterSpec and
rsaGenParamSpec.getArgument(0).(IntegerLiteral).getIntValue() < 2048 and
source.asExpr() = rsaGenParamSpec
)
@@ -26,18 +23,8 @@ class AsymmetricKeyTrackingConfiguration extends TaintTracking2::Configuration {
}
override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma, VarAccess va |
+ exists(MethodAccess ma |
ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- //ma.getFile().getBaseName().matches("SignatureTest.java") and
- // va.getVariable()
- // .getAnAssignedValue()
- // .(JavaSecurityKeyPairGenerator)
- // .getAlgoSpec()
- // .(StringLiteral)
- // .getValue()
- // .toUpperCase()
- // .matches(["RSA", "DSA", "DH"]) and
- // ma.getQualifier() = va and
exists(
JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kpgConfig,
DataFlow::PathNode source, DataFlow::PathNode dest
@@ -52,13 +39,6 @@ class AsymmetricKeyTrackingConfiguration extends TaintTracking2::Configuration {
}
}
-// predicate hasInsufficientKeySize(string msg) { hasShortAsymmetricKeyPair(msg) }
-// predicate hasShortAsymmetricKeyPair(string msg) {
-// exists(AsymmetricKeyTrackingConfiguration config1, DataFlow::Node source, DataFlow::Node sink |
-// config1.hasFlow(source, sink)
-// ) and
-// msg = "Key size should be at least 2048 bits for " + "___" + " encryption."
-// }
/**
* Asymmetric (EC) key length data flow tracking configuration.
*/
@@ -66,8 +46,9 @@ class AsymmetricECCKeyTrackingConfiguration extends TaintTracking2::Configuratio
AsymmetricECCKeyTrackingConfiguration() { this = "AsymmetricECCKeyTrackingConfiguration" }
override predicate isSource(DataFlow::Node source) {
+ // ! may need to change below to still use `keysize` variable as the source, not the spec
exists(ClassInstanceExpr ecGenParamSpec |
- getECKeySize(ecGenParamSpec.getArgument(0).(StringLiteral).getValue()) < 256 and // ! can generate EC with just the keysize and not the curve apparently... (based on netty/netty FP example)
+ getECKeySize(ecGenParamSpec.getArgument(0).(StringLiteral).getValue()) < 256 and
source.asExpr() = ecGenParamSpec
)
or
@@ -75,18 +56,8 @@ class AsymmetricECCKeyTrackingConfiguration extends TaintTracking2::Configuratio
}
override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma, VarAccess va |
+ exists(MethodAccess ma |
ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- //ma.getArgument(0).getType() instanceof ECGenParameterSpec and // ! can generate EC with just the keysize and not the curve apparently... (based on netty/netty FP example)
- // va.getVariable()
- // .getAnAssignedValue()
- // .(JavaSecurityKeyPairGenerator)
- // .getAlgoSpec()
- // .(StringLiteral)
- // .getValue()
- // .toUpperCase()
- // .matches(["EC%"]) and
- // ma.getQualifier() = va and
exists(
JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kpgConfig,
DataFlow::PathNode source, DataFlow::PathNode dest
@@ -112,17 +83,8 @@ class SymmetricKeyTrackingConfiguration extends TaintTracking2::Configuration {
}
override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma, VarAccess va |
+ exists(MethodAccess ma |
ma.getMethod() instanceof KeyGeneratorInitMethod and
- // va.getVariable()
- // .getAnAssignedValue()
- // .(JavaxCryptoKeyGenerator)
- // .getAlgoSpec()
- // .(StringLiteral)
- // .getValue()
- // .toUpperCase()
- // .matches(["AES"]) and
- // ma.getQualifier() = va and
exists(
JavaxCryptoKeyGenerator jcg, KeyGeneratorInitConfiguration kgConfig,
DataFlow::PathNode source, DataFlow::PathNode dest
@@ -137,71 +99,13 @@ class SymmetricKeyTrackingConfiguration extends TaintTracking2::Configuration {
}
}
-// ! below doesn't work for some reason...
+// ! below predicate doesn't work
// predicate hasInsufficientKeySize2(DataFlow::PathNode source, DataFlow::PathNode sink) {
// exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink))
// or
// exists(SymmetricKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink))
// }
-// ******** Need the below for the above ********
-// ! move to Encryption.qll?
-/** The Java class `java.security.spec.ECGenParameterSpec`. */
-private class ECGenParameterSpec extends RefType {
- ECGenParameterSpec() { this.hasQualifiedName("java.security.spec", "ECGenParameterSpec") }
-}
-
-/** The Java class `java.security.spec.ECGenParameterSpec`. */
-private class RSAGenParameterSpec extends RefType {
- RSAGenParameterSpec() { this.hasQualifiedName("java.security.spec", "RSAKeyGenParameterSpec") }
-}
-
-// ! move to Encryption.qll?
-/** Returns the key size in the EC algorithm string */
-bindingset[algorithm]
-private int getECKeySize(string algorithm) {
- algorithm.matches("sec%") and // specification such as "secp256r1"
- result = algorithm.regexpCapture("sec[p|t](\\d+)[a-zA-Z].*", 1).toInt()
- or
- algorithm.matches("X9.62%") and //specification such as "X9.62 prime192v2"
- result = algorithm.regexpCapture("X9\\.62 .*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
- or
- (algorithm.matches("prime%") or algorithm.matches("c2tnb%")) and //specification such as "prime192v2"
- result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
-}
-
-// ! move to Encryption.qll?
-/** The `init` method declared in `javax.crypto.KeyGenerator`. */
-private class KeyGeneratorInitMethod extends Method {
- KeyGeneratorInitMethod() {
- this.getDeclaringType() instanceof KeyGenerator and
- this.hasName("init")
- }
-}
-
-// ! move to Encryption.qll?
-/** The `initialize` method declared in `java.security.KeyPairGenerator`. */
-private class KeyPairGeneratorInitMethod extends Method {
- KeyPairGeneratorInitMethod() {
- this.getDeclaringType() instanceof KeyPairGenerator and
- this.hasName("initialize")
- }
-}
-
-// ******* DATAFLOW ABOVE *************************************************************************
-// ************************************************************************************************
-// ************************************************************************************************
-// ************************************************************************************************
-// ************************************************************************************************
-// ************************************************************************************************
-// ******* OLD/UNUSED OR EXPERIMENTAL CODE BELOW **************************************************
-class UnsafeSymmetricKeySize extends IntegerLiteral {
- UnsafeSymmetricKeySize() { this.getIntValue() < 128 }
-}
-
-class UnsafeAsymmetricKeySize extends IntegerLiteral {
- UnsafeAsymmetricKeySize() { this.getIntValue() < 2048 }
-}
-
+// ******** Need the below models for the above configs ********
/** Taint configuration tracking flow from a key generator to a `init` method call. */
private class KeyGeneratorInitConfiguration extends TaintTracking::Configuration {
KeyGeneratorInitConfiguration() { this = "KeyGeneratorInitConfiguration" }
@@ -236,3 +140,62 @@ private class KeyPairGeneratorInitConfiguration extends TaintTracking::Configura
)
}
}
+
+// ! move some/all of below to Encryption.qll or elsewhere?
+/** The Java class `java.security.spec.ECGenParameterSpec`. */
+private class ECGenParameterSpec extends RefType {
+ ECGenParameterSpec() { this.hasQualifiedName("java.security.spec", "ECGenParameterSpec") }
+}
+
+/** The Java class `java.security.spec.ECGenParameterSpec`. */
+private class RSAGenParameterSpec extends RefType {
+ RSAGenParameterSpec() { this.hasQualifiedName("java.security.spec", "RSAKeyGenParameterSpec") }
+}
+
+/** Returns the key size in the EC algorithm string */
+bindingset[algorithm]
+private int getECKeySize(string algorithm) {
+ algorithm.matches("sec%") and // specification such as "secp256r1"
+ result = algorithm.regexpCapture("sec[p|t](\\d+)[a-zA-Z].*", 1).toInt()
+ or
+ algorithm.matches("X9.62%") and //specification such as "X9.62 prime192v2"
+ result = algorithm.regexpCapture("X9\\.62 .*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
+ or
+ (algorithm.matches("prime%") or algorithm.matches("c2tnb%")) and //specification such as "prime192v2"
+ result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
+}
+
+/** The `init` method declared in `javax.crypto.KeyGenerator`. */
+private class KeyGeneratorInitMethod extends Method {
+ KeyGeneratorInitMethod() {
+ this.getDeclaringType() instanceof KeyGenerator and
+ this.hasName("init")
+ }
+}
+
+/** The `initialize` method declared in `java.security.KeyPairGenerator`. */
+private class KeyPairGeneratorInitMethod extends Method {
+ KeyPairGeneratorInitMethod() {
+ this.getDeclaringType() instanceof KeyPairGenerator and
+ this.hasName("initialize")
+ }
+}
+
+// ******* DATAFLOW ABOVE *************************************************************************
+// ************************************************************************************************
+// ************************************************************************************************
+// ******* OLD/UNUSED OR EXPERIMENTAL CODE BELOW **************************************************
+class UnsafeSymmetricKeySize extends IntegerLiteral {
+ UnsafeSymmetricKeySize() { this.getIntValue() < 128 }
+}
+
+class UnsafeAsymmetricKeySize extends IntegerLiteral {
+ UnsafeAsymmetricKeySize() { this.getIntValue() < 2048 }
+}
+// TODO:
+// ! todo #0a: find a better way to combine the two needed taint-tracking configs so can go back to having a path-graph...
+// ! todo #0b: possible to combine the 3 dataflow configs?
+// todo #1: make representation of source that can be shared across the configs
+// todo #2: make representation of sink that can be shared across the configs
+// todo #3: make list of algo names more easily reusable (either as constant-type variable at top of file, or model as own class to share, etc.)
+// todo #4: refactor to be more like the Python version? (or not possible because of lack of DataFlow::Node for void method in Java?)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery_BeforeClean-UpForDoubleTaintTracking-2022-10-10.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery_BeforeClean-UpForDoubleTaintTracking-2022-10-10.qll
new file mode 100644
index 00000000000..eec0e086f1b
--- /dev/null
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery_BeforeClean-UpForDoubleTaintTracking-2022-10-10.qll
@@ -0,0 +1,239 @@
+import semmle.code.java.security.Encryption
+import semmle.code.java.dataflow.TaintTracking2
+import semmle.code.java.dataflow.TaintTracking
+import semmle.code.java.dataflow.DataFlow
+
+// TODO:
+// todo #0: find a better way to combine the two needed taint-tracking configs so can go back to having a path-graph...
+// todo #1: make representation of source that can be shared across the configs
+// todo #2: make representation of sink that can be shared across the configs
+// todo #3: make list of algo names more easily reusable (either as constant-type variable at top of file, or model as own class to share, etc.)
+// todo #4: refactor to be more like the Python version? (or not possible because of lack of DataFlow::Node for void method in Java?)
+// ******* DATAFLOW BELOW *************************************************************************
+/**
+ * Asymmetric (RSA, DSA, DH) key length data flow tracking configuration.
+ */
+class AsymmetricKeyTrackingConfiguration extends TaintTracking2::Configuration {
+ AsymmetricKeyTrackingConfiguration() { this = "AsymmetricKeyTrackingConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) {
+ exists(ClassInstanceExpr rsaGenParamSpec |
+ rsaGenParamSpec.getConstructedType() instanceof RSAGenParameterSpec and // ! double-check if should just use getType() instead
+ rsaGenParamSpec.getArgument(0).(IntegerLiteral).getIntValue() < 2048 and
+ source.asExpr() = rsaGenParamSpec
+ )
+ or
+ source.asExpr().(IntegerLiteral).getIntValue() < 2048
+ }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(MethodAccess ma, VarAccess va |
+ ma.getMethod() instanceof KeyPairGeneratorInitMethod and
+ //ma.getFile().getBaseName().matches("SignatureTest.java") and
+ // va.getVariable()
+ // .getAnAssignedValue()
+ // .(JavaSecurityKeyPairGenerator)
+ // .getAlgoSpec()
+ // .(StringLiteral)
+ // .getValue()
+ // .toUpperCase()
+ // .matches(["RSA", "DSA", "DH"]) and
+ // ma.getQualifier() = va and
+ exists(
+ JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kpgConfig,
+ DataFlow::PathNode source, DataFlow::PathNode dest
+ |
+ jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches(["RSA", "DSA", "DH"]) and
+ source.getNode().asExpr() = jpg and
+ dest.getNode().asExpr() = ma.getQualifier() and
+ kpgConfig.hasFlowPath(source, dest)
+ ) and
+ sink.asExpr() = ma.getArgument(0)
+ )
+ }
+}
+
+// predicate hasInsufficientKeySize(string msg) { hasShortAsymmetricKeyPair(msg) }
+// predicate hasShortAsymmetricKeyPair(string msg) {
+// exists(AsymmetricKeyTrackingConfiguration config1, DataFlow::Node source, DataFlow::Node sink |
+// config1.hasFlow(source, sink)
+// ) and
+// msg = "Key size should be at least 2048 bits for " + "___" + " encryption."
+// }
+/**
+ * Asymmetric (EC) key length data flow tracking configuration.
+ */
+class AsymmetricECCKeyTrackingConfiguration extends TaintTracking2::Configuration {
+ AsymmetricECCKeyTrackingConfiguration() { this = "AsymmetricECCKeyTrackingConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) {
+ exists(ClassInstanceExpr ecGenParamSpec |
+ getECKeySize(ecGenParamSpec.getArgument(0).(StringLiteral).getValue()) < 256 and // ! can generate EC with just the keysize and not the curve apparently... (based on netty/netty FP example)
+ source.asExpr() = ecGenParamSpec
+ )
+ or
+ source.asExpr().(IntegerLiteral).getIntValue() < 256
+ }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(MethodAccess ma, VarAccess va |
+ ma.getMethod() instanceof KeyPairGeneratorInitMethod and
+ //ma.getArgument(0).getType() instanceof ECGenParameterSpec and // ! can generate EC with just the keysize and not the curve apparently... (based on netty/netty FP example)
+ // va.getVariable()
+ // .getAnAssignedValue()
+ // .(JavaSecurityKeyPairGenerator)
+ // .getAlgoSpec()
+ // .(StringLiteral)
+ // .getValue()
+ // .toUpperCase()
+ // .matches(["EC%"]) and
+ // ma.getQualifier() = va and
+ exists(
+ JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kpgConfig,
+ DataFlow::PathNode source, DataFlow::PathNode dest
+ |
+ jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches("EC%") and
+ source.getNode().asExpr() = jpg and
+ dest.getNode().asExpr() = ma.getQualifier() and
+ kpgConfig.hasFlowPath(source, dest)
+ ) and
+ sink.asExpr() = ma.getArgument(0)
+ )
+ }
+}
+
+/**
+ * Symmetric (AES) key length data flow tracking configuration.
+ */
+class SymmetricKeyTrackingConfiguration extends TaintTracking2::Configuration {
+ SymmetricKeyTrackingConfiguration() { this = "SymmetricKeyTrackingConfiguration2" }
+
+ override predicate isSource(DataFlow::Node source) {
+ source.asExpr().(IntegerLiteral).getIntValue() < 128
+ }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(MethodAccess ma, VarAccess va |
+ ma.getMethod() instanceof KeyGeneratorInitMethod and
+ // va.getVariable()
+ // .getAnAssignedValue()
+ // .(JavaxCryptoKeyGenerator)
+ // .getAlgoSpec()
+ // .(StringLiteral)
+ // .getValue()
+ // .toUpperCase()
+ // .matches(["AES"]) and
+ // ma.getQualifier() = va and
+ exists(
+ JavaxCryptoKeyGenerator jcg, KeyGeneratorInitConfiguration kgConfig,
+ DataFlow::PathNode source, DataFlow::PathNode dest
+ |
+ jcg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches("AES") and
+ source.getNode().asExpr() = jcg and
+ dest.getNode().asExpr() = ma.getQualifier() and
+ kgConfig.hasFlowPath(source, dest)
+ ) and
+ sink.asExpr() = ma.getArgument(0)
+ )
+ }
+}
+
+// ! below doesn't work for some reason...
+// predicate hasInsufficientKeySize2(DataFlow::PathNode source, DataFlow::PathNode sink) {
+// exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink))
+// or
+// exists(SymmetricKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink))
+// }
+// ******** Need the below for the above ********
+// ! move to Encryption.qll?
+/** The Java class `java.security.spec.ECGenParameterSpec`. */
+private class ECGenParameterSpec extends RefType {
+ ECGenParameterSpec() { this.hasQualifiedName("java.security.spec", "ECGenParameterSpec") }
+}
+
+/** The Java class `java.security.spec.ECGenParameterSpec`. */
+private class RSAGenParameterSpec extends RefType {
+ RSAGenParameterSpec() { this.hasQualifiedName("java.security.spec", "RSAKeyGenParameterSpec") }
+}
+
+// ! move to Encryption.qll?
+/** Returns the key size in the EC algorithm string */
+bindingset[algorithm]
+private int getECKeySize(string algorithm) {
+ algorithm.matches("sec%") and // specification such as "secp256r1"
+ result = algorithm.regexpCapture("sec[p|t](\\d+)[a-zA-Z].*", 1).toInt()
+ or
+ algorithm.matches("X9.62%") and //specification such as "X9.62 prime192v2"
+ result = algorithm.regexpCapture("X9\\.62 .*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
+ or
+ (algorithm.matches("prime%") or algorithm.matches("c2tnb%")) and //specification such as "prime192v2"
+ result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
+}
+
+// ! move to Encryption.qll?
+/** The `init` method declared in `javax.crypto.KeyGenerator`. */
+private class KeyGeneratorInitMethod extends Method {
+ KeyGeneratorInitMethod() {
+ this.getDeclaringType() instanceof KeyGenerator and
+ this.hasName("init")
+ }
+}
+
+// ! move to Encryption.qll?
+/** The `initialize` method declared in `java.security.KeyPairGenerator`. */
+private class KeyPairGeneratorInitMethod extends Method {
+ KeyPairGeneratorInitMethod() {
+ this.getDeclaringType() instanceof KeyPairGenerator and
+ this.hasName("initialize")
+ }
+}
+
+// ******* DATAFLOW ABOVE *************************************************************************
+// ************************************************************************************************
+// ************************************************************************************************
+// ************************************************************************************************
+// ************************************************************************************************
+// ************************************************************************************************
+// ******* OLD/UNUSED OR EXPERIMENTAL CODE BELOW **************************************************
+class UnsafeSymmetricKeySize extends IntegerLiteral {
+ UnsafeSymmetricKeySize() { this.getIntValue() < 128 }
+}
+
+class UnsafeAsymmetricKeySize extends IntegerLiteral {
+ UnsafeAsymmetricKeySize() { this.getIntValue() < 2048 }
+}
+
+/** Taint configuration tracking flow from a key generator to a `init` method call. */
+private class KeyGeneratorInitConfiguration extends TaintTracking::Configuration {
+ KeyGeneratorInitConfiguration() { this = "KeyGeneratorInitConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) {
+ source.asExpr() instanceof JavaxCryptoKeyGenerator
+ }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(MethodAccess ma |
+ ma.getMethod() instanceof KeyGeneratorInitMethod and
+ sink.asExpr() = ma.getQualifier()
+ )
+ }
+}
+
+/**
+ * Taint configuration tracking flow from a keypair generator to
+ * an `initialize` method call.
+ */
+private class KeyPairGeneratorInitConfiguration extends TaintTracking::Configuration {
+ KeyPairGeneratorInitConfiguration() { this = "KeyPairGeneratorInitConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) {
+ source.asExpr() instanceof JavaSecurityKeyPairGenerator
+ }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(MethodAccess ma |
+ ma.getMethod() instanceof KeyPairGeneratorInitMethod and
+ sink.asExpr() = ma.getQualifier()
+ )
+ }
+}
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
index c3a406514ac..81c21c7d00b 100644
--- a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
+++ b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
@@ -14,9 +14,11 @@
import java
import semmle.code.java.security.InsufficientKeySizeQuery
+// * Original:
//import DataFlow::PathGraph
// from Expr e, string msg
// where hasInsufficientKeySize(e, msg)
+// * Test data-flow config with just Asymmetric:
// select e, msg
// from
// AsymmetricKeyTrackingConfiguration cfg, DataFlow::PathNode source, DataFlow::PathNode sink,
@@ -26,19 +28,17 @@ import semmle.code.java.security.InsufficientKeySizeQuery
// cfg2.hasFlowPath(source, sink)
// select sink.getNode(), source, sink, "The $@ of an asymmetric key should be at least 2048 bits.",
// sink.getNode(), "size"
-// * Use Below
+// * Data-Flow path-graph with All configs: (but doesn't track algo name properly...)
// from DataFlow::PathNode source, DataFlow::PathNode sink
// where exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink)) //or
// //exists(AsymmetricECCKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink)) //or
// //exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlowPath(source, sink))
// select sink.getNode(), source, sink, "This $@ is too small, and flows to $@.", source.getNode(),
// "key size", sink.getNode(), "here"
-// * Use Above
-// * Use Below for taint-tracking with kpg
+// * Taint-tracking with kpg to track algo names
from DataFlow::Node source, DataFlow::Node sink
where
exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlow(source, sink)) or
exists(AsymmetricECCKeyTrackingConfiguration config2 | config2.hasFlow(source, sink)) or
exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlow(source, sink))
select sink, "This $@ is too small and creates a key $@.", source, "key size", sink, "here"
-// * Use Above for taint-tracking with kpg
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
index 75ba5e36e76..367a5227b14 100644
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
@@ -243,4 +243,12 @@ public class InsufficientKeySizeTest {
ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp112r1");
kpg.initialize(ecSpec); // $ hasInsufficientKeySize
}
+
+ // ToDo testing:
+ // todo #1: add tests for keysize variable passed to specs
+ // ? todo #2: add tests with DH and DSA specs? (or do those specs not make dev specify keysize?)
+ // ? todo #3: add test for retrieving a key from elsewhere?
+ // todo #4: add barrier-guard tests (see FP from OpenIdentityPlatform/OpenAM)
+ // ? todo #5: add tests for updated keysize variable?: e.g. keysize = 1024; keysize += 1024; so when it's used it is correctly 2048.
+ // ? todo #6: consider if some flow paths for keysize variables will be too hard to track how the keysize is updated (e.g. if calling some other method to get keysize, etc....)
}
From 0c2cff253f140b675a6f240a541bb104ecb511b6 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Mon, 10 Oct 2022 14:29:04 -0400
Subject: [PATCH 018/249] updates from discussing with Tony
---
.../java/security/InsufficientKeySizeQuery.qll | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
index c52ae2df550..8ae2be5c17f 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
@@ -7,7 +7,7 @@ import semmle.code.java.dataflow.DataFlow
/**
* Asymmetric (RSA, DSA, DH) key length data flow tracking configuration.
*/
-class AsymmetricKeyTrackingConfiguration extends TaintTracking2::Configuration {
+class AsymmetricKeyTrackingConfiguration extends DataFlow2::Configuration {
AsymmetricKeyTrackingConfiguration() { this = "AsymmetricKeyTrackingConfiguration" }
override predicate isSource(DataFlow::Node source) {
@@ -15,7 +15,7 @@ class AsymmetricKeyTrackingConfiguration extends TaintTracking2::Configuration {
// ! also need to look into specs for DSA and DH more
exists(ClassInstanceExpr rsaGenParamSpec |
rsaGenParamSpec.getConstructedType() instanceof RSAGenParameterSpec and
- rsaGenParamSpec.getArgument(0).(IntegerLiteral).getIntValue() < 2048 and
+ rsaGenParamSpec.getArgument(0).(CompileTimeConstantExpr).getIntValue() < 2048 and
source.asExpr() = rsaGenParamSpec
)
or
@@ -34,7 +34,7 @@ class AsymmetricKeyTrackingConfiguration extends TaintTracking2::Configuration {
dest.getNode().asExpr() = ma.getQualifier() and
kpgConfig.hasFlowPath(source, dest)
) and
- sink.asExpr() = ma.getArgument(0)
+ sink.asExpr() = ma.getArgument(0) // ! todo: add spec as a sink
)
}
}
@@ -42,7 +42,7 @@ class AsymmetricKeyTrackingConfiguration extends TaintTracking2::Configuration {
/**
* Asymmetric (EC) key length data flow tracking configuration.
*/
-class AsymmetricECCKeyTrackingConfiguration extends TaintTracking2::Configuration {
+class AsymmetricECCKeyTrackingConfiguration extends DataFlow2::Configuration {
AsymmetricECCKeyTrackingConfiguration() { this = "AsymmetricECCKeyTrackingConfiguration" }
override predicate isSource(DataFlow::Node source) {
@@ -75,7 +75,7 @@ class AsymmetricECCKeyTrackingConfiguration extends TaintTracking2::Configuratio
/**
* Symmetric (AES) key length data flow tracking configuration.
*/
-class SymmetricKeyTrackingConfiguration extends TaintTracking2::Configuration {
+class SymmetricKeyTrackingConfiguration extends DataFlow2::Configuration {
SymmetricKeyTrackingConfiguration() { this = "SymmetricKeyTrackingConfiguration2" }
override predicate isSource(DataFlow::Node source) {
@@ -107,7 +107,7 @@ class SymmetricKeyTrackingConfiguration extends TaintTracking2::Configuration {
// }
// ******** Need the below models for the above configs ********
/** Taint configuration tracking flow from a key generator to a `init` method call. */
-private class KeyGeneratorInitConfiguration extends TaintTracking::Configuration {
+private class KeyGeneratorInitConfiguration extends DataFlow::Configuration {
KeyGeneratorInitConfiguration() { this = "KeyGeneratorInitConfiguration" }
override predicate isSource(DataFlow::Node source) {
@@ -126,7 +126,7 @@ private class KeyGeneratorInitConfiguration extends TaintTracking::Configuration
* Taint configuration tracking flow from a keypair generator to
* an `initialize` method call.
*/
-private class KeyPairGeneratorInitConfiguration extends TaintTracking::Configuration {
+private class KeyPairGeneratorInitConfiguration extends DataFlow::Configuration {
KeyPairGeneratorInitConfiguration() { this = "KeyPairGeneratorInitConfiguration" }
override predicate isSource(DataFlow::Node source) {
From bd76b1fcc0aa52820542a53449a7176957aa4fb6 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Mon, 10 Oct 2022 16:18:49 -0400
Subject: [PATCH 019/249] clean-up and update configurations to have specs as
sink
---
.../security/InsufficientKeySizeQuery.qll | 123 +++++++++---------
.../CWE/CWE-326/InsufficientKeySize.ql | 26 +---
.../CWE-326/InsufficientKeySizeTest.java | 73 +++++------
.../CWE-326/InsufficientKeySizeTest.ql | 18 +--
4 files changed, 99 insertions(+), 141 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
index 8ae2be5c17f..ed7408facf9 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
@@ -1,24 +1,14 @@
import semmle.code.java.security.Encryption
-import semmle.code.java.dataflow.TaintTracking2
-import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.DataFlow
+import semmle.code.java.dataflow.DataFlow2
-// ******* DATAFLOW BELOW *************************************************************************
/**
* Asymmetric (RSA, DSA, DH) key length data flow tracking configuration.
*/
-class AsymmetricKeyTrackingConfiguration extends DataFlow2::Configuration {
- AsymmetricKeyTrackingConfiguration() { this = "AsymmetricKeyTrackingConfiguration" }
+class AsymmetricNonECKeyTrackingConfiguration extends DataFlow2::Configuration {
+ AsymmetricNonECKeyTrackingConfiguration() { this = "AsymmetricNonECKeyTrackingConfiguration" }
override predicate isSource(DataFlow::Node source) {
- // ! may need to change below to still use `keysize` variable as the source, not the spec
- // ! also need to look into specs for DSA and DH more
- exists(ClassInstanceExpr rsaGenParamSpec |
- rsaGenParamSpec.getConstructedType() instanceof RSAGenParameterSpec and
- rsaGenParamSpec.getArgument(0).(CompileTimeConstantExpr).getIntValue() < 2048 and
- source.asExpr() = rsaGenParamSpec
- )
- or
source.asExpr().(IntegerLiteral).getIntValue() < 2048
}
@@ -34,7 +24,23 @@ class AsymmetricKeyTrackingConfiguration extends DataFlow2::Configuration {
dest.getNode().asExpr() = ma.getQualifier() and
kpgConfig.hasFlowPath(source, dest)
) and
- sink.asExpr() = ma.getArgument(0) // ! todo: add spec as a sink
+ sink.asExpr() = ma.getArgument(0)
+ )
+ or
+ // TODO: combine below three for less duplicated code
+ exists(ClassInstanceExpr rsaKeyGenParamSpec |
+ rsaKeyGenParamSpec.getConstructedType() instanceof RSAKeyGenParameterSpec and
+ sink.asExpr() = rsaKeyGenParamSpec.getArgument(0)
+ )
+ or
+ exists(ClassInstanceExpr dsaGenParamSpec |
+ dsaGenParamSpec.getConstructedType() instanceof DSAGenParameterSpec and
+ sink.asExpr() = dsaGenParamSpec.getArgument(0)
+ )
+ or
+ exists(ClassInstanceExpr dhGenParamSpec |
+ dhGenParamSpec.getConstructedType() instanceof DHGenParameterSpec and
+ sink.asExpr() = dhGenParamSpec.getArgument(0)
)
}
}
@@ -42,17 +48,12 @@ class AsymmetricKeyTrackingConfiguration extends DataFlow2::Configuration {
/**
* Asymmetric (EC) key length data flow tracking configuration.
*/
-class AsymmetricECCKeyTrackingConfiguration extends DataFlow2::Configuration {
- AsymmetricECCKeyTrackingConfiguration() { this = "AsymmetricECCKeyTrackingConfiguration" }
+class AsymmetricECKeyTrackingConfiguration extends DataFlow2::Configuration {
+ AsymmetricECKeyTrackingConfiguration() { this = "AsymmetricECKeyTrackingConfiguration" }
override predicate isSource(DataFlow::Node source) {
- // ! may need to change below to still use `keysize` variable as the source, not the spec
- exists(ClassInstanceExpr ecGenParamSpec |
- getECKeySize(ecGenParamSpec.getArgument(0).(StringLiteral).getValue()) < 256 and
- source.asExpr() = ecGenParamSpec
- )
- or
- source.asExpr().(IntegerLiteral).getIntValue() < 256
+ source.asExpr().(IntegerLiteral).getIntValue() < 256 or
+ getECKeySize(source.asExpr().(StringLiteral).getValue()) < 256 // need this for the cases when the key size is embedded in the curve name.
}
override predicate isSink(DataFlow::Node sink) {
@@ -69,6 +70,12 @@ class AsymmetricECCKeyTrackingConfiguration extends DataFlow2::Configuration {
) and
sink.asExpr() = ma.getArgument(0)
)
+ or
+ exists(ClassInstanceExpr ecGenParamSpec |
+ ecGenParamSpec.getConstructedType() instanceof ECGenParameterSpec and
+ //getECKeySize(ecGenParamSpec.getArgument(0).(StringLiteral).getValue()) < 256 and
+ sink.asExpr() = ecGenParamSpec.getArgument(0)
+ )
}
}
@@ -76,7 +83,7 @@ class AsymmetricECCKeyTrackingConfiguration extends DataFlow2::Configuration {
* Symmetric (AES) key length data flow tracking configuration.
*/
class SymmetricKeyTrackingConfiguration extends DataFlow2::Configuration {
- SymmetricKeyTrackingConfiguration() { this = "SymmetricKeyTrackingConfiguration2" }
+ SymmetricKeyTrackingConfiguration() { this = "SymmetricKeyTrackingConfiguration" }
override predicate isSource(DataFlow::Node source) {
source.asExpr().(IntegerLiteral).getIntValue() < 128
@@ -99,14 +106,9 @@ class SymmetricKeyTrackingConfiguration extends DataFlow2::Configuration {
}
}
-// ! below predicate doesn't work
-// predicate hasInsufficientKeySize2(DataFlow::PathNode source, DataFlow::PathNode sink) {
-// exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink))
-// or
-// exists(SymmetricKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink))
-// }
-// ******** Need the below models for the above configs ********
-/** Taint configuration tracking flow from a key generator to a `init` method call. */
+// ********************** Need the below models for the above configs **********************
+// todo: move some/all of below to Encryption.qll or elsewhere?
+/** Data flow configuration tracking flow from a key generator to an `init` method call. */
private class KeyGeneratorInitConfiguration extends DataFlow::Configuration {
KeyGeneratorInitConfiguration() { this = "KeyGeneratorInitConfiguration" }
@@ -122,10 +124,7 @@ private class KeyGeneratorInitConfiguration extends DataFlow::Configuration {
}
}
-/**
- * Taint configuration tracking flow from a keypair generator to
- * an `initialize` method call.
- */
+/** Data flow configuration tracking flow from a keypair generator to an `initialize` method call. */
private class KeyPairGeneratorInitConfiguration extends DataFlow::Configuration {
KeyPairGeneratorInitConfiguration() { this = "KeyPairGeneratorInitConfiguration" }
@@ -141,28 +140,24 @@ private class KeyPairGeneratorInitConfiguration extends DataFlow::Configuration
}
}
-// ! move some/all of below to Encryption.qll or elsewhere?
/** The Java class `java.security.spec.ECGenParameterSpec`. */
private class ECGenParameterSpec extends RefType {
ECGenParameterSpec() { this.hasQualifiedName("java.security.spec", "ECGenParameterSpec") }
}
-/** The Java class `java.security.spec.ECGenParameterSpec`. */
-private class RSAGenParameterSpec extends RefType {
- RSAGenParameterSpec() { this.hasQualifiedName("java.security.spec", "RSAKeyGenParameterSpec") }
+/** The Java class `java.security.spec.RSAKeyGenParameterSpec`. */
+private class RSAKeyGenParameterSpec extends RefType {
+ RSAKeyGenParameterSpec() { this.hasQualifiedName("java.security.spec", "RSAKeyGenParameterSpec") }
}
-/** Returns the key size in the EC algorithm string */
-bindingset[algorithm]
-private int getECKeySize(string algorithm) {
- algorithm.matches("sec%") and // specification such as "secp256r1"
- result = algorithm.regexpCapture("sec[p|t](\\d+)[a-zA-Z].*", 1).toInt()
- or
- algorithm.matches("X9.62%") and //specification such as "X9.62 prime192v2"
- result = algorithm.regexpCapture("X9\\.62 .*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
- or
- (algorithm.matches("prime%") or algorithm.matches("c2tnb%")) and //specification such as "prime192v2"
- result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
+/** The Java class `java.security.spec.DSAGenParameterSpec`. */
+private class DSAGenParameterSpec extends RefType {
+ DSAGenParameterSpec() { this.hasQualifiedName("java.security.spec", "DSAGenParameterSpec") }
+}
+
+/** The Java class `javax.crypto.spec.DHGenParameterSpec`. */
+private class DHGenParameterSpec extends RefType {
+ DHGenParameterSpec() { this.hasQualifiedName("javax.crypto.spec", "DHGenParameterSpec") }
}
/** The `init` method declared in `javax.crypto.KeyGenerator`. */
@@ -181,21 +176,21 @@ private class KeyPairGeneratorInitMethod extends Method {
}
}
+/** Returns the key size in the EC algorithm string */
+bindingset[algorithm]
+private int getECKeySize(string algorithm) {
+ algorithm.matches("sec%") and // specification such as "secp256r1"
+ result = algorithm.regexpCapture("sec[p|t](\\d+)[a-zA-Z].*", 1).toInt()
+ or
+ algorithm.matches("X9.62%") and //specification such as "X9.62 prime192v2"
+ result = algorithm.regexpCapture("X9\\.62 .*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
+ or
+ (algorithm.matches("prime%") or algorithm.matches("c2tnb%")) and //specification such as "prime192v2"
+ result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
+}
// ******* DATAFLOW ABOVE *************************************************************************
-// ************************************************************************************************
-// ************************************************************************************************
-// ******* OLD/UNUSED OR EXPERIMENTAL CODE BELOW **************************************************
-class UnsafeSymmetricKeySize extends IntegerLiteral {
- UnsafeSymmetricKeySize() { this.getIntValue() < 128 }
-}
-
-class UnsafeAsymmetricKeySize extends IntegerLiteral {
- UnsafeAsymmetricKeySize() { this.getIntValue() < 2048 }
-}
// TODO:
-// ! todo #0a: find a better way to combine the two needed taint-tracking configs so can go back to having a path-graph...
-// ! todo #0b: possible to combine the 3 dataflow configs?
// todo #1: make representation of source that can be shared across the configs
// todo #2: make representation of sink that can be shared across the configs
// todo #3: make list of algo names more easily reusable (either as constant-type variable at top of file, or model as own class to share, etc.)
-// todo #4: refactor to be more like the Python version? (or not possible because of lack of DataFlow::Node for void method in Java?)
+// todo #4: refactor to be more like the Python (or C#) version? (or not possible because of lack of DataFlow::Node for void method in Java?)
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
index 81c21c7d00b..a5401dfb157 100644
--- a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
+++ b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
@@ -14,31 +14,9 @@
import java
import semmle.code.java.security.InsufficientKeySizeQuery
-// * Original:
-//import DataFlow::PathGraph
-// from Expr e, string msg
-// where hasInsufficientKeySize(e, msg)
-// * Test data-flow config with just Asymmetric:
-// select e, msg
-// from
-// AsymmetricKeyTrackingConfiguration cfg, DataFlow::PathNode source, DataFlow::PathNode sink,
-// KeyTrackingConfiguration cfg2 //, DataFlow::PathNode source2, DataFlow::PathNode sink2
-// where
-// //cfg.hasFlowPath(source, sink) //or
-// cfg2.hasFlowPath(source, sink)
-// select sink.getNode(), source, sink, "The $@ of an asymmetric key should be at least 2048 bits.",
-// sink.getNode(), "size"
-// * Data-Flow path-graph with All configs: (but doesn't track algo name properly...)
-// from DataFlow::PathNode source, DataFlow::PathNode sink
-// where exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink)) //or
-// //exists(AsymmetricECCKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink)) //or
-// //exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlowPath(source, sink))
-// select sink.getNode(), source, sink, "This $@ is too small, and flows to $@.", source.getNode(),
-// "key size", sink.getNode(), "here"
-// * Taint-tracking with kpg to track algo names
from DataFlow::Node source, DataFlow::Node sink
where
- exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlow(source, sink)) or
- exists(AsymmetricECCKeyTrackingConfiguration config2 | config2.hasFlow(source, sink)) or
+ exists(AsymmetricNonECKeyTrackingConfiguration config1 | config1.hasFlow(source, sink)) or
+ exists(AsymmetricECKeyTrackingConfiguration config2 | config2.hasFlow(source, sink)) or
exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlow(source, sink))
select sink, "This $@ is too small and creates a key $@.", source, "key size", sink, "here"
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
index 367a5227b14..9ba11f1c632 100644
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
@@ -36,8 +36,8 @@ public class InsufficientKeySizeTest {
// test with spec
// BAD: Key size is less than 2048
KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("RSA");
- RSAKeyGenParameterSpec rsaSpec = new RSAKeyGenParameterSpec(1024, null);
- keyPairGen3.initialize(rsaSpec); // $ hasInsufficientKeySize
+ RSAKeyGenParameterSpec rsaSpec = new RSAKeyGenParameterSpec(1024, null); // $ hasInsufficientKeySize
+ keyPairGen3.initialize(rsaSpec);
// BAD: Key size is less than 2048
KeyPairGenerator keyPairGen4 = KeyPairGenerator.getInstance("RSA");
@@ -54,15 +54,15 @@ public class InsufficientKeySizeTest {
KeyPairGenerator keyPairGen4 = KeyPairGenerator.getInstance("DSA");
keyPairGen4.initialize(2048); // Safe
- // test with spec?
- // // BAD: Key size is less than 2048
- // KeyPairGenerator keyPairGen5 = KeyPairGenerator.getInstance("DSA");
- // DSAGenParameterSpec dsaSpec = new DSAGenParameterSpec(1024, null);
- // keyPairGen5.initialize(dsaSpec); // $ hasInsufficientKeySize
+ // test with spec
+ // BAD: Key size is less than 2048
+ KeyPairGenerator keyPairGen5 = KeyPairGenerator.getInstance("DSA");
+ DSAGenParameterSpec dsaSpec = new DSAGenParameterSpec(1024, 0); // $ hasInsufficientKeySize
+ keyPairGen5.initialize(dsaSpec);
- // // BAD: Key size is less than 2048
- // KeyPairGenerator keyPairGen6 = KeyPairGenerator.getInstance("DSA");
- // keyPairGen6.initialize(new DSAGenParameterSpec(1024, null)); // $ hasInsufficientKeySize
+ // BAD: Key size is less than 2048
+ KeyPairGenerator keyPairGen6 = KeyPairGenerator.getInstance("DSA");
+ keyPairGen6.initialize(new DSAGenParameterSpec(1024, 0)); // $ hasInsufficientKeySize
}
// DH (Asymmetric)
@@ -75,15 +75,15 @@ public class InsufficientKeySizeTest {
KeyPairGenerator keyPairGen17 = KeyPairGenerator.getInstance("DH");
keyPairGen17.initialize(2048); // Safe
- // test with spec?
- // // BAD: Key size is less than 2048
- // KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("DH");
- // DHGenParameterSpec dhSpec = new DHGenParameterSpec(1024, null);
- // keyPairGen3.initialize(dhSpec); // $ hasInsufficientKeySize
+ // test with spec
+ // BAD: Key size is less than 2048
+ KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("DH");
+ DHGenParameterSpec dhSpec = new DHGenParameterSpec(1024, 0); // $ hasInsufficientKeySize
+ keyPairGen3.initialize(dhSpec);
- // // BAD: Key size is less than 2048
- // KeyPairGenerator keyPairGen4 = KeyPairGenerator.getInstance("DH");
- // keyPairGen4.initialize(new DHGenParameterSpec(1024, null)); // $ hasInsufficientKeySize
+ // BAD: Key size is less than 2048
+ KeyPairGenerator keyPairGen4 = KeyPairGenerator.getInstance("DH");
+ keyPairGen4.initialize(new DHGenParameterSpec(1024, 0)); // $ hasInsufficientKeySize
}
// EC (Asymmetric)
@@ -91,8 +91,8 @@ public class InsufficientKeySizeTest {
{
// BAD: Key size is less than 256
KeyPairGenerator keyPairGen5 = KeyPairGenerator.getInstance("EC");
- ECGenParameterSpec ecSpec1 = new ECGenParameterSpec("secp112r1");
- keyPairGen5.initialize(ecSpec1); // $ hasInsufficientKeySize
+ ECGenParameterSpec ecSpec1 = new ECGenParameterSpec("secp112r1"); // $ hasInsufficientKeySize
+ keyPairGen5.initialize(ecSpec1);
// BAD: Key size is less than 256
KeyPairGenerator keyPairGen6 = KeyPairGenerator.getInstance("EC");
@@ -105,18 +105,18 @@ public class InsufficientKeySizeTest {
// BAD: Key size is less than 256
KeyPairGenerator keyPairGen8 = KeyPairGenerator.getInstance("EC");
- ECGenParameterSpec ecSpec3 = new ECGenParameterSpec("X9.62 prime192v2");
- keyPairGen8.initialize(ecSpec3); // $ hasInsufficientKeySize
+ ECGenParameterSpec ecSpec3 = new ECGenParameterSpec("X9.62 prime192v2"); // $ hasInsufficientKeySize
+ keyPairGen8.initialize(ecSpec3);
// BAD: Key size is less than 256
KeyPairGenerator keyPairGen9 = KeyPairGenerator.getInstance("EC");
- ECGenParameterSpec ecSpec4 = new ECGenParameterSpec("X9.62 c2tnb191v3");
- keyPairGen9.initialize(ecSpec4); // $ hasInsufficientKeySize
+ ECGenParameterSpec ecSpec4 = new ECGenParameterSpec("X9.62 c2tnb191v3"); // $ hasInsufficientKeySize
+ keyPairGen9.initialize(ecSpec4);
// BAD: Key size is less than 256
KeyPairGenerator keyPairGen10 = KeyPairGenerator.getInstance("EC");
- ECGenParameterSpec ecSpec5 = new ECGenParameterSpec("sect163k1");
- keyPairGen10.initialize(ecSpec5); // $ hasInsufficientKeySize
+ ECGenParameterSpec ecSpec5 = new ECGenParameterSpec("sect163k1"); // $ hasInsufficientKeySize
+ keyPairGen10.initialize(ecSpec5);
// GOOD: Key size is no less than 256
KeyPairGenerator keyPairGen11 = KeyPairGenerator.getInstance("EC");
@@ -125,8 +125,8 @@ public class InsufficientKeySizeTest {
// BAD: Key size is less than 256
KeyPairGenerator keyPairGen12 = KeyPairGenerator.getInstance("EC");
- ECGenParameterSpec ecSpec7 = new ECGenParameterSpec("prime192v2");
- keyPairGen12.initialize(ecSpec7); // $ hasInsufficientKeySize
+ ECGenParameterSpec ecSpec7 = new ECGenParameterSpec("prime192v2"); // $ hasInsufficientKeySize
+ keyPairGen12.initialize(ecSpec7);
// GOOD: Key size is no less than 256
KeyPairGenerator keyPairGen13 = KeyPairGenerator.getInstance("EC");
@@ -135,8 +135,8 @@ public class InsufficientKeySizeTest {
// BAD: Key size is less than 256
KeyPairGenerator keyPairGen14 = KeyPairGenerator.getInstance("EC");
- ECGenParameterSpec ecSpec9 = new ECGenParameterSpec("c2tnb191v1");
- keyPairGen14.initialize(ecSpec9); // $ hasInsufficientKeySize
+ ECGenParameterSpec ecSpec9 = new ECGenParameterSpec("c2tnb191v1"); // $ hasInsufficientKeySize
+ keyPairGen14.initialize(ecSpec9);
// GOOD: Key size is no less than 256
KeyPairGenerator keyPairGen15 = KeyPairGenerator.getInstance("EC");
@@ -194,7 +194,7 @@ public class InsufficientKeySizeTest {
// Test variable passed to other method(s) - Asymmetric, EC
{
- ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp112r1"); // test ECGenParameterSpec variable
+ ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp112r1"); // $ hasInsufficientKeySize // test ECGenParameterSpec variable
KeyPairGenerator keyPairGen22 = KeyPairGenerator.getInstance("EC"); // test KeyPairGenerator variable
testAsymmetricEC(ecSpec, keyPairGen22);
@@ -237,18 +237,17 @@ public class InsufficientKeySizeTest {
public static void testAsymmetricEC(ECGenParameterSpec spec, KeyPairGenerator kpg) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
// BAD: Key size is less than 256
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("EC");
- keyPairGen.initialize(spec); // $ hasInsufficientKeySize
+ keyPairGen.initialize(spec); // sink is now at above where `spec` variable is initialized
// BAD: Key size is less than 256
- ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp112r1");
- kpg.initialize(ecSpec); // $ hasInsufficientKeySize
+ ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp112r1"); // $ hasInsufficientKeySize
+ kpg.initialize(ecSpec);
}
// ToDo testing:
- // todo #1: add tests for keysize variable passed to specs
- // ? todo #2: add tests with DH and DSA specs? (or do those specs not make dev specify keysize?)
+ // ? todo #1: add tests for keysize variable passed to specs - not needed if spec is sink now
// ? todo #3: add test for retrieving a key from elsewhere?
- // todo #4: add barrier-guard tests (see FP from OpenIdentityPlatform/OpenAM)
+ // ? todo #4: add barrier-guard tests (see FP from OpenIdentityPlatform/OpenAM)
// ? todo #5: add tests for updated keysize variable?: e.g. keysize = 1024; keysize += 1024; so when it's used it is correctly 2048.
// ? todo #6: consider if some flow paths for keysize variables will be too hard to track how the keysize is updated (e.g. if calling some other method to get keysize, etc....)
}
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
index 93ad76704c2..eef02e9352a 100644
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
@@ -7,25 +7,11 @@ class InsufficientKeySizeTest extends InlineExpectationsTest {
override string getARelevantTag() { result = "hasInsufficientKeySize" }
- // * Path-problem
- // override predicate hasActualResult(Location location, string element, string tag, string value) {
- // tag = "hasInsufficientKeySize" and
- // exists(DataFlow::PathNode source, DataFlow::PathNode sink |
- // exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink)) or
- // exists(AsymmetricECCKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink)) or
- // exists(SymmetricKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink))
- // |
- // sink.getNode().getLocation() = location and
- // element = sink.getNode().toString() and
- // value = ""
- // )
- // }
- // * Not path-problem
override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "hasInsufficientKeySize" and
exists(DataFlow::Node source, DataFlow::Node sink |
- exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlow(source, sink)) or
- exists(AsymmetricECCKeyTrackingConfiguration config2 | config2.hasFlow(source, sink)) or
+ exists(AsymmetricNonECKeyTrackingConfiguration config1 | config1.hasFlow(source, sink)) or
+ exists(AsymmetricECKeyTrackingConfiguration config2 | config2.hasFlow(source, sink)) or
exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlow(source, sink))
|
sink.getLocation() = location and
From b6a8c27d4858e2aee9c29d31d128fdcce6e8f0be Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Mon, 10 Oct 2022 16:22:42 -0400
Subject: [PATCH 020/249] delete experimental files
---
.../semmle/code/java/security/Encryption.qll | 5 +-
...BeforeAddingTaintFlowBackForAsymmetric.qll | 264 ------------------
...an-UpForDoubleTaintTracking-2022-10-10.qll | 239 ----------------
...ientKeySizeQuery_BeforeDataFlowRewrite.qll | 182 ------------
4 files changed, 1 insertion(+), 689 deletions(-)
delete mode 100644 java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery_BeforeAddingTaintFlowBackForAsymmetric.qll
delete mode 100644 java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery_BeforeClean-UpForDoubleTaintTracking-2022-10-10.qll
delete mode 100644 java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery_BeforeDataFlowRewrite.qll
diff --git a/java/ql/lib/semmle/code/java/security/Encryption.qll b/java/ql/lib/semmle/code/java/security/Encryption.qll
index c19d51a2f4b..39e3f2d2110 100644
--- a/java/ql/lib/semmle/code/java/security/Encryption.qll
+++ b/java/ql/lib/semmle/code/java/security/Encryption.qll
@@ -249,10 +249,7 @@ string getASecureAlgorithmName() {
result =
[
"RSA", "SHA256", "SHA512", "CCM", "GCM", "AES(?)",
- "Blowfish", "ECIES" // ! Blowfish not actually secure based on https://rules.sonarsource.com/java/type/Vulnerability/RSPEC-4426 ??
- // ! hmm, other sources imply that it is secure...
- // ! also no DH here, etc.?
- // ! also is ECB matched with AES?
+ "Blowfish", "ECIES"
]
}
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery_BeforeAddingTaintFlowBackForAsymmetric.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery_BeforeAddingTaintFlowBackForAsymmetric.qll
deleted file mode 100644
index 720ed112981..00000000000
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery_BeforeAddingTaintFlowBackForAsymmetric.qll
+++ /dev/null
@@ -1,264 +0,0 @@
-import semmle.code.java.security.Encryption
-import semmle.code.java.dataflow.TaintTracking
-import semmle.code.java.dataflow.DataFlow
-
-//import DataFlow::PathGraph
-/**
- * Asymmetric (RSA, DSA, DH) key length data flow tracking configuration.
- */
-class AsymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
- AsymmetricKeyTrackingConfiguration() { this = "AsymmetricKeyTrackingConfiguration" }
-
- override predicate isSource(DataFlow::Node source) {
- exists(IntegerLiteral integer, VarAccess var |
- integer.getIntValue() < 2048 and
- source.asExpr() = integer
- or
- var.getVariable().getInitializer().getUnderlyingExpr() instanceof IntegerLiteral and
- var.getVariable().getInitializer().getUnderlyingExpr().toString().toInt() < 2048 and
- source.asExpr() = var.getVariable().getInitializer()
- )
- }
-
- override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma |
- ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- sink.asExpr() = ma.getArgument(0)
- )
- }
-}
-
-/** The Java class `java.security.spec.ECGenParameterSpec`. */
-private class ECGenParameterSpec extends RefType {
- ECGenParameterSpec() { this.hasQualifiedName("java.security.spec", "ECGenParameterSpec") }
-}
-
-/** The `init` method declared in `javax.crypto.KeyGenerator`. */
-private class KeyGeneratorInitMethod extends Method {
- KeyGeneratorInitMethod() {
- this.getDeclaringType() instanceof KeyGenerator and
- this.hasName("init")
- }
-}
-
-/** The `initialize` method declared in `java.security.KeyPairGenerator`. */
-private class KeyPairGeneratorInitMethod extends Method {
- KeyPairGeneratorInitMethod() {
- this.getDeclaringType() instanceof KeyPairGenerator and
- this.hasName("initialize")
- }
-}
-
-/** Returns the key size in the EC algorithm string */
-bindingset[algorithm]
-private int getECKeySize(string algorithm) {
- algorithm.matches("sec%") and // specification such as "secp256r1"
- result = algorithm.regexpCapture("sec[p|t](\\d+)[a-zA-Z].*", 1).toInt()
- or
- algorithm.matches("X9.62%") and //specification such as "X9.62 prime192v2"
- result = algorithm.regexpCapture("X9\\.62 .*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
- or
- (algorithm.matches("prime%") or algorithm.matches("c2tnb%")) and //specification such as "prime192v2"
- result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
-}
-
-/** Taint configuration tracking flow from a key generator to a `init` method call. */
-private class KeyGeneratorInitConfiguration extends TaintTracking::Configuration {
- KeyGeneratorInitConfiguration() { this = "KeyGeneratorInitConfiguration" }
-
- override predicate isSource(DataFlow::Node source) {
- source.asExpr() instanceof JavaxCryptoKeyGenerator
- }
-
- override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma |
- ma.getMethod() instanceof KeyGeneratorInitMethod and
- sink.asExpr() = ma.getQualifier()
- )
- }
-}
-
-/**
- * Taint configuration tracking flow from a keypair generator to
- * an `initialize` method call.
- */
-private class KeyPairGeneratorInitConfiguration extends TaintTracking::Configuration {
- KeyPairGeneratorInitConfiguration() { this = "KeyPairGeneratorInitConfiguration" }
-
- override predicate isSource(DataFlow::Node source) {
- source.asExpr() instanceof JavaSecurityKeyPairGenerator
- }
-
- override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma |
- ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- sink.asExpr() = ma.getQualifier()
- )
- }
-}
-
-/**
- * Holds if a symmetric `KeyGenerator` implementing encryption algorithm
- * `type` and initialized by `ma` uses an insufficient key size.
- *
- * `msg` provides a human-readable description of the problem.
- */
-bindingset[type]
-private predicate hasShortSymmetricKey(MethodAccess ma, string msg, string type) {
- ma.getMethod() instanceof KeyGeneratorInitMethod and
- // flow needed to correctly determine algorithm type and
- // not match to ANY symmetric algorithm (although doesn't really matter since only have AES currently...)
- exists(
- JavaxCryptoKeyGenerator jcg, KeyGeneratorInitConfiguration cc, DataFlow::PathNode source,
- DataFlow::PathNode dest
- |
- jcg.getAlgoSpec().(StringLiteral).getValue() = type and
- source.getNode().asExpr() = jcg and
- dest.getNode().asExpr() = ma.getQualifier() and
- cc.hasFlowPath(source, dest)
- ) and
- (
- // VarAccess case needed to handle FN of key-size stored in a variable
- // Note: cannot use CompileTimeConstantExpr since will miss cases when variable is not a compile-time constant
- // (e.g. not declared `final` in Java)
- exists(VarAccess var |
- var.getVariable().getInitializer().getUnderlyingExpr() instanceof IntegerLiteral and
- var.getVariable().getInitializer().getUnderlyingExpr().toString().toInt() < 128 and
- ma.getArgument(0) = var
- )
- or
- // exists(CompileTimeConstantExpr var |
- // //var.getUnderlyingExpr() instanceof IntegerLiteral and // can't include this...
- // var.getIntValue() < 128 and
- // ma.getArgument(0) = var
- // )
- // or
- ma.getArgument(0).(IntegerLiteral).getIntValue() < 128
- ) and
- msg = "Key size should be at least 128 bits for " + type + " encryption."
-}
-
-/**
- * Holds if an AES `KeyGenerator` initialized by `ma` uses an insufficient key size.
- * `msg` provides a human-readable description of the problem.
- */
-private predicate hasShortAESKey(MethodAccess ma, string msg) {
- hasShortSymmetricKey(ma, msg, "AES")
-}
-
-/**
- * Holds if an asymmetric `KeyPairGenerator` implementing encryption algorithm
- * `type` and initialized by `ma` uses an insufficient key size.
- *
- * `msg` provides a human-readable description of the problem.
- */
-bindingset[type]
-private predicate hasShortAsymmetricKeyPair(MethodAccess ma, string msg, string type) {
- ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- //ma.getQualifier() instanceof JavaSecurityKeyPairGenerator and
- //ma.getQualifier().getBasicBlock() instanceof JavaSecurityKeyPairGenerator and
- // * USE BELOW
- ma.getQualifier().getBasicBlock().getAPredecessor() instanceof JavaSecurityKeyPairGenerator and
- // * USE ABOVE
- //ma.getQualifier().getBasicBlock().getNode(2) instanceof JavaSecurityKeyPairGenerator and
- // ma.getQualifier()
- // .getBasicBlock()
- // .getANode()
- // .(JavaSecurityKeyPairGenerator)
- // .getAlgoSpec()
- // .(StringLiteral)
- // .getValue()
- // .toUpperCase() = type and
- //ma.getQualifier().getBasicBlock().getAPredecessor() instanceof JavaSecurityKeyPairGenerator and
- // * USE BELOW
- ma.getQualifier()
- .getBasicBlock()
- .getAPredecessor()
- .(JavaSecurityKeyPairGenerator)
- .getAlgoSpec()
- .(StringLiteral)
- .getValue()
- .toUpperCase() = type and
- // * USE ABOVE
- // flow needed to correctly determine algorithm type and
- // not match to ANY asymmetric algorithm
- // * REMOVE BELOW
- // exists(
- // JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kc,
- // DataFlow::PathNode source, DataFlow::PathNode dest
- // |
- // jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase() = type and
- // source.getNode().asExpr() = jpg and
- // dest.getNode().asExpr() = ma.getQualifier() and
- // kc.hasFlowPath(source, dest)
- // ) and
- // * REMOVE ABOVE
- // VarAccess case needed to handle FN of key-size stored in a variable
- // Note: cannot use CompileTimeConstantExpr since will miss cases when variable is not a compile-time constant
- // (e.g. not declared `final` in Java)
- (
- exists(VarAccess var |
- var.getVariable().getInitializer().getUnderlyingExpr() instanceof IntegerLiteral and
- var.getVariable().getInitializer().getUnderlyingExpr().toString().toInt() < 2048 and
- ma.getArgument(0) = var
- )
- or
- ma.getArgument(0).(IntegerLiteral).getIntValue() < 2048
- // or
- // exists(
- // AsymmetricKeyTrackingConfiguration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
- // |
- // cfg.hasFlowPath(source, sink)
- // )
- ) and
- msg = "Key size should be at least 2048 bits for " + type + " encryption."
-}
-
-/**
- * Holds if a DSA `KeyPairGenerator` initialized by `ma` uses an insufficient key size.
- *
- * `msg` provides a human-readable description of the problem.
- */
-private predicate hasShortDsaKeyPair(MethodAccess ma, string msg) {
- hasShortAsymmetricKeyPair(ma, msg, "DSA") or
- hasShortAsymmetricKeyPair(ma, msg, "DH")
-}
-
-/**
- * Holds if a RSA `KeyPairGenerator` initialized by `ma` uses an insufficient key size.
- *
- * `msg` provides a human-readable description of the problem.
- */
-private predicate hasShortRsaKeyPair(MethodAccess ma, string msg) {
- hasShortAsymmetricKeyPair(ma, msg, "RSA")
-}
-
-/**
- * Holds if an EC `KeyPairGenerator` initialized by `ma` uses an insufficient key size.
- *
- * `msg` provides a human-readable description of the problem.
- */
-private predicate hasShortECKeyPair(MethodAccess ma, string msg) {
- ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- exists(
- JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kc,
- DataFlow::PathNode source, DataFlow::PathNode dest, ClassInstanceExpr cie
- |
- jpg.getAlgoSpec().(StringLiteral).getValue().matches("EC%") and // ECC variants such as ECDH and ECDSA
- source.getNode().asExpr() = jpg and
- dest.getNode().asExpr() = ma.getQualifier() and
- kc.hasFlowPath(source, dest) and
- DataFlow::localExprFlow(cie, ma.getArgument(0)) and
- ma.getArgument(0).getType() instanceof ECGenParameterSpec and
- getECKeySize(cie.getArgument(0).(StringLiteral).getValue()) < 256
- ) and
- msg = "Key size should be at least 256 bits for EC encryption."
-}
-
-// ! refactor this so can use 'path-problem' select clause instead?
-predicate hasInsufficientKeySize(Expr e, string msg) {
- hasShortAESKey(e, msg) or
- hasShortDsaKeyPair(e, msg) or
- hasShortRsaKeyPair(e, msg) or
- hasShortECKeyPair(e, msg)
-}
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery_BeforeClean-UpForDoubleTaintTracking-2022-10-10.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery_BeforeClean-UpForDoubleTaintTracking-2022-10-10.qll
deleted file mode 100644
index eec0e086f1b..00000000000
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery_BeforeClean-UpForDoubleTaintTracking-2022-10-10.qll
+++ /dev/null
@@ -1,239 +0,0 @@
-import semmle.code.java.security.Encryption
-import semmle.code.java.dataflow.TaintTracking2
-import semmle.code.java.dataflow.TaintTracking
-import semmle.code.java.dataflow.DataFlow
-
-// TODO:
-// todo #0: find a better way to combine the two needed taint-tracking configs so can go back to having a path-graph...
-// todo #1: make representation of source that can be shared across the configs
-// todo #2: make representation of sink that can be shared across the configs
-// todo #3: make list of algo names more easily reusable (either as constant-type variable at top of file, or model as own class to share, etc.)
-// todo #4: refactor to be more like the Python version? (or not possible because of lack of DataFlow::Node for void method in Java?)
-// ******* DATAFLOW BELOW *************************************************************************
-/**
- * Asymmetric (RSA, DSA, DH) key length data flow tracking configuration.
- */
-class AsymmetricKeyTrackingConfiguration extends TaintTracking2::Configuration {
- AsymmetricKeyTrackingConfiguration() { this = "AsymmetricKeyTrackingConfiguration" }
-
- override predicate isSource(DataFlow::Node source) {
- exists(ClassInstanceExpr rsaGenParamSpec |
- rsaGenParamSpec.getConstructedType() instanceof RSAGenParameterSpec and // ! double-check if should just use getType() instead
- rsaGenParamSpec.getArgument(0).(IntegerLiteral).getIntValue() < 2048 and
- source.asExpr() = rsaGenParamSpec
- )
- or
- source.asExpr().(IntegerLiteral).getIntValue() < 2048
- }
-
- override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma, VarAccess va |
- ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- //ma.getFile().getBaseName().matches("SignatureTest.java") and
- // va.getVariable()
- // .getAnAssignedValue()
- // .(JavaSecurityKeyPairGenerator)
- // .getAlgoSpec()
- // .(StringLiteral)
- // .getValue()
- // .toUpperCase()
- // .matches(["RSA", "DSA", "DH"]) and
- // ma.getQualifier() = va and
- exists(
- JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kpgConfig,
- DataFlow::PathNode source, DataFlow::PathNode dest
- |
- jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches(["RSA", "DSA", "DH"]) and
- source.getNode().asExpr() = jpg and
- dest.getNode().asExpr() = ma.getQualifier() and
- kpgConfig.hasFlowPath(source, dest)
- ) and
- sink.asExpr() = ma.getArgument(0)
- )
- }
-}
-
-// predicate hasInsufficientKeySize(string msg) { hasShortAsymmetricKeyPair(msg) }
-// predicate hasShortAsymmetricKeyPair(string msg) {
-// exists(AsymmetricKeyTrackingConfiguration config1, DataFlow::Node source, DataFlow::Node sink |
-// config1.hasFlow(source, sink)
-// ) and
-// msg = "Key size should be at least 2048 bits for " + "___" + " encryption."
-// }
-/**
- * Asymmetric (EC) key length data flow tracking configuration.
- */
-class AsymmetricECCKeyTrackingConfiguration extends TaintTracking2::Configuration {
- AsymmetricECCKeyTrackingConfiguration() { this = "AsymmetricECCKeyTrackingConfiguration" }
-
- override predicate isSource(DataFlow::Node source) {
- exists(ClassInstanceExpr ecGenParamSpec |
- getECKeySize(ecGenParamSpec.getArgument(0).(StringLiteral).getValue()) < 256 and // ! can generate EC with just the keysize and not the curve apparently... (based on netty/netty FP example)
- source.asExpr() = ecGenParamSpec
- )
- or
- source.asExpr().(IntegerLiteral).getIntValue() < 256
- }
-
- override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma, VarAccess va |
- ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- //ma.getArgument(0).getType() instanceof ECGenParameterSpec and // ! can generate EC with just the keysize and not the curve apparently... (based on netty/netty FP example)
- // va.getVariable()
- // .getAnAssignedValue()
- // .(JavaSecurityKeyPairGenerator)
- // .getAlgoSpec()
- // .(StringLiteral)
- // .getValue()
- // .toUpperCase()
- // .matches(["EC%"]) and
- // ma.getQualifier() = va and
- exists(
- JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kpgConfig,
- DataFlow::PathNode source, DataFlow::PathNode dest
- |
- jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches("EC%") and
- source.getNode().asExpr() = jpg and
- dest.getNode().asExpr() = ma.getQualifier() and
- kpgConfig.hasFlowPath(source, dest)
- ) and
- sink.asExpr() = ma.getArgument(0)
- )
- }
-}
-
-/**
- * Symmetric (AES) key length data flow tracking configuration.
- */
-class SymmetricKeyTrackingConfiguration extends TaintTracking2::Configuration {
- SymmetricKeyTrackingConfiguration() { this = "SymmetricKeyTrackingConfiguration2" }
-
- override predicate isSource(DataFlow::Node source) {
- source.asExpr().(IntegerLiteral).getIntValue() < 128
- }
-
- override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma, VarAccess va |
- ma.getMethod() instanceof KeyGeneratorInitMethod and
- // va.getVariable()
- // .getAnAssignedValue()
- // .(JavaxCryptoKeyGenerator)
- // .getAlgoSpec()
- // .(StringLiteral)
- // .getValue()
- // .toUpperCase()
- // .matches(["AES"]) and
- // ma.getQualifier() = va and
- exists(
- JavaxCryptoKeyGenerator jcg, KeyGeneratorInitConfiguration kgConfig,
- DataFlow::PathNode source, DataFlow::PathNode dest
- |
- jcg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches("AES") and
- source.getNode().asExpr() = jcg and
- dest.getNode().asExpr() = ma.getQualifier() and
- kgConfig.hasFlowPath(source, dest)
- ) and
- sink.asExpr() = ma.getArgument(0)
- )
- }
-}
-
-// ! below doesn't work for some reason...
-// predicate hasInsufficientKeySize2(DataFlow::PathNode source, DataFlow::PathNode sink) {
-// exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink))
-// or
-// exists(SymmetricKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink))
-// }
-// ******** Need the below for the above ********
-// ! move to Encryption.qll?
-/** The Java class `java.security.spec.ECGenParameterSpec`. */
-private class ECGenParameterSpec extends RefType {
- ECGenParameterSpec() { this.hasQualifiedName("java.security.spec", "ECGenParameterSpec") }
-}
-
-/** The Java class `java.security.spec.ECGenParameterSpec`. */
-private class RSAGenParameterSpec extends RefType {
- RSAGenParameterSpec() { this.hasQualifiedName("java.security.spec", "RSAKeyGenParameterSpec") }
-}
-
-// ! move to Encryption.qll?
-/** Returns the key size in the EC algorithm string */
-bindingset[algorithm]
-private int getECKeySize(string algorithm) {
- algorithm.matches("sec%") and // specification such as "secp256r1"
- result = algorithm.regexpCapture("sec[p|t](\\d+)[a-zA-Z].*", 1).toInt()
- or
- algorithm.matches("X9.62%") and //specification such as "X9.62 prime192v2"
- result = algorithm.regexpCapture("X9\\.62 .*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
- or
- (algorithm.matches("prime%") or algorithm.matches("c2tnb%")) and //specification such as "prime192v2"
- result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
-}
-
-// ! move to Encryption.qll?
-/** The `init` method declared in `javax.crypto.KeyGenerator`. */
-private class KeyGeneratorInitMethod extends Method {
- KeyGeneratorInitMethod() {
- this.getDeclaringType() instanceof KeyGenerator and
- this.hasName("init")
- }
-}
-
-// ! move to Encryption.qll?
-/** The `initialize` method declared in `java.security.KeyPairGenerator`. */
-private class KeyPairGeneratorInitMethod extends Method {
- KeyPairGeneratorInitMethod() {
- this.getDeclaringType() instanceof KeyPairGenerator and
- this.hasName("initialize")
- }
-}
-
-// ******* DATAFLOW ABOVE *************************************************************************
-// ************************************************************************************************
-// ************************************************************************************************
-// ************************************************************************************************
-// ************************************************************************************************
-// ************************************************************************************************
-// ******* OLD/UNUSED OR EXPERIMENTAL CODE BELOW **************************************************
-class UnsafeSymmetricKeySize extends IntegerLiteral {
- UnsafeSymmetricKeySize() { this.getIntValue() < 128 }
-}
-
-class UnsafeAsymmetricKeySize extends IntegerLiteral {
- UnsafeAsymmetricKeySize() { this.getIntValue() < 2048 }
-}
-
-/** Taint configuration tracking flow from a key generator to a `init` method call. */
-private class KeyGeneratorInitConfiguration extends TaintTracking::Configuration {
- KeyGeneratorInitConfiguration() { this = "KeyGeneratorInitConfiguration" }
-
- override predicate isSource(DataFlow::Node source) {
- source.asExpr() instanceof JavaxCryptoKeyGenerator
- }
-
- override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma |
- ma.getMethod() instanceof KeyGeneratorInitMethod and
- sink.asExpr() = ma.getQualifier()
- )
- }
-}
-
-/**
- * Taint configuration tracking flow from a keypair generator to
- * an `initialize` method call.
- */
-private class KeyPairGeneratorInitConfiguration extends TaintTracking::Configuration {
- KeyPairGeneratorInitConfiguration() { this = "KeyPairGeneratorInitConfiguration" }
-
- override predicate isSource(DataFlow::Node source) {
- source.asExpr() instanceof JavaSecurityKeyPairGenerator
- }
-
- override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma |
- ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- sink.asExpr() = ma.getQualifier()
- )
- }
-}
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery_BeforeDataFlowRewrite.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery_BeforeDataFlowRewrite.qll
deleted file mode 100644
index d6943adebc5..00000000000
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery_BeforeDataFlowRewrite.qll
+++ /dev/null
@@ -1,182 +0,0 @@
-import semmle.code.java.security.Encryption
-import semmle.code.java.dataflow.TaintTracking
-import semmle.code.java.dataflow.DataFlow3
-
-/** The Java class `java.security.spec.ECGenParameterSpec`. */
-private class ECGenParameterSpec extends RefType {
- ECGenParameterSpec() { this.hasQualifiedName("java.security.spec", "ECGenParameterSpec") }
-}
-
-/** The `init` method declared in `javax.crypto.KeyGenerator`. */
-private class KeyGeneratorInitMethod extends Method {
- KeyGeneratorInitMethod() {
- this.getDeclaringType() instanceof KeyGenerator and
- this.hasName("init")
- }
-}
-
-/** The `initialize` method declared in `java.security.KeyPairGenerator`. */
-private class KeyPairGeneratorInitMethod extends Method {
- KeyPairGeneratorInitMethod() {
- this.getDeclaringType() instanceof KeyPairGenerator and
- this.hasName("initialize")
- }
-}
-
-/** Returns the key size in the EC algorithm string */
-bindingset[algorithm]
-private int getECKeySize(string algorithm) {
- algorithm.matches("sec%") and // specification such as "secp256r1"
- result = algorithm.regexpCapture("sec[p|t](\\d+)[a-zA-Z].*", 1).toInt()
- or
- algorithm.matches("X9.62%") and //specification such as "X9.62 prime192v2"
- result = algorithm.regexpCapture("X9\\.62 .*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
- or
- (algorithm.matches("prime%") or algorithm.matches("c2tnb%")) and //specification such as "prime192v2"
- result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
-}
-
-/** Taint configuration tracking flow from a key generator to a `init` method call. */
-private class KeyGeneratorInitConfiguration extends TaintTracking::Configuration {
- KeyGeneratorInitConfiguration() { this = "KeyGeneratorInitConfiguration" }
-
- override predicate isSource(DataFlow::Node source) {
- source.asExpr() instanceof JavaxCryptoKeyGenerator
- }
-
- override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma |
- ma.getMethod() instanceof KeyGeneratorInitMethod and
- sink.asExpr() = ma.getQualifier()
- )
- }
-}
-
-/**
- * Taint configuration tracking flow from a keypair generator to
- * an `initialize` method call.
- */
-private class KeyPairGeneratorInitConfiguration extends TaintTracking::Configuration {
- KeyPairGeneratorInitConfiguration() { this = "KeyPairGeneratorInitConfiguration" }
-
- override predicate isSource(DataFlow::Node source) {
- source.asExpr() instanceof JavaSecurityKeyPairGenerator
- }
-
- override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma |
- ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- sink.asExpr() = ma.getQualifier()
- )
- }
-}
-
-/**
- * Holds if a symmetric `KeyGenerator` implementing encryption algorithm
- * `type` and initialized by `ma` uses an insufficient key size.
- *
- * `msg` provides a human-readable description of the problem.
- */
-bindingset[type]
-private predicate hasShortSymmetricKey(MethodAccess ma, string msg, string type) {
- ma.getMethod() instanceof KeyGeneratorInitMethod and
- exists(
- JavaxCryptoKeyGenerator jcg, KeyGeneratorInitConfiguration cc, DataFlow::PathNode source,
- DataFlow::PathNode dest
- |
- jcg.getAlgoSpec().(StringLiteral).getValue() = type and
- source.getNode().asExpr() = jcg and
- dest.getNode().asExpr() = ma.getQualifier() and
- //ma.getArgument(0) = var and // ! me
- //var.getVariable().getInitializer().getUnderlyingExpr() instanceof IntegerLiteral and // ! me
- cc.hasFlowPath(source, dest) //and
- //var.getVariable().getInitializer().getUnderlyingExpr().toString().toInt() < 128 // ! me
- ) and
- exists(VarAccess var |
- var.getVariable().getInitializer().getUnderlyingExpr() instanceof IntegerLiteral and
- var.getVariable().getInitializer().getUnderlyingExpr().toString().toInt() < 128 and
- //DataFlow3::localExprFlow(var, ma.getArgument(0)) and
- ma.getArgument(0) = var
- //ma.getArgument(0).(IntegerLiteral).getIntValue() < 128
- ) and
- msg = "Key size should be at least 128 bits for " + type + " encryption."
-}
-
-/**
- * Holds if an AES `KeyGenerator` initialized by `ma` uses an insufficient key size.
- * `msg` provides a human-readable description of the problem.
- */
-private predicate hasShortAESKey(MethodAccess ma, string msg) {
- hasShortSymmetricKey(ma, msg, "AES")
-}
-
-/**
- * Holds if an asymmetric `KeyPairGenerator` implementing encryption algorithm
- * `type` and initialized by `ma` uses an insufficient key size.
- *
- * `msg` provides a human-readable description of the problem.
- */
-bindingset[type]
-private predicate hasShortAsymmetricKeyPair(MethodAccess ma, string msg, string type) {
- ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- exists(
- JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kc,
- DataFlow::PathNode source, DataFlow::PathNode dest
- |
- jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase() = type and
- source.getNode().asExpr() = jpg and
- dest.getNode().asExpr() = ma.getQualifier() and
- kc.hasFlowPath(source, dest)
- ) and
- ma.getArgument(0).(IntegerLiteral).getIntValue() < 2048 and
- msg = "Key size should be at least 2048 bits for " + type + " encryption."
-}
-
-/**
- * Holds if a DSA `KeyPairGenerator` initialized by `ma` uses an insufficient key size.
- *
- * `msg` provides a human-readable description of the problem.
- */
-private predicate hasShortDsaKeyPair(MethodAccess ma, string msg) {
- hasShortAsymmetricKeyPair(ma, msg, "DSA") or
- hasShortAsymmetricKeyPair(ma, msg, "DH")
-}
-
-/**
- * Holds if a RSA `KeyPairGenerator` initialized by `ma` uses an insufficient key size.
- *
- * `msg` provides a human-readable description of the problem.
- */
-private predicate hasShortRsaKeyPair(MethodAccess ma, string msg) {
- hasShortAsymmetricKeyPair(ma, msg, "RSA")
-}
-
-/**
- * Holds if an EC `KeyPairGenerator` initialized by `ma` uses an insufficient key size.
- *
- * `msg` provides a human-readable description of the problem.
- */
-private predicate hasShortECKeyPair(MethodAccess ma, string msg) {
- ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- exists(
- JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kc,
- DataFlow::PathNode source, DataFlow::PathNode dest, ClassInstanceExpr cie
- |
- jpg.getAlgoSpec().(StringLiteral).getValue().matches("EC%") and // ECC variants such as ECDH and ECDSA
- source.getNode().asExpr() = jpg and
- dest.getNode().asExpr() = ma.getQualifier() and
- kc.hasFlowPath(source, dest) and
- DataFlow::localExprFlow(cie, ma.getArgument(0)) and
- ma.getArgument(0).getType() instanceof ECGenParameterSpec and
- getECKeySize(cie.getArgument(0).(StringLiteral).getValue()) < 256
- ) and
- msg = "Key size should be at least 256 bits for EC encryption."
-}
-
-// ! refactor this so can use 'path-problem' select clause instead?
-predicate hasInsufficientKeySize(Expr e, string msg) {
- hasShortAESKey(e, msg) or
- hasShortDsaKeyPair(e, msg) or
- hasShortRsaKeyPair(e, msg) or
- hasShortECKeyPair(e, msg)
-}
From e64825ff7afe9d15ed46864858979e3c2352a2b9 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Mon, 10 Oct 2022 23:01:55 -0400
Subject: [PATCH 021/249] fix code-scanning bot problems
---
.../security/InsufficientKeySizeQuery.qll | 39 ++++++++++---------
.../CWE/CWE-326/InsufficientKeySize.ql | 2 +-
.../CWE/CWE-326/InsufficientKeySize_OLD.java | 37 ------------------
.../CWE-326/InsufficientKeySizeTestOLD.txt | 11 ------
4 files changed, 22 insertions(+), 67 deletions(-)
delete mode 100644 java/ql/src/Security/CWE/CWE-326/InsufficientKeySize_OLD.java
delete mode 100644 java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTestOLD.txt
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
index ed7408facf9..2891bbcac50 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
@@ -1,9 +1,11 @@
+/** Provides classes and predicates related to insufficient key sizes in Java. */
+
import semmle.code.java.security.Encryption
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.DataFlow2
/**
- * Asymmetric (RSA, DSA, DH) key length data flow tracking configuration.
+ * An Asymmetric (RSA, DSA, DH) key length data flow tracking configuration.
*/
class AsymmetricNonECKeyTrackingConfiguration extends DataFlow2::Configuration {
AsymmetricNonECKeyTrackingConfiguration() { this = "AsymmetricNonECKeyTrackingConfiguration" }
@@ -29,24 +31,24 @@ class AsymmetricNonECKeyTrackingConfiguration extends DataFlow2::Configuration {
or
// TODO: combine below three for less duplicated code
exists(ClassInstanceExpr rsaKeyGenParamSpec |
- rsaKeyGenParamSpec.getConstructedType() instanceof RSAKeyGenParameterSpec and
+ rsaKeyGenParamSpec.getConstructedType() instanceof RsaKeyGenParameterSpec and
sink.asExpr() = rsaKeyGenParamSpec.getArgument(0)
)
or
exists(ClassInstanceExpr dsaGenParamSpec |
- dsaGenParamSpec.getConstructedType() instanceof DSAGenParameterSpec and
+ dsaGenParamSpec.getConstructedType() instanceof DsaGenParameterSpec and
sink.asExpr() = dsaGenParamSpec.getArgument(0)
)
or
exists(ClassInstanceExpr dhGenParamSpec |
- dhGenParamSpec.getConstructedType() instanceof DHGenParameterSpec and
+ dhGenParamSpec.getConstructedType() instanceof DhGenParameterSpec and
sink.asExpr() = dhGenParamSpec.getArgument(0)
)
}
}
/**
- * Asymmetric (EC) key length data flow tracking configuration.
+ * An Asymmetric (EC) key length data flow tracking configuration.
*/
class AsymmetricECKeyTrackingConfiguration extends DataFlow2::Configuration {
AsymmetricECKeyTrackingConfiguration() { this = "AsymmetricECKeyTrackingConfiguration" }
@@ -72,7 +74,7 @@ class AsymmetricECKeyTrackingConfiguration extends DataFlow2::Configuration {
)
or
exists(ClassInstanceExpr ecGenParamSpec |
- ecGenParamSpec.getConstructedType() instanceof ECGenParameterSpec and
+ ecGenParamSpec.getConstructedType() instanceof EcGenParameterSpec and
//getECKeySize(ecGenParamSpec.getArgument(0).(StringLiteral).getValue()) < 256 and
sink.asExpr() = ecGenParamSpec.getArgument(0)
)
@@ -80,7 +82,7 @@ class AsymmetricECKeyTrackingConfiguration extends DataFlow2::Configuration {
}
/**
- * Symmetric (AES) key length data flow tracking configuration.
+ * A Symmetric (AES) key length data flow tracking configuration.
*/
class SymmetricKeyTrackingConfiguration extends DataFlow2::Configuration {
SymmetricKeyTrackingConfiguration() { this = "SymmetricKeyTrackingConfiguration" }
@@ -96,7 +98,7 @@ class SymmetricKeyTrackingConfiguration extends DataFlow2::Configuration {
JavaxCryptoKeyGenerator jcg, KeyGeneratorInitConfiguration kgConfig,
DataFlow::PathNode source, DataFlow::PathNode dest
|
- jcg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches("AES") and
+ jcg.getAlgoSpec().(StringLiteral).getValue().toUpperCase() = "AES" and
source.getNode().asExpr() = jcg and
dest.getNode().asExpr() = ma.getQualifier() and
kgConfig.hasFlowPath(source, dest)
@@ -108,7 +110,7 @@ class SymmetricKeyTrackingConfiguration extends DataFlow2::Configuration {
// ********************** Need the below models for the above configs **********************
// todo: move some/all of below to Encryption.qll or elsewhere?
-/** Data flow configuration tracking flow from a key generator to an `init` method call. */
+/** A data flow configuration tracking flow from a key generator to an `init` method call. */
private class KeyGeneratorInitConfiguration extends DataFlow::Configuration {
KeyGeneratorInitConfiguration() { this = "KeyGeneratorInitConfiguration" }
@@ -124,7 +126,7 @@ private class KeyGeneratorInitConfiguration extends DataFlow::Configuration {
}
}
-/** Data flow configuration tracking flow from a keypair generator to an `initialize` method call. */
+/** A data flow configuration tracking flow from a keypair generator to an `initialize` method call. */
private class KeyPairGeneratorInitConfiguration extends DataFlow::Configuration {
KeyPairGeneratorInitConfiguration() { this = "KeyPairGeneratorInitConfiguration" }
@@ -141,23 +143,23 @@ private class KeyPairGeneratorInitConfiguration extends DataFlow::Configuration
}
/** The Java class `java.security.spec.ECGenParameterSpec`. */
-private class ECGenParameterSpec extends RefType {
- ECGenParameterSpec() { this.hasQualifiedName("java.security.spec", "ECGenParameterSpec") }
+private class EcGenParameterSpec extends RefType {
+ EcGenParameterSpec() { this.hasQualifiedName("java.security.spec", "ECGenParameterSpec") }
}
/** The Java class `java.security.spec.RSAKeyGenParameterSpec`. */
-private class RSAKeyGenParameterSpec extends RefType {
- RSAKeyGenParameterSpec() { this.hasQualifiedName("java.security.spec", "RSAKeyGenParameterSpec") }
+private class RsaKeyGenParameterSpec extends RefType {
+ RsaKeyGenParameterSpec() { this.hasQualifiedName("java.security.spec", "RSAKeyGenParameterSpec") }
}
/** The Java class `java.security.spec.DSAGenParameterSpec`. */
-private class DSAGenParameterSpec extends RefType {
- DSAGenParameterSpec() { this.hasQualifiedName("java.security.spec", "DSAGenParameterSpec") }
+private class DsaGenParameterSpec extends RefType {
+ DsaGenParameterSpec() { this.hasQualifiedName("java.security.spec", "DSAGenParameterSpec") }
}
/** The Java class `javax.crypto.spec.DHGenParameterSpec`. */
-private class DHGenParameterSpec extends RefType {
- DHGenParameterSpec() { this.hasQualifiedName("javax.crypto.spec", "DHGenParameterSpec") }
+private class DhGenParameterSpec extends RefType {
+ DhGenParameterSpec() { this.hasQualifiedName("javax.crypto.spec", "DHGenParameterSpec") }
}
/** The `init` method declared in `javax.crypto.KeyGenerator`. */
@@ -190,6 +192,7 @@ private int getECKeySize(string algorithm) {
}
// ******* DATAFLOW ABOVE *************************************************************************
// TODO:
+// todo #0: look into use of specs without keygens; should spec not be a sink in these cases?
// todo #1: make representation of source that can be shared across the configs
// todo #2: make representation of sink that can be shared across the configs
// todo #3: make list of algo names more easily reusable (either as constant-type variable at top of file, or model as own class to share, etc.)
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
index a5401dfb157..c1f99e67b9f 100644
--- a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
+++ b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
@@ -19,4 +19,4 @@ where
exists(AsymmetricNonECKeyTrackingConfiguration config1 | config1.hasFlow(source, sink)) or
exists(AsymmetricECKeyTrackingConfiguration config2 | config2.hasFlow(source, sink)) or
exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlow(source, sink))
-select sink, "This $@ is too small and creates a key $@.", source, "key size", sink, "here"
+select sink, "This $@ is too small.", source, "key size"
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize_OLD.java b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize_OLD.java
deleted file mode 100644
index ff30196abb5..00000000000
--- a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize_OLD.java
+++ /dev/null
@@ -1,37 +0,0 @@
-public class InsufficientKeySize {
- public void CryptoMethod() {
- KeyGenerator keyGen1 = KeyGenerator.getInstance("AES");
- // BAD: Key size is less than 128
- keyGen1.init(64);
-
- KeyGenerator keyGen2 = KeyGenerator.getInstance("AES");
- // GOOD: Key size is no less than 128
- keyGen2.init(128);
-
- KeyPairGenerator keyPairGen1 = KeyPairGenerator.getInstance("RSA");
- // BAD: Key size is less than 2048
- keyPairGen1.initialize(1024);
-
- KeyPairGenerator keyPairGen2 = KeyPairGenerator.getInstance("RSA");
- // GOOD: Key size is no less than 2048
- keyPairGen2.initialize(2048);
-
- KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("DSA");
- // BAD: Key size is less than 2048
- keyPairGen3.initialize(1024);
-
- KeyPairGenerator keyPairGen4 = KeyPairGenerator.getInstance("DSA");
- // GOOD: Key size is no less than 2048
- keyPairGen4.initialize(2048);
-
- KeyPairGenerator keyPairGen5 = KeyPairGenerator.getInstance("EC");
- // BAD: Key size is less than 256
- ECGenParameterSpec ecSpec1 = new ECGenParameterSpec("secp112r1");
- keyPairGen5.initialize(ecSpec1);
-
- KeyPairGenerator keyPairGen6 = KeyPairGenerator.getInstance("EC");
- // GOOD: Key size is no less than 256
- ECGenParameterSpec ecSpec2 = new ECGenParameterSpec("secp256r1");
- keyPairGen6.initialize(ecSpec2);
- }
-}
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTestOLD.txt b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTestOLD.txt
deleted file mode 100644
index 421335b84ff..00000000000
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTestOLD.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-| InsufficientKeySize.java:9:9:9:24 | init(...) | Key size should be at least 128 bits for AES encryption. |
-| InsufficientKeySize.java:17:9:17:36 | initialize(...) | Key size should be at least 2048 bits for RSA encryption. |
-| InsufficientKeySize.java:25:9:25:36 | initialize(...) | Key size should be at least 2048 bits for DSA encryption. |
-| InsufficientKeySize.java:34:9:34:39 | initialize(...) | Key size should be at least 256 bits for EC encryption. |
-| InsufficientKeySize.java:38:9:38:67 | initialize(...) | Key size should be at least 256 bits for EC encryption. |
-| InsufficientKeySize.java:48:9:48:39 | initialize(...) | Key size should be at least 256 bits for EC encryption. |
-| InsufficientKeySize.java:53:9:53:39 | initialize(...) | Key size should be at least 256 bits for EC encryption. |
-| InsufficientKeySize.java:58:9:58:40 | initialize(...) | Key size should be at least 256 bits for EC encryption. |
-| InsufficientKeySize.java:68:9:68:40 | initialize(...) | Key size should be at least 256 bits for EC encryption. |
-| InsufficientKeySize.java:78:9:78:40 | initialize(...) | Key size should be at least 256 bits for EC encryption. |
-| InsufficientKeySize.java:87:9:87:37 | initialize(...) | Key size should be at least 2048 bits for DH encryption. |
From 26f4abf12b6f706ca3d98b6d844bec894e2715b1 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Tue, 11 Oct 2022 16:31:02 -0400
Subject: [PATCH 022/249] remove globalflow for key(pair)gen
---
.../security/InsufficientKeySizeQuery.qll | 124 +++++++++---------
.../CWE-326/InsufficientKeySizeTest.java | 4 +-
2 files changed, 64 insertions(+), 64 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
index 2891bbcac50..c6dba3959cc 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
@@ -15,17 +15,19 @@ class AsymmetricNonECKeyTrackingConfiguration extends DataFlow2::Configuration {
}
override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma |
+ exists(MethodAccess ma, JavaSecurityKeyPairGenerator jpg |
ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- exists(
- JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kpgConfig,
- DataFlow::PathNode source, DataFlow::PathNode dest
- |
- jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches(["RSA", "DSA", "DH"]) and
- source.getNode().asExpr() = jpg and
- dest.getNode().asExpr() = ma.getQualifier() and
- kpgConfig.hasFlowPath(source, dest)
- ) and
+ jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches(["RSA", "DSA", "DH"]) and
+ DataFlow::localExprFlow(jpg, ma.getQualifier()) and
+ // exists(
+ // JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kpgConfig,
+ // DataFlow::PathNode source, DataFlow::PathNode dest
+ // |
+ // jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches(["RSA", "DSA", "DH"]) and
+ // source.getNode().asExpr() = jpg and
+ // dest.getNode().asExpr() = ma.getQualifier() and
+ // kpgConfig.hasFlowPath(source, dest)
+ // ) and
sink.asExpr() = ma.getArgument(0)
)
or
@@ -59,17 +61,19 @@ class AsymmetricECKeyTrackingConfiguration extends DataFlow2::Configuration {
}
override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma |
+ exists(MethodAccess ma, JavaSecurityKeyPairGenerator jpg |
ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- exists(
- JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kpgConfig,
- DataFlow::PathNode source, DataFlow::PathNode dest
- |
- jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches("EC%") and
- source.getNode().asExpr() = jpg and
- dest.getNode().asExpr() = ma.getQualifier() and
- kpgConfig.hasFlowPath(source, dest)
- ) and
+ jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches("EC%") and
+ DataFlow::localExprFlow(jpg, ma.getQualifier()) and
+ // exists(
+ // JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kpgConfig,
+ // DataFlow::PathNode source, DataFlow::PathNode dest
+ // |
+ // jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches("EC%") and
+ // source.getNode().asExpr() = jpg and
+ // dest.getNode().asExpr() = ma.getQualifier() and
+ // kpgConfig.hasFlowPath(source, dest)
+ // ) and
sink.asExpr() = ma.getArgument(0)
)
or
@@ -92,17 +96,19 @@ class SymmetricKeyTrackingConfiguration extends DataFlow2::Configuration {
}
override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma |
+ exists(MethodAccess ma, JavaxCryptoKeyGenerator jcg |
ma.getMethod() instanceof KeyGeneratorInitMethod and
- exists(
- JavaxCryptoKeyGenerator jcg, KeyGeneratorInitConfiguration kgConfig,
- DataFlow::PathNode source, DataFlow::PathNode dest
- |
- jcg.getAlgoSpec().(StringLiteral).getValue().toUpperCase() = "AES" and
- source.getNode().asExpr() = jcg and
- dest.getNode().asExpr() = ma.getQualifier() and
- kgConfig.hasFlowPath(source, dest)
- ) and
+ jcg.getAlgoSpec().(StringLiteral).getValue().toUpperCase() = "AES" and
+ DataFlow::localExprFlow(jcg, ma.getQualifier()) and
+ // exists(
+ // JavaxCryptoKeyGenerator jcg, KeyGeneratorInitConfiguration kgConfig,
+ // DataFlow::PathNode source, DataFlow::PathNode dest
+ // |
+ // jcg.getAlgoSpec().(StringLiteral).getValue().toUpperCase() = "AES" and
+ // source.getNode().asExpr() = jcg and
+ // dest.getNode().asExpr() = ma.getQualifier() and
+ // kgConfig.hasFlowPath(source, dest)
+ // ) and
sink.asExpr() = ma.getArgument(0)
)
}
@@ -110,38 +116,32 @@ class SymmetricKeyTrackingConfiguration extends DataFlow2::Configuration {
// ********************** Need the below models for the above configs **********************
// todo: move some/all of below to Encryption.qll or elsewhere?
-/** A data flow configuration tracking flow from a key generator to an `init` method call. */
-private class KeyGeneratorInitConfiguration extends DataFlow::Configuration {
- KeyGeneratorInitConfiguration() { this = "KeyGeneratorInitConfiguration" }
-
- override predicate isSource(DataFlow::Node source) {
- source.asExpr() instanceof JavaxCryptoKeyGenerator
- }
-
- override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma |
- ma.getMethod() instanceof KeyGeneratorInitMethod and
- sink.asExpr() = ma.getQualifier()
- )
- }
-}
-
-/** A data flow configuration tracking flow from a keypair generator to an `initialize` method call. */
-private class KeyPairGeneratorInitConfiguration extends DataFlow::Configuration {
- KeyPairGeneratorInitConfiguration() { this = "KeyPairGeneratorInitConfiguration" }
-
- override predicate isSource(DataFlow::Node source) {
- source.asExpr() instanceof JavaSecurityKeyPairGenerator
- }
-
- override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma |
- ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- sink.asExpr() = ma.getQualifier()
- )
- }
-}
-
+// /** A data flow configuration tracking flow from a key generator to an `init` method call. */
+// private class KeyGeneratorInitConfiguration extends DataFlow::Configuration {
+// KeyGeneratorInitConfiguration() { this = "KeyGeneratorInitConfiguration" }
+// override predicate isSource(DataFlow::Node source) {
+// source.asExpr() instanceof JavaxCryptoKeyGenerator
+// }
+// override predicate isSink(DataFlow::Node sink) {
+// exists(MethodAccess ma |
+// ma.getMethod() instanceof KeyGeneratorInitMethod and
+// sink.asExpr() = ma.getQualifier()
+// )
+// }
+// }
+// /** A data flow configuration tracking flow from a keypair generator to an `initialize` method call. */
+// private class KeyPairGeneratorInitConfiguration extends DataFlow::Configuration {
+// KeyPairGeneratorInitConfiguration() { this = "KeyPairGeneratorInitConfiguration" }
+// override predicate isSource(DataFlow::Node source) {
+// source.asExpr() instanceof JavaSecurityKeyPairGenerator
+// }
+// override predicate isSink(DataFlow::Node sink) {
+// exists(MethodAccess ma |
+// ma.getMethod() instanceof KeyPairGeneratorInitMethod and
+// sink.asExpr() = ma.getQualifier()
+// )
+// }
+// }
/** The Java class `java.security.spec.ECGenParameterSpec`. */
private class EcGenParameterSpec extends RefType {
EcGenParameterSpec() { this.hasQualifiedName("java.security.spec", "ECGenParameterSpec") }
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
index 9ba11f1c632..e6a61d7bb4c 100644
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
@@ -208,7 +208,7 @@ public class InsufficientKeySizeTest {
keyGen.init(keySize); // $ hasInsufficientKeySize
// BAD: Key size is less than 2048
- kg.init(64); // $ hasInsufficientKeySize
+ kg.init(64); // $ MISSING: hasInsufficientKeySize
}
//! refactor this to use expected-value tag and combine with above method
@@ -224,7 +224,7 @@ public class InsufficientKeySizeTest {
keyPairGen.initialize(keySize); // $ hasInsufficientKeySize
// BAD: Key size is less than 2048
- kpg.initialize(1024); // $ hasInsufficientKeySize
+ kpg.initialize(1024); // $ MISSING: hasInsufficientKeySize
}
//! refactor this to use expected-value tag and combine with above method
From 3e8748e639014f5edf27b12439bb3efdcd5364b9 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Tue, 11 Oct 2022 16:54:07 -0400
Subject: [PATCH 023/249] add path-graph back to query alerts
---
.../code/java/security/InsufficientKeySizeQuery.qll | 7 +++----
.../src/Security/CWE/CWE-326/InsufficientKeySize.ql | 13 +++++++------
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
index c6dba3959cc..ce2d3c3a927 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
@@ -2,12 +2,11 @@
import semmle.code.java.security.Encryption
import semmle.code.java.dataflow.DataFlow
-import semmle.code.java.dataflow.DataFlow2
/**
* An Asymmetric (RSA, DSA, DH) key length data flow tracking configuration.
*/
-class AsymmetricNonECKeyTrackingConfiguration extends DataFlow2::Configuration {
+class AsymmetricNonECKeyTrackingConfiguration extends DataFlow::Configuration {
AsymmetricNonECKeyTrackingConfiguration() { this = "AsymmetricNonECKeyTrackingConfiguration" }
override predicate isSource(DataFlow::Node source) {
@@ -52,7 +51,7 @@ class AsymmetricNonECKeyTrackingConfiguration extends DataFlow2::Configuration {
/**
* An Asymmetric (EC) key length data flow tracking configuration.
*/
-class AsymmetricECKeyTrackingConfiguration extends DataFlow2::Configuration {
+class AsymmetricECKeyTrackingConfiguration extends DataFlow::Configuration {
AsymmetricECKeyTrackingConfiguration() { this = "AsymmetricECKeyTrackingConfiguration" }
override predicate isSource(DataFlow::Node source) {
@@ -88,7 +87,7 @@ class AsymmetricECKeyTrackingConfiguration extends DataFlow2::Configuration {
/**
* A Symmetric (AES) key length data flow tracking configuration.
*/
-class SymmetricKeyTrackingConfiguration extends DataFlow2::Configuration {
+class SymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
SymmetricKeyTrackingConfiguration() { this = "SymmetricKeyTrackingConfiguration" }
override predicate isSource(DataFlow::Node source) {
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
index c1f99e67b9f..27f4d7e2612 100644
--- a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
+++ b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
@@ -2,7 +2,7 @@
* @name Insufficient key size used with a cryptographic algorithm
* @description Using cryptographic algorithms with too small of a key size can
* allow an attacker to compromise security.
- * @kind problem
+ * @kind path-problem
* @problem.severity error
* @security-severity 7.5
* @precision high
@@ -13,10 +13,11 @@
import java
import semmle.code.java.security.InsufficientKeySizeQuery
+import DataFlow::PathGraph
-from DataFlow::Node source, DataFlow::Node sink
+from DataFlow::PathNode source, DataFlow::PathNode sink
where
- exists(AsymmetricNonECKeyTrackingConfiguration config1 | config1.hasFlow(source, sink)) or
- exists(AsymmetricECKeyTrackingConfiguration config2 | config2.hasFlow(source, sink)) or
- exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlow(source, sink))
-select sink, "This $@ is too small.", source, "key size"
+ exists(AsymmetricNonECKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink)) or
+ exists(AsymmetricECKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink)) or
+ exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlowPath(source, sink))
+select sink.getNode(), source, sink, "This $@ is too small.", source.getNode(), "key size"
From 29de0c67482aa8e95296e5a9eb464003571787cc Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Tue, 11 Oct 2022 22:29:48 -0400
Subject: [PATCH 024/249] make one config for asymm with flow states; seems to
work...
---
.../security/InsufficientKeySizeQuery.qll | 78 +++++++++++++++++++
.../CWE/CWE-326/InsufficientKeySize.ql | 6 +-
.../CWE-326/InsufficientKeySizeTest.ql | 15 ++--
3 files changed, 91 insertions(+), 8 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
index ce2d3c3a927..51c5bf702e0 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
@@ -2,6 +2,84 @@
import semmle.code.java.security.Encryption
import semmle.code.java.dataflow.DataFlow
+import semmle.code.java.dataflow.TaintTracking
+
+//import semmle.code.java.dataflow.internal.DataFlowImplCommonPublic
+//import semmle.code.java.dataflow.FlowSources
+//import semmle.code.java.dataflow.internal.DataFlowNodes
+/**
+ * An Asymmetric (RSA, DSA, DH) key length data flow tracking configuration.
+ */
+class AsymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
+ AsymmetricKeyTrackingConfiguration() { this = "AsymmetricKeyTrackingConfiguration" }
+
+ override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
+ //state instanceof DataFlow::FlowStateEmpty and
+ source.asExpr().(IntegerLiteral).getIntValue() < 2048 and state = "2048"
+ or
+ source.asExpr().(IntegerLiteral).getIntValue() < 256 and state = "256"
+ or
+ getECKeySize(source.asExpr().(StringLiteral).getValue()) < 256 and state = "256" // need this for the cases when the key size is embedded in the curve name.
+ }
+
+ override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
+ exists(MethodAccess ma, JavaSecurityKeyPairGenerator jpg |
+ ma.getMethod() instanceof KeyPairGeneratorInitMethod and
+ (
+ jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches(["RSA", "DSA", "DH"]) and
+ DataFlow::localExprFlow(jpg, ma.getQualifier()) and
+ sink.asExpr() = ma.getArgument(0) and
+ //ma.getArgument(0).(LocalSourceNode).flowsTo(sink) and
+ //ma.getArgument(0).(CompileTimeConstantExpr).getIntValue() < 2048 and
+ state = "2048"
+ )
+ or
+ jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches("EC%") and
+ DataFlow::localExprFlow(jpg, ma.getQualifier()) and
+ sink.asExpr() = ma.getArgument(0) and
+ //ma.getArgument(0).(CompileTimeConstantExpr).getIntValue() < 256 and
+ state = "256"
+ )
+ or
+ // TODO: combine below three for less duplicated code
+ exists(ClassInstanceExpr rsaKeyGenParamSpec |
+ rsaKeyGenParamSpec.getConstructedType() instanceof RsaKeyGenParameterSpec and
+ sink.asExpr() = rsaKeyGenParamSpec.getArgument(0) and
+ state = "2048"
+ )
+ or
+ exists(ClassInstanceExpr dsaGenParamSpec |
+ dsaGenParamSpec.getConstructedType() instanceof DsaGenParameterSpec and
+ sink.asExpr() = dsaGenParamSpec.getArgument(0) and
+ state = "2048"
+ )
+ or
+ exists(ClassInstanceExpr dhGenParamSpec |
+ dhGenParamSpec.getConstructedType() instanceof DhGenParameterSpec and
+ sink.asExpr() = dhGenParamSpec.getArgument(0) and
+ state = "2048"
+ )
+ or
+ exists(ClassInstanceExpr ecGenParamSpec |
+ ecGenParamSpec.getConstructedType() instanceof EcGenParameterSpec and
+ sink.asExpr() = ecGenParamSpec.getArgument(0) and
+ state = "256"
+ )
+ }
+
+ override predicate isAdditionalFlowStep(
+ DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
+ DataFlow::FlowState state2
+ ) {
+ exists(IntegerLiteral intLiteral |
+ state1 = "" and
+ state2 = intLiteral.toString() and
+ node1.asExpr() = intLiteral and
+ node2.asExpr() = intLiteral
+ //intLiteral.toString().toInt() = 64 // test viability of this craziness
+ )
+ }
+}
/**
* An Asymmetric (RSA, DSA, DH) key length data flow tracking configuration.
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
index 27f4d7e2612..979a6d4958b 100644
--- a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
+++ b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
@@ -17,7 +17,9 @@ import DataFlow::PathGraph
from DataFlow::PathNode source, DataFlow::PathNode sink
where
- exists(AsymmetricNonECKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink)) or
- exists(AsymmetricECKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink)) or
+ exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink))
+ or
+ // exists(AsymmetricNonECKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink)) or
+ // exists(AsymmetricECKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink)) or
exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlowPath(source, sink))
select sink.getNode(), source, sink, "This $@ is too small.", source.getNode(), "key size"
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
index eef02e9352a..bd7c164273f 100644
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
@@ -1,6 +1,7 @@
import java
import TestUtilities.InlineExpectationsTest
import semmle.code.java.security.InsufficientKeySizeQuery
+import DataFlow::PathGraph
class InsufficientKeySizeTest extends InlineExpectationsTest {
InsufficientKeySizeTest() { this = "InsufficientKeySize" }
@@ -9,13 +10,15 @@ class InsufficientKeySizeTest extends InlineExpectationsTest {
override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "hasInsufficientKeySize" and
- exists(DataFlow::Node source, DataFlow::Node sink |
- exists(AsymmetricNonECKeyTrackingConfiguration config1 | config1.hasFlow(source, sink)) or
- exists(AsymmetricECKeyTrackingConfiguration config2 | config2.hasFlow(source, sink)) or
- exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlow(source, sink))
+ exists(DataFlow::PathNode source, DataFlow::PathNode sink |
+ exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink))
+ or
+ // exists(AsymmetricNonECKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink)) or
+ // exists(AsymmetricECKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink)) or
+ exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlowPath(source, sink))
|
- sink.getLocation() = location and
- element = sink.toString() and
+ sink.getNode().getLocation() = location and
+ element = sink.getNode().toString() and
value = ""
)
}
From 01c2a8cbba765d4fe1750d03fa89d95cc43b7d88 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Wed, 12 Oct 2022 08:51:22 -0400
Subject: [PATCH 025/249] add symm to the single config; still seems to work
---
.../security/InsufficientKeySizeQuery.qll | 22 +++++++++++++++----
.../CWE/CWE-326/InsufficientKeySize.ql | 11 +++++-----
.../CWE-326/InsufficientKeySizeTest.ql | 10 ++++-----
3 files changed, 28 insertions(+), 15 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
index 51c5bf702e0..d5739fa2310 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
@@ -8,13 +8,17 @@ import semmle.code.java.dataflow.TaintTracking
//import semmle.code.java.dataflow.FlowSources
//import semmle.code.java.dataflow.internal.DataFlowNodes
/**
- * An Asymmetric (RSA, DSA, DH) key length data flow tracking configuration.
+ * A key length data flow tracking configuration.
*/
-class AsymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
- AsymmetricKeyTrackingConfiguration() { this = "AsymmetricKeyTrackingConfiguration" }
+class KeyTrackingConfiguration extends DataFlow::Configuration {
+ KeyTrackingConfiguration() { this = "KeyTrackingConfiguration" }
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
//state instanceof DataFlow::FlowStateEmpty and
+ // SYMMETRIC
+ source.asExpr().(IntegerLiteral).getIntValue() < 128 and state = "128"
+ or
+ // ASYMMETRIC
source.asExpr().(IntegerLiteral).getIntValue() < 2048 and state = "2048"
or
source.asExpr().(IntegerLiteral).getIntValue() < 256 and state = "256"
@@ -23,6 +27,16 @@ class AsymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
}
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
+ // SYMMETRIC
+ exists(MethodAccess ma, JavaxCryptoKeyGenerator jcg |
+ ma.getMethod() instanceof KeyGeneratorInitMethod and
+ jcg.getAlgoSpec().(StringLiteral).getValue().toUpperCase() = "AES" and
+ DataFlow::localExprFlow(jcg, ma.getQualifier()) and
+ sink.asExpr() = ma.getArgument(0) and
+ state = "128"
+ )
+ or
+ // ASYMMETRIC
exists(MethodAccess ma, JavaSecurityKeyPairGenerator jpg |
ma.getMethod() instanceof KeyPairGeneratorInitMethod and
(
@@ -67,6 +81,7 @@ class AsymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
)
}
+ // ! FlowStates seem to work without even including a step like the below... hmmm
override predicate isAdditionalFlowStep(
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
DataFlow::FlowState state2
@@ -76,7 +91,6 @@ class AsymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
state2 = intLiteral.toString() and
node1.asExpr() = intLiteral and
node2.asExpr() = intLiteral
- //intLiteral.toString().toInt() = 64 // test viability of this craziness
)
}
}
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
index 979a6d4958b..040f17abfc0 100644
--- a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
+++ b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
@@ -16,10 +16,9 @@ import semmle.code.java.security.InsufficientKeySizeQuery
import DataFlow::PathGraph
from DataFlow::PathNode source, DataFlow::PathNode sink
-where
- exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink))
- or
- // exists(AsymmetricNonECKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink)) or
- // exists(AsymmetricECKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink)) or
- exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlowPath(source, sink))
+where exists(KeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink))
+//or
+// exists(AsymmetricNonECKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink)) or
+// exists(AsymmetricECKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink)) or
+// exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlowPath(source, sink))
select sink.getNode(), source, sink, "This $@ is too small.", source.getNode(), "key size"
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
index bd7c164273f..6d0d83f25fe 100644
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
@@ -1,8 +1,8 @@
import java
import TestUtilities.InlineExpectationsTest
import semmle.code.java.security.InsufficientKeySizeQuery
-import DataFlow::PathGraph
+//import DataFlow::PathGraph // Note: importing this messes up tests - adds edges and nodes to actual file...
class InsufficientKeySizeTest extends InlineExpectationsTest {
InsufficientKeySizeTest() { this = "InsufficientKeySize" }
@@ -11,12 +11,12 @@ class InsufficientKeySizeTest extends InlineExpectationsTest {
override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "hasInsufficientKeySize" and
exists(DataFlow::PathNode source, DataFlow::PathNode sink |
- exists(AsymmetricKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink))
- or
+ exists(KeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink))
+ |
+ //or
// exists(AsymmetricNonECKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink)) or
// exists(AsymmetricECKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink)) or
- exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlowPath(source, sink))
- |
+ //exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlowPath(source, sink))
sink.getNode().getLocation() = location and
element = sink.getNode().toString() and
value = ""
From 0fc4a33d431ef97059a9404eabb733a9a417bda1 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Wed, 12 Oct 2022 08:54:06 -0400
Subject: [PATCH 026/249] remove commented-out code
---
.../security/InsufficientKeySizeQuery.qll | 230 +++++++-----------
1 file changed, 85 insertions(+), 145 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
index d5739fa2310..c141cd04529 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
@@ -4,97 +4,6 @@ import semmle.code.java.security.Encryption
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.TaintTracking
-//import semmle.code.java.dataflow.internal.DataFlowImplCommonPublic
-//import semmle.code.java.dataflow.FlowSources
-//import semmle.code.java.dataflow.internal.DataFlowNodes
-/**
- * A key length data flow tracking configuration.
- */
-class KeyTrackingConfiguration extends DataFlow::Configuration {
- KeyTrackingConfiguration() { this = "KeyTrackingConfiguration" }
-
- override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
- //state instanceof DataFlow::FlowStateEmpty and
- // SYMMETRIC
- source.asExpr().(IntegerLiteral).getIntValue() < 128 and state = "128"
- or
- // ASYMMETRIC
- source.asExpr().(IntegerLiteral).getIntValue() < 2048 and state = "2048"
- or
- source.asExpr().(IntegerLiteral).getIntValue() < 256 and state = "256"
- or
- getECKeySize(source.asExpr().(StringLiteral).getValue()) < 256 and state = "256" // need this for the cases when the key size is embedded in the curve name.
- }
-
- override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
- // SYMMETRIC
- exists(MethodAccess ma, JavaxCryptoKeyGenerator jcg |
- ma.getMethod() instanceof KeyGeneratorInitMethod and
- jcg.getAlgoSpec().(StringLiteral).getValue().toUpperCase() = "AES" and
- DataFlow::localExprFlow(jcg, ma.getQualifier()) and
- sink.asExpr() = ma.getArgument(0) and
- state = "128"
- )
- or
- // ASYMMETRIC
- exists(MethodAccess ma, JavaSecurityKeyPairGenerator jpg |
- ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- (
- jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches(["RSA", "DSA", "DH"]) and
- DataFlow::localExprFlow(jpg, ma.getQualifier()) and
- sink.asExpr() = ma.getArgument(0) and
- //ma.getArgument(0).(LocalSourceNode).flowsTo(sink) and
- //ma.getArgument(0).(CompileTimeConstantExpr).getIntValue() < 2048 and
- state = "2048"
- )
- or
- jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches("EC%") and
- DataFlow::localExprFlow(jpg, ma.getQualifier()) and
- sink.asExpr() = ma.getArgument(0) and
- //ma.getArgument(0).(CompileTimeConstantExpr).getIntValue() < 256 and
- state = "256"
- )
- or
- // TODO: combine below three for less duplicated code
- exists(ClassInstanceExpr rsaKeyGenParamSpec |
- rsaKeyGenParamSpec.getConstructedType() instanceof RsaKeyGenParameterSpec and
- sink.asExpr() = rsaKeyGenParamSpec.getArgument(0) and
- state = "2048"
- )
- or
- exists(ClassInstanceExpr dsaGenParamSpec |
- dsaGenParamSpec.getConstructedType() instanceof DsaGenParameterSpec and
- sink.asExpr() = dsaGenParamSpec.getArgument(0) and
- state = "2048"
- )
- or
- exists(ClassInstanceExpr dhGenParamSpec |
- dhGenParamSpec.getConstructedType() instanceof DhGenParameterSpec and
- sink.asExpr() = dhGenParamSpec.getArgument(0) and
- state = "2048"
- )
- or
- exists(ClassInstanceExpr ecGenParamSpec |
- ecGenParamSpec.getConstructedType() instanceof EcGenParameterSpec and
- sink.asExpr() = ecGenParamSpec.getArgument(0) and
- state = "256"
- )
- }
-
- // ! FlowStates seem to work without even including a step like the below... hmmm
- override predicate isAdditionalFlowStep(
- DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
- DataFlow::FlowState state2
- ) {
- exists(IntegerLiteral intLiteral |
- state1 = "" and
- state2 = intLiteral.toString() and
- node1.asExpr() = intLiteral and
- node2.asExpr() = intLiteral
- )
- }
-}
-
/**
* An Asymmetric (RSA, DSA, DH) key length data flow tracking configuration.
*/
@@ -110,15 +19,6 @@ class AsymmetricNonECKeyTrackingConfiguration extends DataFlow::Configuration {
ma.getMethod() instanceof KeyPairGeneratorInitMethod and
jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches(["RSA", "DSA", "DH"]) and
DataFlow::localExprFlow(jpg, ma.getQualifier()) and
- // exists(
- // JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kpgConfig,
- // DataFlow::PathNode source, DataFlow::PathNode dest
- // |
- // jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches(["RSA", "DSA", "DH"]) and
- // source.getNode().asExpr() = jpg and
- // dest.getNode().asExpr() = ma.getQualifier() and
- // kpgConfig.hasFlowPath(source, dest)
- // ) and
sink.asExpr() = ma.getArgument(0)
)
or
@@ -156,21 +56,11 @@ class AsymmetricECKeyTrackingConfiguration extends DataFlow::Configuration {
ma.getMethod() instanceof KeyPairGeneratorInitMethod and
jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches("EC%") and
DataFlow::localExprFlow(jpg, ma.getQualifier()) and
- // exists(
- // JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kpgConfig,
- // DataFlow::PathNode source, DataFlow::PathNode dest
- // |
- // jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches("EC%") and
- // source.getNode().asExpr() = jpg and
- // dest.getNode().asExpr() = ma.getQualifier() and
- // kpgConfig.hasFlowPath(source, dest)
- // ) and
sink.asExpr() = ma.getArgument(0)
)
or
exists(ClassInstanceExpr ecGenParamSpec |
ecGenParamSpec.getConstructedType() instanceof EcGenParameterSpec and
- //getECKeySize(ecGenParamSpec.getArgument(0).(StringLiteral).getValue()) < 256 and
sink.asExpr() = ecGenParamSpec.getArgument(0)
)
}
@@ -191,15 +81,6 @@ class SymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
ma.getMethod() instanceof KeyGeneratorInitMethod and
jcg.getAlgoSpec().(StringLiteral).getValue().toUpperCase() = "AES" and
DataFlow::localExprFlow(jcg, ma.getQualifier()) and
- // exists(
- // JavaxCryptoKeyGenerator jcg, KeyGeneratorInitConfiguration kgConfig,
- // DataFlow::PathNode source, DataFlow::PathNode dest
- // |
- // jcg.getAlgoSpec().(StringLiteral).getValue().toUpperCase() = "AES" and
- // source.getNode().asExpr() = jcg and
- // dest.getNode().asExpr() = ma.getQualifier() and
- // kgConfig.hasFlowPath(source, dest)
- // ) and
sink.asExpr() = ma.getArgument(0)
)
}
@@ -207,32 +88,6 @@ class SymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
// ********************** Need the below models for the above configs **********************
// todo: move some/all of below to Encryption.qll or elsewhere?
-// /** A data flow configuration tracking flow from a key generator to an `init` method call. */
-// private class KeyGeneratorInitConfiguration extends DataFlow::Configuration {
-// KeyGeneratorInitConfiguration() { this = "KeyGeneratorInitConfiguration" }
-// override predicate isSource(DataFlow::Node source) {
-// source.asExpr() instanceof JavaxCryptoKeyGenerator
-// }
-// override predicate isSink(DataFlow::Node sink) {
-// exists(MethodAccess ma |
-// ma.getMethod() instanceof KeyGeneratorInitMethod and
-// sink.asExpr() = ma.getQualifier()
-// )
-// }
-// }
-// /** A data flow configuration tracking flow from a keypair generator to an `initialize` method call. */
-// private class KeyPairGeneratorInitConfiguration extends DataFlow::Configuration {
-// KeyPairGeneratorInitConfiguration() { this = "KeyPairGeneratorInitConfiguration" }
-// override predicate isSource(DataFlow::Node source) {
-// source.asExpr() instanceof JavaSecurityKeyPairGenerator
-// }
-// override predicate isSink(DataFlow::Node sink) {
-// exists(MethodAccess ma |
-// ma.getMethod() instanceof KeyPairGeneratorInitMethod and
-// sink.asExpr() = ma.getQualifier()
-// )
-// }
-// }
/** The Java class `java.security.spec.ECGenParameterSpec`. */
private class EcGenParameterSpec extends RefType {
EcGenParameterSpec() { this.hasQualifiedName("java.security.spec", "ECGenParameterSpec") }
@@ -288,3 +143,88 @@ private int getECKeySize(string algorithm) {
// todo #2: make representation of sink that can be shared across the configs
// todo #3: make list of algo names more easily reusable (either as constant-type variable at top of file, or model as own class to share, etc.)
// todo #4: refactor to be more like the Python (or C#) version? (or not possible because of lack of DataFlow::Node for void method in Java?)
+// ******* SINGLE CONFIG ATTEMPT BELOW *************************************************************************
+// /**
+// * A key length data flow tracking configuration.
+// */
+// class KeyTrackingConfiguration extends DataFlow::Configuration {
+// KeyTrackingConfiguration() { this = "KeyTrackingConfiguration" }
+// override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
+// //state instanceof DataFlow::FlowStateEmpty and
+// // SYMMETRIC
+// source.asExpr().(IntegerLiteral).getIntValue() < 128 and state = "128"
+// or
+// // ASYMMETRIC
+// source.asExpr().(IntegerLiteral).getIntValue() < 2048 and state = "2048"
+// or
+// source.asExpr().(IntegerLiteral).getIntValue() < 256 and state = "256"
+// or
+// getECKeySize(source.asExpr().(StringLiteral).getValue()) < 256 and state = "256" // need this for the cases when the key size is embedded in the curve name.
+// }
+// override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
+// // SYMMETRIC
+// exists(MethodAccess ma, JavaxCryptoKeyGenerator jcg |
+// ma.getMethod() instanceof KeyGeneratorInitMethod and
+// jcg.getAlgoSpec().(StringLiteral).getValue().toUpperCase() = "AES" and
+// DataFlow::localExprFlow(jcg, ma.getQualifier()) and
+// sink.asExpr() = ma.getArgument(0) and
+// state = "128"
+// )
+// or
+// // ASYMMETRIC
+// exists(MethodAccess ma, JavaSecurityKeyPairGenerator jpg |
+// ma.getMethod() instanceof KeyPairGeneratorInitMethod and
+// (
+// jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches(["RSA", "DSA", "DH"]) and
+// DataFlow::localExprFlow(jpg, ma.getQualifier()) and
+// sink.asExpr() = ma.getArgument(0) and
+// //ma.getArgument(0).(LocalSourceNode).flowsTo(sink) and
+// //ma.getArgument(0).(CompileTimeConstantExpr).getIntValue() < 2048 and
+// state = "2048"
+// )
+// or
+// jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches("EC%") and
+// DataFlow::localExprFlow(jpg, ma.getQualifier()) and
+// sink.asExpr() = ma.getArgument(0) and
+// //ma.getArgument(0).(CompileTimeConstantExpr).getIntValue() < 256 and
+// state = "256"
+// )
+// or
+// // TODO: combine below three for less duplicated code
+// exists(ClassInstanceExpr rsaKeyGenParamSpec |
+// rsaKeyGenParamSpec.getConstructedType() instanceof RsaKeyGenParameterSpec and
+// sink.asExpr() = rsaKeyGenParamSpec.getArgument(0) and
+// state = "2048"
+// )
+// or
+// exists(ClassInstanceExpr dsaGenParamSpec |
+// dsaGenParamSpec.getConstructedType() instanceof DsaGenParameterSpec and
+// sink.asExpr() = dsaGenParamSpec.getArgument(0) and
+// state = "2048"
+// )
+// or
+// exists(ClassInstanceExpr dhGenParamSpec |
+// dhGenParamSpec.getConstructedType() instanceof DhGenParameterSpec and
+// sink.asExpr() = dhGenParamSpec.getArgument(0) and
+// state = "2048"
+// )
+// or
+// exists(ClassInstanceExpr ecGenParamSpec |
+// ecGenParamSpec.getConstructedType() instanceof EcGenParameterSpec and
+// sink.asExpr() = ecGenParamSpec.getArgument(0) and
+// state = "256"
+// )
+// }
+// // ! FlowStates seem to work without even including a step like the below... hmmm
+// override predicate isAdditionalFlowStep(
+// DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
+// DataFlow::FlowState state2
+// ) {
+// exists(IntegerLiteral intLiteral |
+// state1 = "" and
+// state2 = intLiteral.toString() and
+// node1.asExpr() = intLiteral and
+// node2.asExpr() = intLiteral
+// )
+// }
+// }
From 37d85587e007472b976d2416c5a36a5485d20ecd Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Wed, 12 Oct 2022 15:39:57 -0400
Subject: [PATCH 027/249] refactor code into InsufficientKeySize.qll
---
.../semmle/code/java/security/Encryption.qll | 40 ++++++
.../java/security/InsufficientKeySize.qll | 100 +++++++++++++
.../security/InsufficientKeySizeQuery.qll | 133 ++----------------
.../CWE/CWE-326/InsufficientKeySize.ql | 11 +-
.../CWE-326/InsufficientKeySizeTest.ql | 10 +-
5 files changed, 165 insertions(+), 129 deletions(-)
create mode 100644 java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
diff --git a/java/ql/lib/semmle/code/java/security/Encryption.qll b/java/ql/lib/semmle/code/java/security/Encryption.qll
index 39e3f2d2110..a1535a438b5 100644
--- a/java/ql/lib/semmle/code/java/security/Encryption.qll
+++ b/java/ql/lib/semmle/code/java/security/Encryption.qll
@@ -83,11 +83,27 @@ class KeyGenerator extends RefType {
KeyGenerator() { this.hasQualifiedName("javax.crypto", "KeyGenerator") }
}
+/** The `init` method declared in `javax.crypto.KeyGenerator`. */
+class KeyGeneratorInitMethod extends Method {
+ KeyGeneratorInitMethod() {
+ this.getDeclaringType() instanceof KeyGenerator and
+ this.hasName("init")
+ }
+}
+
/** The Java class `java.security.KeyPairGenerator`. */
class KeyPairGenerator extends RefType {
KeyPairGenerator() { this.hasQualifiedName("java.security", "KeyPairGenerator") }
}
+/** The `initialize` method declared in `java.security.KeyPairGenerator`. */
+class KeyPairGeneratorInitMethod extends Method {
+ KeyPairGeneratorInitMethod() {
+ this.getDeclaringType() instanceof KeyPairGenerator and
+ this.hasName("initialize")
+ }
+}
+
/** The `verify` method of the class `javax.net.ssl.HostnameVerifier`. */
class HostnameVerifierVerify extends Method {
HostnameVerifierVerify() {
@@ -307,6 +323,12 @@ class JavaxCryptoSecretKey extends JavaxCryptoAlgoSpec {
}
}
+// TODO: consider extending JavaxCryptoAlgoSpec as above does; will need to override getAlgoSpec() method
+/** The Java class `javax.crypto.spec.DHGenParameterSpec`. */
+class DhGenParameterSpec extends RefType {
+ DhGenParameterSpec() { this.hasQualifiedName("javax.crypto.spec", "DHGenParameterSpec") }
+}
+
class JavaxCryptoKeyGenerator extends JavaxCryptoAlgoSpec {
JavaxCryptoKeyGenerator() {
exists(Method m | m.getAReference() = this |
@@ -367,6 +389,24 @@ class JavaSecuritySignature extends JavaSecurityAlgoSpec {
override Expr getAlgoSpec() { result = this.(ConstructorCall).getArgument(0) }
}
+// TODO: consider extending JavaSecurityAlgoSpec as above does; will need to override getAlgoSpec() method
+/** The Java class `java.security.spec.ECGenParameterSpec`. */
+class EcGenParameterSpec extends RefType {
+ EcGenParameterSpec() { this.hasQualifiedName("java.security.spec", "ECGenParameterSpec") }
+}
+
+// TODO: consider extending JavaSecurityAlgoSpec as above does; will need to override getAlgoSpec() method
+/** The Java class `java.security.spec.RSAKeyGenParameterSpec`. */
+class RsaKeyGenParameterSpec extends RefType {
+ RsaKeyGenParameterSpec() { this.hasQualifiedName("java.security.spec", "RSAKeyGenParameterSpec") }
+}
+
+// TODO: consider extending JavaSecurityAlgoSpec as above does; will need to override getAlgoSpec() method
+/** The Java class `java.security.spec.DSAGenParameterSpec`. */
+class DsaGenParameterSpec extends RefType {
+ DsaGenParameterSpec() { this.hasQualifiedName("java.security.spec", "DSAGenParameterSpec") }
+}
+
/** A method call to the Java class `java.security.KeyPairGenerator`. */
class JavaSecurityKeyPairGenerator extends JavaxCryptoAlgoSpec {
JavaSecurityKeyPairGenerator() {
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
new file mode 100644
index 00000000000..4aa4bbe7a44
--- /dev/null
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
@@ -0,0 +1,100 @@
+/** Provides classes and predicates related to insufficient key sizes in Java. */
+
+private import semmle.code.java.security.Encryption
+private import semmle.code.java.dataflow.DataFlow
+
+/** A source for an insufficient key size. */
+abstract class InsufficientKeySizeSource extends DataFlow::Node { }
+
+/** A sink for an insufficient key size. */
+abstract class InsufficientKeySizeSink extends DataFlow::Node { }
+
+// TODO: Consider if below 3 sources should be private and if it's possible to only use InsufficientKeySizeSource in the configs
+// TODO: add QLDocs if keeping non-private
+class AsymmetricNonECSource extends InsufficientKeySizeSource {
+ AsymmetricNonECSource() { getNodeIntValue(this) < 2048 }
+}
+
+class AsymmetricECSource extends InsufficientKeySizeSource {
+ AsymmetricECSource() {
+ getNodeIntValue(this) < 256 or
+ getECKeySize(this.asExpr().(StringLiteral).getValue()) < 256 // need this for the cases when the key size is embedded in the curve name.
+ }
+}
+
+class SymmetricSource extends InsufficientKeySizeSource {
+ SymmetricSource() { getNodeIntValue(this) < 128 }
+}
+
+private int getNodeIntValue(DataFlow::Node node) {
+ result = node.asExpr().(IntegerLiteral).getIntValue()
+}
+
+/** Returns the key size in the EC algorithm string */
+bindingset[algorithm]
+private int getECKeySize(string algorithm) {
+ algorithm.matches("sec%") and // specification such as "secp256r1"
+ result = algorithm.regexpCapture("sec[p|t](\\d+)[a-zA-Z].*", 1).toInt()
+ or
+ algorithm.matches("X9.62%") and //specification such as "X9.62 prime192v2"
+ result = algorithm.regexpCapture("X9\\.62 .*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
+ or
+ (algorithm.matches("prime%") or algorithm.matches("c2tnb%")) and //specification such as "prime192v2"
+ result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
+}
+
+class AsymmetricNonECSink extends InsufficientKeySizeSink {
+ AsymmetricNonECSink() {
+ exists(MethodAccess ma, JavaSecurityKeyPairGenerator jpg |
+ ma.getMethod() instanceof KeyPairGeneratorInitMethod and
+ jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches(["RSA", "DSA", "DH"]) and
+ DataFlow::localExprFlow(jpg, ma.getQualifier()) and
+ this.asExpr() = ma.getArgument(0)
+ )
+ or
+ exists(ClassInstanceExpr genParamSpec |
+ genParamSpec.getConstructedType() instanceof AsymmetricNonECSpec and
+ this.asExpr() = genParamSpec.getArgument(0)
+ )
+ }
+}
+
+// TODO: move to Encryption.qll? or keep here since specific to this query?
+private class AsymmetricNonECSpec extends RefType {
+ AsymmetricNonECSpec() {
+ this instanceof RsaKeyGenParameterSpec or
+ this instanceof DsaGenParameterSpec or
+ this instanceof DhGenParameterSpec
+ }
+}
+
+class AsymmetricECSink extends InsufficientKeySizeSink {
+ AsymmetricECSink() {
+ exists(MethodAccess ma, JavaSecurityKeyPairGenerator jpg |
+ ma.getMethod() instanceof KeyPairGeneratorInitMethod and
+ jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches("EC%") and
+ DataFlow::localExprFlow(jpg, ma.getQualifier()) and
+ this.asExpr() = ma.getArgument(0)
+ )
+ or
+ exists(ClassInstanceExpr ecGenParamSpec |
+ ecGenParamSpec.getConstructedType() instanceof EcGenParameterSpec and
+ this.asExpr() = ecGenParamSpec.getArgument(0)
+ )
+ }
+}
+
+class SymmetricSink extends InsufficientKeySizeSink {
+ SymmetricSink() {
+ exists(MethodAccess ma, JavaxCryptoKeyGenerator jcg |
+ ma.getMethod() instanceof KeyGeneratorInitMethod and
+ jcg.getAlgoSpec().(StringLiteral).getValue().toUpperCase() = "AES" and
+ DataFlow::localExprFlow(jcg, ma.getQualifier()) and
+ this.asExpr() = ma.getArgument(0)
+ )
+ }
+}
+// TODO:
+// todo #0: look into use of specs without keygen objects; should spec not be a sink in these cases?
+// todo #3: make list of algo names more easily reusable (either as constant-type variable at top of file, or model as own class to share, etc.)
+// todo #5:
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
index c141cd04529..51800aa8df9 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
@@ -1,148 +1,43 @@
-/** Provides classes and predicates related to insufficient key sizes in Java. */
+/** Provides data flow configurations to be used in queries related to insufficient key sizes. */
import semmle.code.java.security.Encryption
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.TaintTracking
+import semmle.code.java.security.InsufficientKeySize
/**
- * An Asymmetric (RSA, DSA, DH) key length data flow tracking configuration.
+ * A data flow configuration for tracking non-elliptic curve asymmetric algorithms
+ * (RSA, DSA, and DH) key sizes.
*/
class AsymmetricNonECKeyTrackingConfiguration extends DataFlow::Configuration {
AsymmetricNonECKeyTrackingConfiguration() { this = "AsymmetricNonECKeyTrackingConfiguration" }
- override predicate isSource(DataFlow::Node source) {
- source.asExpr().(IntegerLiteral).getIntValue() < 2048
- }
+ override predicate isSource(DataFlow::Node source) { source instanceof AsymmetricNonECSource }
- override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma, JavaSecurityKeyPairGenerator jpg |
- ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches(["RSA", "DSA", "DH"]) and
- DataFlow::localExprFlow(jpg, ma.getQualifier()) and
- sink.asExpr() = ma.getArgument(0)
- )
- or
- // TODO: combine below three for less duplicated code
- exists(ClassInstanceExpr rsaKeyGenParamSpec |
- rsaKeyGenParamSpec.getConstructedType() instanceof RsaKeyGenParameterSpec and
- sink.asExpr() = rsaKeyGenParamSpec.getArgument(0)
- )
- or
- exists(ClassInstanceExpr dsaGenParamSpec |
- dsaGenParamSpec.getConstructedType() instanceof DsaGenParameterSpec and
- sink.asExpr() = dsaGenParamSpec.getArgument(0)
- )
- or
- exists(ClassInstanceExpr dhGenParamSpec |
- dhGenParamSpec.getConstructedType() instanceof DhGenParameterSpec and
- sink.asExpr() = dhGenParamSpec.getArgument(0)
- )
- }
+ override predicate isSink(DataFlow::Node sink) { sink instanceof AsymmetricNonECSink }
}
/**
- * An Asymmetric (EC) key length data flow tracking configuration.
+ * A data flow configuration for tracking elliptic curve (EC) asymmetric
+ * algorithm key sizes.
*/
class AsymmetricECKeyTrackingConfiguration extends DataFlow::Configuration {
AsymmetricECKeyTrackingConfiguration() { this = "AsymmetricECKeyTrackingConfiguration" }
- override predicate isSource(DataFlow::Node source) {
- source.asExpr().(IntegerLiteral).getIntValue() < 256 or
- getECKeySize(source.asExpr().(StringLiteral).getValue()) < 256 // need this for the cases when the key size is embedded in the curve name.
- }
+ override predicate isSource(DataFlow::Node source) { source instanceof AsymmetricECSource }
- override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma, JavaSecurityKeyPairGenerator jpg |
- ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches("EC%") and
- DataFlow::localExprFlow(jpg, ma.getQualifier()) and
- sink.asExpr() = ma.getArgument(0)
- )
- or
- exists(ClassInstanceExpr ecGenParamSpec |
- ecGenParamSpec.getConstructedType() instanceof EcGenParameterSpec and
- sink.asExpr() = ecGenParamSpec.getArgument(0)
- )
- }
+ override predicate isSink(DataFlow::Node sink) { sink instanceof AsymmetricECSink }
}
-/**
- * A Symmetric (AES) key length data flow tracking configuration.
- */
+/** A data flow configuration for tracking symmetric algorithm (AES) key sizes. */
class SymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
SymmetricKeyTrackingConfiguration() { this = "SymmetricKeyTrackingConfiguration" }
- override predicate isSource(DataFlow::Node source) {
- source.asExpr().(IntegerLiteral).getIntValue() < 128
- }
+ override predicate isSource(DataFlow::Node source) { source instanceof SymmetricSource }
- override predicate isSink(DataFlow::Node sink) {
- exists(MethodAccess ma, JavaxCryptoKeyGenerator jcg |
- ma.getMethod() instanceof KeyGeneratorInitMethod and
- jcg.getAlgoSpec().(StringLiteral).getValue().toUpperCase() = "AES" and
- DataFlow::localExprFlow(jcg, ma.getQualifier()) and
- sink.asExpr() = ma.getArgument(0)
- )
- }
+ override predicate isSink(DataFlow::Node sink) { sink instanceof SymmetricSink }
}
-
-// ********************** Need the below models for the above configs **********************
-// todo: move some/all of below to Encryption.qll or elsewhere?
-/** The Java class `java.security.spec.ECGenParameterSpec`. */
-private class EcGenParameterSpec extends RefType {
- EcGenParameterSpec() { this.hasQualifiedName("java.security.spec", "ECGenParameterSpec") }
-}
-
-/** The Java class `java.security.spec.RSAKeyGenParameterSpec`. */
-private class RsaKeyGenParameterSpec extends RefType {
- RsaKeyGenParameterSpec() { this.hasQualifiedName("java.security.spec", "RSAKeyGenParameterSpec") }
-}
-
-/** The Java class `java.security.spec.DSAGenParameterSpec`. */
-private class DsaGenParameterSpec extends RefType {
- DsaGenParameterSpec() { this.hasQualifiedName("java.security.spec", "DSAGenParameterSpec") }
-}
-
-/** The Java class `javax.crypto.spec.DHGenParameterSpec`. */
-private class DhGenParameterSpec extends RefType {
- DhGenParameterSpec() { this.hasQualifiedName("javax.crypto.spec", "DHGenParameterSpec") }
-}
-
-/** The `init` method declared in `javax.crypto.KeyGenerator`. */
-private class KeyGeneratorInitMethod extends Method {
- KeyGeneratorInitMethod() {
- this.getDeclaringType() instanceof KeyGenerator and
- this.hasName("init")
- }
-}
-
-/** The `initialize` method declared in `java.security.KeyPairGenerator`. */
-private class KeyPairGeneratorInitMethod extends Method {
- KeyPairGeneratorInitMethod() {
- this.getDeclaringType() instanceof KeyPairGenerator and
- this.hasName("initialize")
- }
-}
-
-/** Returns the key size in the EC algorithm string */
-bindingset[algorithm]
-private int getECKeySize(string algorithm) {
- algorithm.matches("sec%") and // specification such as "secp256r1"
- result = algorithm.regexpCapture("sec[p|t](\\d+)[a-zA-Z].*", 1).toInt()
- or
- algorithm.matches("X9.62%") and //specification such as "X9.62 prime192v2"
- result = algorithm.regexpCapture("X9\\.62 .*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
- or
- (algorithm.matches("prime%") or algorithm.matches("c2tnb%")) and //specification such as "prime192v2"
- result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
-}
-// ******* DATAFLOW ABOVE *************************************************************************
-// TODO:
-// todo #0: look into use of specs without keygens; should spec not be a sink in these cases?
-// todo #1: make representation of source that can be shared across the configs
-// todo #2: make representation of sink that can be shared across the configs
-// todo #3: make list of algo names more easily reusable (either as constant-type variable at top of file, or model as own class to share, etc.)
-// todo #4: refactor to be more like the Python (or C#) version? (or not possible because of lack of DataFlow::Node for void method in Java?)
+// ******* 3 DATAFLOW CONFIGS ABOVE *************************************************************************
// ******* SINGLE CONFIG ATTEMPT BELOW *************************************************************************
// /**
// * A key length data flow tracking configuration.
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
index 040f17abfc0..cb560aa1f34 100644
--- a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
+++ b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
@@ -16,9 +16,10 @@ import semmle.code.java.security.InsufficientKeySizeQuery
import DataFlow::PathGraph
from DataFlow::PathNode source, DataFlow::PathNode sink
-where exists(KeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink))
-//or
-// exists(AsymmetricNonECKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink)) or
-// exists(AsymmetricECKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink)) or
-// exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlowPath(source, sink))
+where
+ //exists(KeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink))
+ //or
+ exists(AsymmetricNonECKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink)) or
+ exists(AsymmetricECKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink)) or
+ exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlowPath(source, sink))
select sink.getNode(), source, sink, "This $@ is too small.", source.getNode(), "key size"
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
index 6d0d83f25fe..869831242f9 100644
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
@@ -11,12 +11,12 @@ class InsufficientKeySizeTest extends InlineExpectationsTest {
override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "hasInsufficientKeySize" and
exists(DataFlow::PathNode source, DataFlow::PathNode sink |
- exists(KeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink))
- |
+ //exists(KeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink))
//or
- // exists(AsymmetricNonECKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink)) or
- // exists(AsymmetricECKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink)) or
- //exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlowPath(source, sink))
+ exists(AsymmetricNonECKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink)) or
+ exists(AsymmetricECKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink)) or
+ exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlowPath(source, sink))
+ |
sink.getNode().getLocation() = location and
element = sink.getNode().toString() and
value = ""
From bfbb6db43612ca347170ac778ab994761a5ca3f6 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Wed, 12 Oct 2022 16:58:34 -0400
Subject: [PATCH 028/249] clean up code
---
.../java/security/InsufficientKeySize.qll | 82 ++++++++++++-------
.../CWE/CWE-326/InsufficientKeySize.ql | 6 +-
.../CWE-326/InsufficientKeySizeTest.ql | 6 +-
3 files changed, 59 insertions(+), 35 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
index 4aa4bbe7a44..8e45a2c2387 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
@@ -26,6 +26,7 @@ class SymmetricSource extends InsufficientKeySizeSource {
SymmetricSource() { getNodeIntValue(this) < 128 }
}
+// TODO: use `typeFlag` like with sinks below to include the size numbers in this predicate?
private int getNodeIntValue(DataFlow::Node node) {
result = node.asExpr().(IntegerLiteral).getIntValue()
}
@@ -43,19 +44,13 @@ private int getECKeySize(string algorithm) {
result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
}
+// TODO: Consider if below 3 sinks should be private and if it's possible to only use InsufficientKeySizeSink in the configs
+// TODO: add QLDocs if keeping non-private
class AsymmetricNonECSink extends InsufficientKeySizeSink {
AsymmetricNonECSink() {
- exists(MethodAccess ma, JavaSecurityKeyPairGenerator jpg |
- ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches(["RSA", "DSA", "DH"]) and
- DataFlow::localExprFlow(jpg, ma.getQualifier()) and
- this.asExpr() = ma.getArgument(0)
- )
+ hasKeySizeInInitMethod(this, "asymmetric-non-ec")
or
- exists(ClassInstanceExpr genParamSpec |
- genParamSpec.getConstructedType() instanceof AsymmetricNonECSpec and
- this.asExpr() = genParamSpec.getArgument(0)
- )
+ hasKeySizeInSpec(this, "asymmetric-non-ec")
}
}
@@ -70,31 +65,60 @@ private class AsymmetricNonECSpec extends RefType {
class AsymmetricECSink extends InsufficientKeySizeSink {
AsymmetricECSink() {
- exists(MethodAccess ma, JavaSecurityKeyPairGenerator jpg |
- ma.getMethod() instanceof KeyPairGeneratorInitMethod and
- jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches("EC%") and
- DataFlow::localExprFlow(jpg, ma.getQualifier()) and
- this.asExpr() = ma.getArgument(0)
- )
+ hasKeySizeInInitMethod(this, "asymmetric-ec")
or
- exists(ClassInstanceExpr ecGenParamSpec |
- ecGenParamSpec.getConstructedType() instanceof EcGenParameterSpec and
- this.asExpr() = ecGenParamSpec.getArgument(0)
- )
+ hasKeySizeInSpec(this, "asymmetric-ec")
}
}
class SymmetricSink extends InsufficientKeySizeSink {
- SymmetricSink() {
- exists(MethodAccess ma, JavaxCryptoKeyGenerator jcg |
- ma.getMethod() instanceof KeyGeneratorInitMethod and
- jcg.getAlgoSpec().(StringLiteral).getValue().toUpperCase() = "AES" and
- DataFlow::localExprFlow(jcg, ma.getQualifier()) and
- this.asExpr() = ma.getArgument(0)
- )
- }
+ SymmetricSink() { hasKeySizeInInitMethod(this, "symmetric") }
+}
+
+// TODO: rethink the predicate name; also think about whether this could/should be a class instead; or a predicate within the sink class so can do sink.predicate()...
+// TODO: can prbly re-work way using the typeFlag to be better and less repetitive
+private predicate hasKeySizeInInitMethod(DataFlow::Node node, string typeFlag) {
+ exists(MethodAccess ma, JavaxCryptoAlgoSpec jcaSpec |
+ (
+ ma.getMethod() instanceof KeyGeneratorInitMethod and typeFlag = "symmetric"
+ or
+ ma.getMethod() instanceof KeyPairGeneratorInitMethod and typeFlag.matches("asymmetric%")
+ ) and
+ (
+ jcaSpec instanceof JavaxCryptoKeyGenerator and typeFlag = "symmetric"
+ or
+ jcaSpec instanceof JavaSecurityKeyPairGenerator and typeFlag.matches("asymmetric%")
+ ) and
+ (
+ jcaSpec.getAlgoSpec().(StringLiteral).getValue().toUpperCase() = "AES" and
+ typeFlag = "symmetric"
+ or
+ jcaSpec.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches(["RSA", "DSA", "DH"]) and
+ typeFlag = "asymmetric-non-ec"
+ or
+ jcaSpec.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches("EC%") and
+ typeFlag = "asymmetric-ec"
+ ) and
+ DataFlow::localExprFlow(jcaSpec, ma.getQualifier()) and
+ node.asExpr() = ma.getArgument(0)
+ )
+}
+
+// TODO: rethink the predicate name; also think about whether this could/should be a class instead; or a predicate within the sink class so can do sink.predicate()...
+// TODO: can prbly re-work way using the typeFlag to be better and less repetitive
+private predicate hasKeySizeInSpec(DataFlow::Node node, string typeFlag) {
+ exists(ClassInstanceExpr paramSpec |
+ (
+ paramSpec.getConstructedType() instanceof AsymmetricNonECSpec and
+ typeFlag = "asymmetric-non-ec"
+ or
+ paramSpec.getConstructedType() instanceof EcGenParameterSpec and
+ typeFlag = "asymmetric-ec"
+ ) and
+ node.asExpr() = paramSpec.getArgument(0)
+ )
}
// TODO:
// todo #0: look into use of specs without keygen objects; should spec not be a sink in these cases?
// todo #3: make list of algo names more easily reusable (either as constant-type variable at top of file, or model as own class to share, etc.)
-// todo #5:
+// todo #4: use flowstate (or even just result predicate) to pass source int size to sink?
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
index cb560aa1f34..f9d3d3baa0f 100644
--- a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
+++ b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
@@ -19,7 +19,7 @@ from DataFlow::PathNode source, DataFlow::PathNode sink
where
//exists(KeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink))
//or
- exists(AsymmetricNonECKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink)) or
- exists(AsymmetricECKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink)) or
- exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlowPath(source, sink))
+ exists(AsymmetricNonECKeyTrackingConfiguration cfg | cfg.hasFlowPath(source, sink)) or
+ exists(AsymmetricECKeyTrackingConfiguration cfg | cfg.hasFlowPath(source, sink)) or
+ exists(SymmetricKeyTrackingConfiguration cfg | cfg.hasFlowPath(source, sink))
select sink.getNode(), source, sink, "This $@ is too small.", source.getNode(), "key size"
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
index 869831242f9..db925935d5c 100644
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
@@ -13,9 +13,9 @@ class InsufficientKeySizeTest extends InlineExpectationsTest {
exists(DataFlow::PathNode source, DataFlow::PathNode sink |
//exists(KeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink))
//or
- exists(AsymmetricNonECKeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink)) or
- exists(AsymmetricECKeyTrackingConfiguration config2 | config2.hasFlowPath(source, sink)) or
- exists(SymmetricKeyTrackingConfiguration config3 | config3.hasFlowPath(source, sink))
+ exists(AsymmetricNonECKeyTrackingConfiguration cfg | cfg.hasFlowPath(source, sink)) or
+ exists(AsymmetricECKeyTrackingConfiguration cfg | cfg.hasFlowPath(source, sink)) or
+ exists(SymmetricKeyTrackingConfiguration cfg | cfg.hasFlowPath(source, sink))
|
sink.getNode().getLocation() = location and
element = sink.getNode().toString() and
From bcb506b63795ba65d823d716a260802d3196f983 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Wed, 12 Oct 2022 17:04:51 -0400
Subject: [PATCH 029/249] add placeholder qldocs
---
.../lib/semmle/code/java/security/InsufficientKeySize.qll | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
index 8e45a2c2387..8f41252908d 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
@@ -11,10 +11,12 @@ abstract class InsufficientKeySizeSink extends DataFlow::Node { }
// TODO: Consider if below 3 sources should be private and if it's possible to only use InsufficientKeySizeSource in the configs
// TODO: add QLDocs if keeping non-private
+/** A source for an insufficient key size (asymmetric-non-ec). */
class AsymmetricNonECSource extends InsufficientKeySizeSource {
AsymmetricNonECSource() { getNodeIntValue(this) < 2048 }
}
+/** A source for an insufficient key size (asymmetric-ec). */
class AsymmetricECSource extends InsufficientKeySizeSource {
AsymmetricECSource() {
getNodeIntValue(this) < 256 or
@@ -22,6 +24,7 @@ class AsymmetricECSource extends InsufficientKeySizeSource {
}
}
+/** A source for an insufficient key size (symmetric). */
class SymmetricSource extends InsufficientKeySizeSource {
SymmetricSource() { getNodeIntValue(this) < 128 }
}
@@ -46,6 +49,7 @@ private int getECKeySize(string algorithm) {
// TODO: Consider if below 3 sinks should be private and if it's possible to only use InsufficientKeySizeSink in the configs
// TODO: add QLDocs if keeping non-private
+/** A sink for an insufficient key size (asymmetric-non-ec). */
class AsymmetricNonECSink extends InsufficientKeySizeSink {
AsymmetricNonECSink() {
hasKeySizeInInitMethod(this, "asymmetric-non-ec")
@@ -63,6 +67,7 @@ private class AsymmetricNonECSpec extends RefType {
}
}
+/** A sink for an insufficient key size (asymmetric-ec). */
class AsymmetricECSink extends InsufficientKeySizeSink {
AsymmetricECSink() {
hasKeySizeInInitMethod(this, "asymmetric-ec")
@@ -71,6 +76,7 @@ class AsymmetricECSink extends InsufficientKeySizeSink {
}
}
+/** A sink for an insufficient key size (symmetric). */
class SymmetricSink extends InsufficientKeySizeSink {
SymmetricSink() { hasKeySizeInInitMethod(this, "symmetric") }
}
From e0f0d554cb11dce3a9522e9be926f879390276be Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Wed, 12 Oct 2022 22:18:07 -0400
Subject: [PATCH 030/249] condense code
---
.../code/java/security/InsufficientKeySize.qll | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
index 8f41252908d..53efe07b490 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
@@ -96,20 +96,22 @@ private predicate hasKeySizeInInitMethod(DataFlow::Node node, string typeFlag) {
jcaSpec instanceof JavaSecurityKeyPairGenerator and typeFlag.matches("asymmetric%")
) and
(
- jcaSpec.getAlgoSpec().(StringLiteral).getValue().toUpperCase() = "AES" and
- typeFlag = "symmetric"
+ getAlgoName(jcaSpec) = "AES" and typeFlag = "symmetric"
or
- jcaSpec.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches(["RSA", "DSA", "DH"]) and
- typeFlag = "asymmetric-non-ec"
+ getAlgoName(jcaSpec).matches(["RSA", "DSA", "DH"]) and typeFlag = "asymmetric-non-ec"
or
- jcaSpec.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches("EC%") and
- typeFlag = "asymmetric-ec"
+ getAlgoName(jcaSpec).matches("EC%") and typeFlag = "asymmetric-ec"
) and
DataFlow::localExprFlow(jcaSpec, ma.getQualifier()) and
node.asExpr() = ma.getArgument(0)
)
}
+// TODO: this predicate is just a poc for more code condensing; redo this
+private string getAlgoName(JavaxCryptoAlgoSpec jca) {
+ result = jca.getAlgoSpec().(StringLiteral).getValue().toUpperCase()
+}
+
// TODO: rethink the predicate name; also think about whether this could/should be a class instead; or a predicate within the sink class so can do sink.predicate()...
// TODO: can prbly re-work way using the typeFlag to be better and less repetitive
private predicate hasKeySizeInSpec(DataFlow::Node node, string typeFlag) {
From 2daa3457d79906b53353ad8a5cc0c475e770f5bd Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Thu, 13 Oct 2022 17:57:56 -0400
Subject: [PATCH 031/249] combine three configs into one
---
.../java/security/InsufficientKeySize.qll | 58 ++++---
.../security/InsufficientKeySizeQuery.qll | 141 ++++--------------
.../CWE/CWE-326/InsufficientKeySize.ql | 11 +-
.../CWE-326/InsufficientKeySizeTest.ql | 10 +-
4 files changed, 79 insertions(+), 141 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
index 53efe07b490..dde014fe6de 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
@@ -3,30 +3,41 @@
private import semmle.code.java.security.Encryption
private import semmle.code.java.dataflow.DataFlow
+// TODO: only update key sizes (and key size strings in one place in the code)
/** A source for an insufficient key size. */
-abstract class InsufficientKeySizeSource extends DataFlow::Node { }
+abstract class InsufficientKeySizeSource extends DataFlow::Node {
+ /** Holds if this source has the specified `state`. */
+ predicate hasState(DataFlow::FlowState state) { state instanceof DataFlow::FlowStateEmpty }
+}
/** A sink for an insufficient key size. */
-abstract class InsufficientKeySizeSink extends DataFlow::Node { }
-
-// TODO: Consider if below 3 sources should be private and if it's possible to only use InsufficientKeySizeSource in the configs
-// TODO: add QLDocs if keeping non-private
-/** A source for an insufficient key size (asymmetric-non-ec). */
-class AsymmetricNonECSource extends InsufficientKeySizeSource {
- AsymmetricNonECSource() { getNodeIntValue(this) < 2048 }
+abstract class InsufficientKeySizeSink extends DataFlow::Node {
+ /** Holds if this sink has the specified `state`. */
+ predicate hasState(DataFlow::FlowState state) { state instanceof DataFlow::FlowStateEmpty }
}
-/** A source for an insufficient key size (asymmetric-ec). */
-class AsymmetricECSource extends InsufficientKeySizeSource {
+/** A source for an insufficient key size (asymmetric-non-ec: RSA, DSA, DH). */
+private class AsymmetricNonECSource extends InsufficientKeySizeSource {
+ AsymmetricNonECSource() { getNodeIntValue(this) < 2048 }
+
+ override predicate hasState(DataFlow::FlowState state) { state = "2048" }
+}
+
+/** A source for an insufficient key size (asymmetric-ec: EC%). */
+private class AsymmetricECSource extends InsufficientKeySizeSource {
AsymmetricECSource() {
getNodeIntValue(this) < 256 or
- getECKeySize(this.asExpr().(StringLiteral).getValue()) < 256 // need this for the cases when the key size is embedded in the curve name.
+ getECKeySize(this.asExpr().(StringLiteral).getValue()) < 256 // for cases when the key size is embedded in the curve name
}
+
+ override predicate hasState(DataFlow::FlowState state) { state = "256" }
}
-/** A source for an insufficient key size (symmetric). */
-class SymmetricSource extends InsufficientKeySizeSource {
+/** A source for an insufficient key size (symmetric: AES). */
+private class SymmetricSource extends InsufficientKeySizeSource {
SymmetricSource() { getNodeIntValue(this) < 128 }
+
+ override predicate hasState(DataFlow::FlowState state) { state = "128" }
}
// TODO: use `typeFlag` like with sinks below to include the size numbers in this predicate?
@@ -34,6 +45,7 @@ private int getNodeIntValue(DataFlow::Node node) {
result = node.asExpr().(IntegerLiteral).getIntValue()
}
+// TODO: check if any other regex should be added based on some MRVA results.
/** Returns the key size in the EC algorithm string */
bindingset[algorithm]
private int getECKeySize(string algorithm) {
@@ -47,18 +59,17 @@ private int getECKeySize(string algorithm) {
result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
}
-// TODO: Consider if below 3 sinks should be private and if it's possible to only use InsufficientKeySizeSink in the configs
-// TODO: add QLDocs if keeping non-private
/** A sink for an insufficient key size (asymmetric-non-ec). */
-class AsymmetricNonECSink extends InsufficientKeySizeSink {
+private class AsymmetricNonECSink extends InsufficientKeySizeSink {
AsymmetricNonECSink() {
hasKeySizeInInitMethod(this, "asymmetric-non-ec")
or
hasKeySizeInSpec(this, "asymmetric-non-ec")
}
+
+ override predicate hasState(DataFlow::FlowState state) { state = "2048" }
}
-// TODO: move to Encryption.qll? or keep here since specific to this query?
private class AsymmetricNonECSpec extends RefType {
AsymmetricNonECSpec() {
this instanceof RsaKeyGenParameterSpec or
@@ -68,17 +79,21 @@ private class AsymmetricNonECSpec extends RefType {
}
/** A sink for an insufficient key size (asymmetric-ec). */
-class AsymmetricECSink extends InsufficientKeySizeSink {
+private class AsymmetricECSink extends InsufficientKeySizeSink {
AsymmetricECSink() {
hasKeySizeInInitMethod(this, "asymmetric-ec")
or
hasKeySizeInSpec(this, "asymmetric-ec")
}
+
+ override predicate hasState(DataFlow::FlowState state) { state = "256" }
}
/** A sink for an insufficient key size (symmetric). */
-class SymmetricSink extends InsufficientKeySizeSink {
+private class SymmetricSink extends InsufficientKeySizeSink {
SymmetricSink() { hasKeySizeInInitMethod(this, "symmetric") }
+
+ override predicate hasState(DataFlow::FlowState state) { state = "128" }
}
// TODO: rethink the predicate name; also think about whether this could/should be a class instead; or a predicate within the sink class so can do sink.predicate()...
@@ -113,7 +128,7 @@ private string getAlgoName(JavaxCryptoAlgoSpec jca) {
}
// TODO: rethink the predicate name; also think about whether this could/should be a class instead; or a predicate within the sink class so can do sink.predicate()...
-// TODO: can prbly re-work way using the typeFlag to be better and less repetitive
+// TODO: can prbly re-work way using the typeFlag to be better and less repetitive...
private predicate hasKeySizeInSpec(DataFlow::Node node, string typeFlag) {
exists(ClassInstanceExpr paramSpec |
(
@@ -126,7 +141,8 @@ private predicate hasKeySizeInSpec(DataFlow::Node node, string typeFlag) {
node.asExpr() = paramSpec.getArgument(0)
)
}
+
+class SpecWithKeySize extends RefType { }
// TODO:
// todo #0: look into use of specs without keygen objects; should spec not be a sink in these cases?
// todo #3: make list of algo names more easily reusable (either as constant-type variable at top of file, or model as own class to share, etc.)
-// todo #4: use flowstate (or even just result predicate) to pass source int size to sink?
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
index 51800aa8df9..08388c345d6 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
@@ -6,120 +6,43 @@ import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.security.InsufficientKeySize
/**
- * A data flow configuration for tracking non-elliptic curve asymmetric algorithms
+ * A data flow configuration for tracking non-elliptic curve asymmetric algorithm
* (RSA, DSA, and DH) key sizes.
*/
-class AsymmetricNonECKeyTrackingConfiguration extends DataFlow::Configuration {
- AsymmetricNonECKeyTrackingConfiguration() { this = "AsymmetricNonECKeyTrackingConfiguration" }
+class KeySizeConfiguration extends DataFlow::Configuration {
+ KeySizeConfiguration() { this = "KeySizeConfiguration" }
- override predicate isSource(DataFlow::Node source) { source instanceof AsymmetricNonECSource }
+ override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
+ source.(InsufficientKeySizeSource).hasState(state)
+ //source instanceof InsufficientKeySizeSource
+ }
- override predicate isSink(DataFlow::Node sink) { sink instanceof AsymmetricNonECSink }
+ override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
+ sink.(InsufficientKeySizeSink).hasState(state)
+ //sink instanceof InsufficientKeySizeSink
+ }
}
-
-/**
- * A data flow configuration for tracking elliptic curve (EC) asymmetric
- * algorithm key sizes.
- */
-class AsymmetricECKeyTrackingConfiguration extends DataFlow::Configuration {
- AsymmetricECKeyTrackingConfiguration() { this = "AsymmetricECKeyTrackingConfiguration" }
-
- override predicate isSource(DataFlow::Node source) { source instanceof AsymmetricECSource }
-
- override predicate isSink(DataFlow::Node sink) { sink instanceof AsymmetricECSink }
-}
-
-/** A data flow configuration for tracking symmetric algorithm (AES) key sizes. */
-class SymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
- SymmetricKeyTrackingConfiguration() { this = "SymmetricKeyTrackingConfiguration" }
-
- override predicate isSource(DataFlow::Node source) { source instanceof SymmetricSource }
-
- override predicate isSink(DataFlow::Node sink) { sink instanceof SymmetricSink }
-}
-// ******* 3 DATAFLOW CONFIGS ABOVE *************************************************************************
-// ******* SINGLE CONFIG ATTEMPT BELOW *************************************************************************
// /**
-// * A key length data flow tracking configuration.
+// * A data flow configuration for tracking non-elliptic curve asymmetric algorithm
+// * (RSA, DSA, and DH) key sizes.
// */
-// class KeyTrackingConfiguration extends DataFlow::Configuration {
-// KeyTrackingConfiguration() { this = "KeyTrackingConfiguration" }
-// override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
-// //state instanceof DataFlow::FlowStateEmpty and
-// // SYMMETRIC
-// source.asExpr().(IntegerLiteral).getIntValue() < 128 and state = "128"
-// or
-// // ASYMMETRIC
-// source.asExpr().(IntegerLiteral).getIntValue() < 2048 and state = "2048"
-// or
-// source.asExpr().(IntegerLiteral).getIntValue() < 256 and state = "256"
-// or
-// getECKeySize(source.asExpr().(StringLiteral).getValue()) < 256 and state = "256" // need this for the cases when the key size is embedded in the curve name.
-// }
-// override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
-// // SYMMETRIC
-// exists(MethodAccess ma, JavaxCryptoKeyGenerator jcg |
-// ma.getMethod() instanceof KeyGeneratorInitMethod and
-// jcg.getAlgoSpec().(StringLiteral).getValue().toUpperCase() = "AES" and
-// DataFlow::localExprFlow(jcg, ma.getQualifier()) and
-// sink.asExpr() = ma.getArgument(0) and
-// state = "128"
-// )
-// or
-// // ASYMMETRIC
-// exists(MethodAccess ma, JavaSecurityKeyPairGenerator jpg |
-// ma.getMethod() instanceof KeyPairGeneratorInitMethod and
-// (
-// jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches(["RSA", "DSA", "DH"]) and
-// DataFlow::localExprFlow(jpg, ma.getQualifier()) and
-// sink.asExpr() = ma.getArgument(0) and
-// //ma.getArgument(0).(LocalSourceNode).flowsTo(sink) and
-// //ma.getArgument(0).(CompileTimeConstantExpr).getIntValue() < 2048 and
-// state = "2048"
-// )
-// or
-// jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase().matches("EC%") and
-// DataFlow::localExprFlow(jpg, ma.getQualifier()) and
-// sink.asExpr() = ma.getArgument(0) and
-// //ma.getArgument(0).(CompileTimeConstantExpr).getIntValue() < 256 and
-// state = "256"
-// )
-// or
-// // TODO: combine below three for less duplicated code
-// exists(ClassInstanceExpr rsaKeyGenParamSpec |
-// rsaKeyGenParamSpec.getConstructedType() instanceof RsaKeyGenParameterSpec and
-// sink.asExpr() = rsaKeyGenParamSpec.getArgument(0) and
-// state = "2048"
-// )
-// or
-// exists(ClassInstanceExpr dsaGenParamSpec |
-// dsaGenParamSpec.getConstructedType() instanceof DsaGenParameterSpec and
-// sink.asExpr() = dsaGenParamSpec.getArgument(0) and
-// state = "2048"
-// )
-// or
-// exists(ClassInstanceExpr dhGenParamSpec |
-// dhGenParamSpec.getConstructedType() instanceof DhGenParameterSpec and
-// sink.asExpr() = dhGenParamSpec.getArgument(0) and
-// state = "2048"
-// )
-// or
-// exists(ClassInstanceExpr ecGenParamSpec |
-// ecGenParamSpec.getConstructedType() instanceof EcGenParameterSpec and
-// sink.asExpr() = ecGenParamSpec.getArgument(0) and
-// state = "256"
-// )
-// }
-// // ! FlowStates seem to work without even including a step like the below... hmmm
-// override predicate isAdditionalFlowStep(
-// DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
-// DataFlow::FlowState state2
-// ) {
-// exists(IntegerLiteral intLiteral |
-// state1 = "" and
-// state2 = intLiteral.toString() and
-// node1.asExpr() = intLiteral and
-// node2.asExpr() = intLiteral
-// )
-// }
+// class AsymmetricNonECKeyTrackingConfiguration extends DataFlow::Configuration {
+// AsymmetricNonECKeyTrackingConfiguration() { this = "AsymmetricNonECKeyTrackingConfiguration" }
+// override predicate isSource(DataFlow::Node source) { source instanceof AsymmetricNonECSource }
+// override predicate isSink(DataFlow::Node sink) { sink instanceof AsymmetricNonECSink }
+// }
+// /**
+// * A data flow configuration for tracking elliptic curve (EC) asymmetric
+// * algorithm key sizes.
+// */
+// class AsymmetricECKeyTrackingConfiguration extends DataFlow::Configuration {
+// AsymmetricECKeyTrackingConfiguration() { this = "AsymmetricECKeyTrackingConfiguration" }
+// override predicate isSource(DataFlow::Node source) { source instanceof AsymmetricECSource }
+// override predicate isSink(DataFlow::Node sink) { sink instanceof AsymmetricECSink }
+// }
+// /** A data flow configuration for tracking symmetric algorithm (AES) key sizes. */
+// class SymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
+// SymmetricKeyTrackingConfiguration() { this = "SymmetricKeyTrackingConfiguration" }
+// override predicate isSource(DataFlow::Node source) { source instanceof SymmetricSource }
+// override predicate isSink(DataFlow::Node sink) { sink instanceof SymmetricSink }
// }
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
index f9d3d3baa0f..8f0f54c7d9b 100644
--- a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
+++ b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
@@ -16,10 +16,9 @@ import semmle.code.java.security.InsufficientKeySizeQuery
import DataFlow::PathGraph
from DataFlow::PathNode source, DataFlow::PathNode sink
-where
- //exists(KeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink))
- //or
- exists(AsymmetricNonECKeyTrackingConfiguration cfg | cfg.hasFlowPath(source, sink)) or
- exists(AsymmetricECKeyTrackingConfiguration cfg | cfg.hasFlowPath(source, sink)) or
- exists(SymmetricKeyTrackingConfiguration cfg | cfg.hasFlowPath(source, sink))
+where exists(KeySizeConfiguration config1 | config1.hasFlowPath(source, sink))
+//or
+// exists(AsymmetricNonECKeyTrackingConfiguration cfg | cfg.hasFlowPath(source, sink)) or
+// exists(AsymmetricECKeyTrackingConfiguration cfg | cfg.hasFlowPath(source, sink)) or
+// exists(SymmetricKeyTrackingConfiguration cfg | cfg.hasFlowPath(source, sink))
select sink.getNode(), source, sink, "This $@ is too small.", source.getNode(), "key size"
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
index db925935d5c..5dc7ecdf467 100644
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
@@ -11,12 +11,12 @@ class InsufficientKeySizeTest extends InlineExpectationsTest {
override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "hasInsufficientKeySize" and
exists(DataFlow::PathNode source, DataFlow::PathNode sink |
- //exists(KeyTrackingConfiguration config1 | config1.hasFlowPath(source, sink))
- //or
- exists(AsymmetricNonECKeyTrackingConfiguration cfg | cfg.hasFlowPath(source, sink)) or
- exists(AsymmetricECKeyTrackingConfiguration cfg | cfg.hasFlowPath(source, sink)) or
- exists(SymmetricKeyTrackingConfiguration cfg | cfg.hasFlowPath(source, sink))
+ exists(KeySizeConfiguration config1 | config1.hasFlowPath(source, sink))
|
+ //or
+ // exists(AsymmetricNonECKeyTrackingConfiguration cfg | cfg.hasFlowPath(source, sink)) or
+ // exists(AsymmetricECKeyTrackingConfiguration cfg | cfg.hasFlowPath(source, sink)) or
+ // exists(SymmetricKeyTrackingConfiguration cfg | cfg.hasFlowPath(source, sink))
sink.getNode().getLocation() = location and
element = sink.getNode().toString() and
value = ""
From c61f23baae0f7fb427334ce7617407fb3523a31e Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Thu, 13 Oct 2022 23:24:06 -0400
Subject: [PATCH 032/249] experiment with more code condensing
---
.../java/security/InsufficientKeySize.qll | 26 ++++++++++++-------
1 file changed, 17 insertions(+), 9 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
index dde014fe6de..778bc111dd0 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
@@ -3,7 +3,7 @@
private import semmle.code.java.security.Encryption
private import semmle.code.java.dataflow.DataFlow
-// TODO: only update key sizes (and key size strings in one place in the code)
+// TODO: only update key sizes (and key size strings) in one place in the code
/** A source for an insufficient key size. */
abstract class InsufficientKeySizeSource extends DataFlow::Node {
/** Holds if this source has the specified `state`. */
@@ -64,7 +64,7 @@ private class AsymmetricNonECSink extends InsufficientKeySizeSink {
AsymmetricNonECSink() {
hasKeySizeInInitMethod(this, "asymmetric-non-ec")
or
- hasKeySizeInSpec(this, "asymmetric-non-ec")
+ hasKeySizeInSpec(this)
}
override predicate hasState(DataFlow::FlowState state) { state = "2048" }
@@ -83,7 +83,7 @@ private class AsymmetricECSink extends InsufficientKeySizeSink {
AsymmetricECSink() {
hasKeySizeInInitMethod(this, "asymmetric-ec")
or
- hasKeySizeInSpec(this, "asymmetric-ec")
+ hasKeySizeInSpec(this)
}
override predicate hasState(DataFlow::FlowState state) { state = "256" }
@@ -129,20 +129,28 @@ private string getAlgoName(JavaxCryptoAlgoSpec jca) {
// TODO: rethink the predicate name; also think about whether this could/should be a class instead; or a predicate within the sink class so can do sink.predicate()...
// TODO: can prbly re-work way using the typeFlag to be better and less repetitive...
-private predicate hasKeySizeInSpec(DataFlow::Node node, string typeFlag) {
+private predicate hasKeySizeInSpec(DataFlow::Node node) {
exists(ClassInstanceExpr paramSpec |
(
- paramSpec.getConstructedType() instanceof AsymmetricNonECSpec and
- typeFlag = "asymmetric-non-ec"
+ paramSpec.getConstructedType() instanceof AsymmetricNonECSpec //and
or
- paramSpec.getConstructedType() instanceof EcGenParameterSpec and
- typeFlag = "asymmetric-ec"
+ //typeFlag = "asymmetric-non-ec"
+ paramSpec.getConstructedType() instanceof EcGenParameterSpec //and
+ //typeFlag = "asymmetric-ec"
) and
node.asExpr() = paramSpec.getArgument(0)
)
}
-class SpecWithKeySize extends RefType { }
+// ! use below instead of/in above??
+class Spec extends ClassInstanceExpr {
+ Spec() {
+ this.getConstructedType() instanceof AsymmetricNonECSpec or
+ this.getConstructedType() instanceof EcGenParameterSpec
+ }
+
+ Argument getKeySizeArg() { result = this.getArgument(0) }
+}
// TODO:
// todo #0: look into use of specs without keygen objects; should spec not be a sink in these cases?
// todo #3: make list of algo names more easily reusable (either as constant-type variable at top of file, or model as own class to share, etc.)
From 6eb58d832c32d240a4a3ad811b16fd999c571f21 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Fri, 14 Oct 2022 00:47:57 -0400
Subject: [PATCH 033/249] remove dependence on typeFlag
---
.../java/security/InsufficientKeySize.qll | 180 +++++++++++-------
1 file changed, 114 insertions(+), 66 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
index 778bc111dd0..3fccbe412b6 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
@@ -8,6 +8,7 @@ private import semmle.code.java.dataflow.DataFlow
abstract class InsufficientKeySizeSource extends DataFlow::Node {
/** Holds if this source has the specified `state`. */
predicate hasState(DataFlow::FlowState state) { state instanceof DataFlow::FlowStateEmpty }
+ //int getIntValue() { result = this.asExpr().(IntegerLiteral).getIntValue() }
}
/** A sink for an insufficient key size. */
@@ -17,17 +18,17 @@ abstract class InsufficientKeySizeSink extends DataFlow::Node {
}
/** A source for an insufficient key size (asymmetric-non-ec: RSA, DSA, DH). */
-private class AsymmetricNonECSource extends InsufficientKeySizeSource {
- AsymmetricNonECSource() { getNodeIntValue(this) < 2048 }
+private class AsymmetricNonEcSource extends InsufficientKeySizeSource {
+ AsymmetricNonEcSource() { getNodeIntValue(this) < 2048 }
override predicate hasState(DataFlow::FlowState state) { state = "2048" }
}
/** A source for an insufficient key size (asymmetric-ec: EC%). */
-private class AsymmetricECSource extends InsufficientKeySizeSource {
- AsymmetricECSource() {
+private class AsymmetricEcSource extends InsufficientKeySizeSource {
+ AsymmetricEcSource() {
getNodeIntValue(this) < 256 or
- getECKeySize(this.asExpr().(StringLiteral).getValue()) < 256 // for cases when the key size is embedded in the curve name
+ getEcKeySize(this.asExpr().(StringLiteral).getValue()) < 256 // for cases when the key size is embedded in the curve name
}
override predicate hasState(DataFlow::FlowState state) { state = "256" }
@@ -40,7 +41,6 @@ private class SymmetricSource extends InsufficientKeySizeSource {
override predicate hasState(DataFlow::FlowState state) { state = "128" }
}
-// TODO: use `typeFlag` like with sinks below to include the size numbers in this predicate?
private int getNodeIntValue(DataFlow::Node node) {
result = node.asExpr().(IntegerLiteral).getIntValue()
}
@@ -48,7 +48,7 @@ private int getNodeIntValue(DataFlow::Node node) {
// TODO: check if any other regex should be added based on some MRVA results.
/** Returns the key size in the EC algorithm string */
bindingset[algorithm]
-private int getECKeySize(string algorithm) {
+private int getEcKeySize(string algorithm) {
algorithm.matches("sec%") and // specification such as "secp256r1"
result = algorithm.regexpCapture("sec[p|t](\\d+)[a-zA-Z].*", 1).toInt()
or
@@ -60,30 +60,36 @@ private int getECKeySize(string algorithm) {
}
/** A sink for an insufficient key size (asymmetric-non-ec). */
-private class AsymmetricNonECSink extends InsufficientKeySizeSink {
- AsymmetricNonECSink() {
- hasKeySizeInInitMethod(this, "asymmetric-non-ec")
+private class AsymmetricNonEcSink extends InsufficientKeySizeSink {
+ AsymmetricNonEcSink() {
+ // hasKeySizeInInitMethod(this, "asymmetric-non-ec")
+ // or
+ //hasKeySizeInSpec(this, "asymmetric-non-ec")
+ exists(AsymmInitMethodAccess ma, AsymmKeyGen kg |
+ kg.getAlgoName().matches(["RSA", "DSA", "DH"]) and
+ DataFlow::localExprFlow(kg, ma.getQualifier()) and
+ this.asExpr() = ma.getKeySizeArg()
+ )
or
- hasKeySizeInSpec(this)
+ exists(AsymmetricNonEcSpec s | this.asExpr() = s.getKeySizeArg())
}
override predicate hasState(DataFlow::FlowState state) { state = "2048" }
}
-private class AsymmetricNonECSpec extends RefType {
- AsymmetricNonECSpec() {
- this instanceof RsaKeyGenParameterSpec or
- this instanceof DsaGenParameterSpec or
- this instanceof DhGenParameterSpec
- }
-}
-
/** A sink for an insufficient key size (asymmetric-ec). */
-private class AsymmetricECSink extends InsufficientKeySizeSink {
- AsymmetricECSink() {
- hasKeySizeInInitMethod(this, "asymmetric-ec")
+private class AsymmetricEcSink extends InsufficientKeySizeSink {
+ AsymmetricEcSink() {
+ // hasKeySizeInInitMethod(this, "asymmetric-ec")
+ // or
+ //hasKeySizeInSpec(this, "asymmetric-ec")
+ exists(AsymmInitMethodAccess ma, AsymmKeyGen kg |
+ kg.getAlgoName().matches("EC%") and
+ DataFlow::localExprFlow(kg, ma.getQualifier()) and
+ this.asExpr() = ma.getKeySizeArg()
+ )
or
- hasKeySizeInSpec(this)
+ exists(AsymmetricEcSpec s | this.asExpr() = s.getKeySizeArg())
}
override predicate hasState(DataFlow::FlowState state) { state = "256" }
@@ -91,65 +97,107 @@ private class AsymmetricECSink extends InsufficientKeySizeSink {
/** A sink for an insufficient key size (symmetric). */
private class SymmetricSink extends InsufficientKeySizeSink {
- SymmetricSink() { hasKeySizeInInitMethod(this, "symmetric") }
+ SymmetricSink() {
+ //hasKeySizeInInitMethod(this, "symmetric")
+ exists(SymmInitMethodAccess ma, SymmKeyGen kg |
+ kg.getAlgoName() = "AES" and
+ DataFlow::localExprFlow(kg, ma.getQualifier()) and
+ this.asExpr() = ma.getKeySizeArg()
+ )
+ }
override predicate hasState(DataFlow::FlowState state) { state = "128" }
}
// TODO: rethink the predicate name; also think about whether this could/should be a class instead; or a predicate within the sink class so can do sink.predicate()...
// TODO: can prbly re-work way using the typeFlag to be better and less repetitive
-private predicate hasKeySizeInInitMethod(DataFlow::Node node, string typeFlag) {
- exists(MethodAccess ma, JavaxCryptoAlgoSpec jcaSpec |
- (
- ma.getMethod() instanceof KeyGeneratorInitMethod and typeFlag = "symmetric"
- or
- ma.getMethod() instanceof KeyPairGeneratorInitMethod and typeFlag.matches("asymmetric%")
- ) and
- (
- jcaSpec instanceof JavaxCryptoKeyGenerator and typeFlag = "symmetric"
- or
- jcaSpec instanceof JavaSecurityKeyPairGenerator and typeFlag.matches("asymmetric%")
- ) and
- (
- getAlgoName(jcaSpec) = "AES" and typeFlag = "symmetric"
- or
- getAlgoName(jcaSpec).matches(["RSA", "DSA", "DH"]) and typeFlag = "asymmetric-non-ec"
- or
- getAlgoName(jcaSpec).matches("EC%") and typeFlag = "asymmetric-ec"
- ) and
- DataFlow::localExprFlow(jcaSpec, ma.getQualifier()) and
- node.asExpr() = ma.getArgument(0)
- )
+// private predicate hasKeySizeInInitMethod(DataFlow::Node node, string typeFlag) {
+// exists(MethodAccess ma, JavaxCryptoAlgoSpec jcaSpec |
+// (
+// ma.getMethod() instanceof KeyGeneratorInitMethod and typeFlag = "symmetric"
+// or
+// ma.getMethod() instanceof KeyPairGeneratorInitMethod and typeFlag.matches("asymmetric%")
+// ) and
+// (
+// jcaSpec instanceof JavaxCryptoKeyGenerator and typeFlag = "symmetric"
+// or
+// jcaSpec instanceof JavaSecurityKeyPairGenerator and typeFlag.matches("asymmetric%")
+// ) and
+// (
+// getAlgoName(jcaSpec) = "AES" and typeFlag = "symmetric"
+// or
+// getAlgoName(jcaSpec).matches(["RSA", "DSA", "DH"]) and typeFlag = "asymmetric-non-ec"
+// or
+// getAlgoName(jcaSpec).matches("EC%") and typeFlag = "asymmetric-ec"
+// ) and
+// DataFlow::localExprFlow(jcaSpec, ma.getQualifier()) and
+// node.asExpr() = ma.getArgument(0)
+// )
+// }
+// // TODO: this predicate is just a poc for more code condensing; redo this
+// private string getAlgoName(JavaxCryptoAlgoSpec jca) {
+// result = jca.getAlgoSpec().(StringLiteral).getValue().toUpperCase()
+// }
+abstract class InitMethodAccess extends MethodAccess {
+ Argument getKeySizeArg() { result = this.getArgument(0) }
}
-// TODO: this predicate is just a poc for more code condensing; redo this
-private string getAlgoName(JavaxCryptoAlgoSpec jca) {
- result = jca.getAlgoSpec().(StringLiteral).getValue().toUpperCase()
+class AsymmInitMethodAccess extends InitMethodAccess {
+ AsymmInitMethodAccess() { this.getMethod() instanceof KeyPairGeneratorInitMethod }
+}
+
+class SymmInitMethodAccess extends InitMethodAccess {
+ SymmInitMethodAccess() { this.getMethod() instanceof KeyGeneratorInitMethod }
+}
+
+abstract class KeyGen extends JavaxCryptoAlgoSpec {
+ string getAlgoName() { result = this.getAlgoSpec().(StringLiteral).getValue().toUpperCase() }
+}
+
+class AsymmKeyGen extends KeyGen {
+ AsymmKeyGen() { this instanceof JavaSecurityKeyPairGenerator }
+
+ // ! this override is repetitive...
+ override Expr getAlgoSpec() { result = this.(MethodAccess).getArgument(0) }
+}
+
+class SymmKeyGen extends KeyGen {
+ SymmKeyGen() { this instanceof JavaxCryptoKeyGenerator }
+
+ // ! this override is repetitive...
+ override Expr getAlgoSpec() { result = this.(MethodAccess).getArgument(0) }
}
// TODO: rethink the predicate name; also think about whether this could/should be a class instead; or a predicate within the sink class so can do sink.predicate()...
// TODO: can prbly re-work way using the typeFlag to be better and less repetitive...
-private predicate hasKeySizeInSpec(DataFlow::Node node) {
- exists(ClassInstanceExpr paramSpec |
- (
- paramSpec.getConstructedType() instanceof AsymmetricNonECSpec //and
- or
- //typeFlag = "asymmetric-non-ec"
- paramSpec.getConstructedType() instanceof EcGenParameterSpec //and
- //typeFlag = "asymmetric-ec"
- ) and
- node.asExpr() = paramSpec.getArgument(0)
- )
+// private predicate hasKeySizeInSpec(DataFlow::Node node, string typeFlag) {
+// exists(ClassInstanceExpr paramSpec |
+// (
+// paramSpec.getConstructedType() instanceof AsymmetricNonEcSpec and
+// typeFlag = "asymmetric-non-ec"
+// or
+// paramSpec.getConstructedType() instanceof EcGenParameterSpec and
+// typeFlag = "asymmetric-ec"
+// ) and
+// node.asExpr() = paramSpec.getArgument(0)
+// )
+// }
+// ! use below instead of/in above?? (actually I don't think I need any of this, can just use AsymmetricNonEcSpec and EcGenParameterSpec directly???)
+// Algo spec
+abstract class AsymmetricAlgoSpec extends ClassInstanceExpr {
+ Argument getKeySizeArg() { result = this.getArgument(0) }
}
-// ! use below instead of/in above??
-class Spec extends ClassInstanceExpr {
- Spec() {
- this.getConstructedType() instanceof AsymmetricNonECSpec or
- this.getConstructedType() instanceof EcGenParameterSpec
+class AsymmetricNonEcSpec extends AsymmetricAlgoSpec {
+ AsymmetricNonEcSpec() {
+ this.getConstructedType() instanceof RsaKeyGenParameterSpec or
+ this.getConstructedType() instanceof DsaGenParameterSpec or
+ this.getConstructedType() instanceof DhGenParameterSpec
}
+}
- Argument getKeySizeArg() { result = this.getArgument(0) }
+class AsymmetricEcSpec extends AsymmetricAlgoSpec {
+ AsymmetricEcSpec() { this.getConstructedType() instanceof EcGenParameterSpec }
}
// TODO:
// todo #0: look into use of specs without keygen objects; should spec not be a sink in these cases?
From 47030df8ac33c41b6bc235d72aeb440c38a35cbf Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Fri, 14 Oct 2022 09:26:44 -0400
Subject: [PATCH 034/249] remove commented-out 3 configs
---
.../security/InsufficientKeySizeQuery.qll | 26 -------------------
1 file changed, 26 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
index 08388c345d6..6036071111e 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
@@ -14,35 +14,9 @@ class KeySizeConfiguration extends DataFlow::Configuration {
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
source.(InsufficientKeySizeSource).hasState(state)
- //source instanceof InsufficientKeySizeSource
}
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
sink.(InsufficientKeySizeSink).hasState(state)
- //sink instanceof InsufficientKeySizeSink
}
}
-// /**
-// * A data flow configuration for tracking non-elliptic curve asymmetric algorithm
-// * (RSA, DSA, and DH) key sizes.
-// */
-// class AsymmetricNonECKeyTrackingConfiguration extends DataFlow::Configuration {
-// AsymmetricNonECKeyTrackingConfiguration() { this = "AsymmetricNonECKeyTrackingConfiguration" }
-// override predicate isSource(DataFlow::Node source) { source instanceof AsymmetricNonECSource }
-// override predicate isSink(DataFlow::Node sink) { sink instanceof AsymmetricNonECSink }
-// }
-// /**
-// * A data flow configuration for tracking elliptic curve (EC) asymmetric
-// * algorithm key sizes.
-// */
-// class AsymmetricECKeyTrackingConfiguration extends DataFlow::Configuration {
-// AsymmetricECKeyTrackingConfiguration() { this = "AsymmetricECKeyTrackingConfiguration" }
-// override predicate isSource(DataFlow::Node source) { source instanceof AsymmetricECSource }
-// override predicate isSink(DataFlow::Node sink) { sink instanceof AsymmetricECSink }
-// }
-// /** A data flow configuration for tracking symmetric algorithm (AES) key sizes. */
-// class SymmetricKeyTrackingConfiguration extends DataFlow::Configuration {
-// SymmetricKeyTrackingConfiguration() { this = "SymmetricKeyTrackingConfiguration" }
-// override predicate isSource(DataFlow::Node source) { source instanceof SymmetricSource }
-// override predicate isSink(DataFlow::Node sink) { sink instanceof SymmetricSink }
-// }
From 0334470f33a2623773cc6118048791a52325a6af Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Fri, 14 Oct 2022 10:55:30 -0400
Subject: [PATCH 035/249] remove commented out predicates that relied on
typeFlag
---
.../java/security/InsufficientKeySize.qll | 47 +------------------
.../security/InsufficientKeySizeQuery.qll | 5 +-
2 files changed, 3 insertions(+), 49 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
index 3fccbe412b6..b34a9e0da21 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
@@ -3,12 +3,10 @@
private import semmle.code.java.security.Encryption
private import semmle.code.java.dataflow.DataFlow
-// TODO: only update key sizes (and key size strings) in one place in the code
/** A source for an insufficient key size. */
abstract class InsufficientKeySizeSource extends DataFlow::Node {
/** Holds if this source has the specified `state`. */
predicate hasState(DataFlow::FlowState state) { state instanceof DataFlow::FlowStateEmpty }
- //int getIntValue() { result = this.asExpr().(IntegerLiteral).getIntValue() }
}
/** A sink for an insufficient key size. */
@@ -109,35 +107,6 @@ private class SymmetricSink extends InsufficientKeySizeSink {
override predicate hasState(DataFlow::FlowState state) { state = "128" }
}
-// TODO: rethink the predicate name; also think about whether this could/should be a class instead; or a predicate within the sink class so can do sink.predicate()...
-// TODO: can prbly re-work way using the typeFlag to be better and less repetitive
-// private predicate hasKeySizeInInitMethod(DataFlow::Node node, string typeFlag) {
-// exists(MethodAccess ma, JavaxCryptoAlgoSpec jcaSpec |
-// (
-// ma.getMethod() instanceof KeyGeneratorInitMethod and typeFlag = "symmetric"
-// or
-// ma.getMethod() instanceof KeyPairGeneratorInitMethod and typeFlag.matches("asymmetric%")
-// ) and
-// (
-// jcaSpec instanceof JavaxCryptoKeyGenerator and typeFlag = "symmetric"
-// or
-// jcaSpec instanceof JavaSecurityKeyPairGenerator and typeFlag.matches("asymmetric%")
-// ) and
-// (
-// getAlgoName(jcaSpec) = "AES" and typeFlag = "symmetric"
-// or
-// getAlgoName(jcaSpec).matches(["RSA", "DSA", "DH"]) and typeFlag = "asymmetric-non-ec"
-// or
-// getAlgoName(jcaSpec).matches("EC%") and typeFlag = "asymmetric-ec"
-// ) and
-// DataFlow::localExprFlow(jcaSpec, ma.getQualifier()) and
-// node.asExpr() = ma.getArgument(0)
-// )
-// }
-// // TODO: this predicate is just a poc for more code condensing; redo this
-// private string getAlgoName(JavaxCryptoAlgoSpec jca) {
-// result = jca.getAlgoSpec().(StringLiteral).getValue().toUpperCase()
-// }
abstract class InitMethodAccess extends MethodAccess {
Argument getKeySizeArg() { result = this.getArgument(0) }
}
@@ -168,20 +137,6 @@ class SymmKeyGen extends KeyGen {
override Expr getAlgoSpec() { result = this.(MethodAccess).getArgument(0) }
}
-// TODO: rethink the predicate name; also think about whether this could/should be a class instead; or a predicate within the sink class so can do sink.predicate()...
-// TODO: can prbly re-work way using the typeFlag to be better and less repetitive...
-// private predicate hasKeySizeInSpec(DataFlow::Node node, string typeFlag) {
-// exists(ClassInstanceExpr paramSpec |
-// (
-// paramSpec.getConstructedType() instanceof AsymmetricNonEcSpec and
-// typeFlag = "asymmetric-non-ec"
-// or
-// paramSpec.getConstructedType() instanceof EcGenParameterSpec and
-// typeFlag = "asymmetric-ec"
-// ) and
-// node.asExpr() = paramSpec.getArgument(0)
-// )
-// }
// ! use below instead of/in above?? (actually I don't think I need any of this, can just use AsymmetricNonEcSpec and EcGenParameterSpec directly???)
// Algo spec
abstract class AsymmetricAlgoSpec extends ClassInstanceExpr {
@@ -202,3 +157,5 @@ class AsymmetricEcSpec extends AsymmetricAlgoSpec {
// TODO:
// todo #0: look into use of specs without keygen objects; should spec not be a sink in these cases?
// todo #3: make list of algo names more easily reusable (either as constant-type variable at top of file, or model as own class to share, etc.)
+// todo: add barrier guard for !=0 conditional case
+// todo: only update key sizes (and key size strings) in one place in the code
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
index 6036071111e..eb8e5441747 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
@@ -5,10 +5,7 @@ import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.security.InsufficientKeySize
-/**
- * A data flow configuration for tracking non-elliptic curve asymmetric algorithm
- * (RSA, DSA, and DH) key sizes.
- */
+/** A data flow configuration for tracking key sizes used in cryptographic algorithms. */
class KeySizeConfiguration extends DataFlow::Configuration {
KeySizeConfiguration() { this = "KeySizeConfiguration" }
From da218fdbf1adf1a96ff43c33bdd13a9831fdbd50 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Fri, 14 Oct 2022 13:03:34 -0400
Subject: [PATCH 036/249] clean up code
---
.../semmle/code/java/security/Encryption.qll | 14 ++-
.../java/security/InsufficientKeySize.qll | 85 ++++++++++---------
.../security/InsufficientKeySizeQuery.qll | 2 -
.../CWE/CWE-326/InsufficientKeySize.ql | 9 +-
4 files changed, 51 insertions(+), 59 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/Encryption.qll b/java/ql/lib/semmle/code/java/security/Encryption.qll
index a1535a438b5..adb2dc74691 100644
--- a/java/ql/lib/semmle/code/java/security/Encryption.qll
+++ b/java/ql/lib/semmle/code/java/security/Encryption.qll
@@ -83,6 +83,11 @@ class KeyGenerator extends RefType {
KeyGenerator() { this.hasQualifiedName("javax.crypto", "KeyGenerator") }
}
+/** The Java class `java.security.KeyPairGenerator`. */
+class KeyPairGenerator extends RefType {
+ KeyPairGenerator() { this.hasQualifiedName("java.security", "KeyPairGenerator") }
+}
+
/** The `init` method declared in `javax.crypto.KeyGenerator`. */
class KeyGeneratorInitMethod extends Method {
KeyGeneratorInitMethod() {
@@ -91,11 +96,6 @@ class KeyGeneratorInitMethod extends Method {
}
}
-/** The Java class `java.security.KeyPairGenerator`. */
-class KeyPairGenerator extends RefType {
- KeyPairGenerator() { this.hasQualifiedName("java.security", "KeyPairGenerator") }
-}
-
/** The `initialize` method declared in `java.security.KeyPairGenerator`. */
class KeyPairGeneratorInitMethod extends Method {
KeyPairGeneratorInitMethod() {
@@ -323,7 +323,6 @@ class JavaxCryptoSecretKey extends JavaxCryptoAlgoSpec {
}
}
-// TODO: consider extending JavaxCryptoAlgoSpec as above does; will need to override getAlgoSpec() method
/** The Java class `javax.crypto.spec.DHGenParameterSpec`. */
class DhGenParameterSpec extends RefType {
DhGenParameterSpec() { this.hasQualifiedName("javax.crypto.spec", "DHGenParameterSpec") }
@@ -389,19 +388,16 @@ class JavaSecuritySignature extends JavaSecurityAlgoSpec {
override Expr getAlgoSpec() { result = this.(ConstructorCall).getArgument(0) }
}
-// TODO: consider extending JavaSecurityAlgoSpec as above does; will need to override getAlgoSpec() method
/** The Java class `java.security.spec.ECGenParameterSpec`. */
class EcGenParameterSpec extends RefType {
EcGenParameterSpec() { this.hasQualifiedName("java.security.spec", "ECGenParameterSpec") }
}
-// TODO: consider extending JavaSecurityAlgoSpec as above does; will need to override getAlgoSpec() method
/** The Java class `java.security.spec.RSAKeyGenParameterSpec`. */
class RsaKeyGenParameterSpec extends RefType {
RsaKeyGenParameterSpec() { this.hasQualifiedName("java.security.spec", "RSAKeyGenParameterSpec") }
}
-// TODO: consider extending JavaSecurityAlgoSpec as above does; will need to override getAlgoSpec() method
/** The Java class `java.security.spec.DSAGenParameterSpec`. */
class DsaGenParameterSpec extends RefType {
DsaGenParameterSpec() { this.hasQualifiedName("java.security.spec", "DSAGenParameterSpec") }
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
index b34a9e0da21..16f960f1b6d 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
@@ -15,36 +15,40 @@ abstract class InsufficientKeySizeSink extends DataFlow::Node {
predicate hasState(DataFlow::FlowState state) { state instanceof DataFlow::FlowStateEmpty }
}
-/** A source for an insufficient key size (asymmetric-non-ec: RSA, DSA, DH). */
+// *********************************** SOURCES ***********************************
+/** A source for an insufficient key size used in RSA, DSA, and DH algorithms. */
private class AsymmetricNonEcSource extends InsufficientKeySizeSource {
AsymmetricNonEcSource() { getNodeIntValue(this) < 2048 }
override predicate hasState(DataFlow::FlowState state) { state = "2048" }
}
-/** A source for an insufficient key size (asymmetric-ec: EC%). */
+/** A source for an insufficient key size used in elliptic curve (EC) algorithms. */
private class AsymmetricEcSource extends InsufficientKeySizeSource {
AsymmetricEcSource() {
- getNodeIntValue(this) < 256 or
- getEcKeySize(this.asExpr().(StringLiteral).getValue()) < 256 // for cases when the key size is embedded in the curve name
+ getNodeIntValue(this) < 256
+ or
+ // the below is needed for cases when the key size is embedded in the curve name
+ getEcKeySize(this.asExpr().(StringLiteral).getValue()) < 256
}
override predicate hasState(DataFlow::FlowState state) { state = "256" }
}
-/** A source for an insufficient key size (symmetric: AES). */
+/** A source for an insufficient key size used in AES algorithms. */
private class SymmetricSource extends InsufficientKeySizeSource {
SymmetricSource() { getNodeIntValue(this) < 128 }
override predicate hasState(DataFlow::FlowState state) { state = "128" }
}
+// ************************** SOURCES HELPER PREDICATES **************************
+/** Returns the integer value of a given Node. */
private int getNodeIntValue(DataFlow::Node node) {
result = node.asExpr().(IntegerLiteral).getIntValue()
}
-// TODO: check if any other regex should be added based on some MRVA results.
-/** Returns the key size in the EC algorithm string */
+/** Returns the key size from an EC algorithm curve name string */
bindingset[algorithm]
private int getEcKeySize(string algorithm) {
algorithm.matches("sec%") and // specification such as "secp256r1"
@@ -57,31 +61,26 @@ private int getEcKeySize(string algorithm) {
result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
}
-/** A sink for an insufficient key size (asymmetric-non-ec). */
+// ************************************ SINKS ************************************
+/** A sink for an insufficient key size used in RSA, DSA, and DH algorithms. */
private class AsymmetricNonEcSink extends InsufficientKeySizeSink {
AsymmetricNonEcSink() {
- // hasKeySizeInInitMethod(this, "asymmetric-non-ec")
- // or
- //hasKeySizeInSpec(this, "asymmetric-non-ec")
- exists(AsymmInitMethodAccess ma, AsymmKeyGen kg |
+ exists(AsymmetricInitMethodAccess ma, AsymmetricKeyGenerator kg |
kg.getAlgoName().matches(["RSA", "DSA", "DH"]) and
DataFlow::localExprFlow(kg, ma.getQualifier()) and
this.asExpr() = ma.getKeySizeArg()
)
or
- exists(AsymmetricNonEcSpec s | this.asExpr() = s.getKeySizeArg())
+ exists(AsymmetricNonEcSpec spec | this.asExpr() = spec.getKeySizeArg())
}
override predicate hasState(DataFlow::FlowState state) { state = "2048" }
}
-/** A sink for an insufficient key size (asymmetric-ec). */
+/** A sink for an insufficient key size used in elliptic curve (EC) algorithms. */
private class AsymmetricEcSink extends InsufficientKeySizeSink {
AsymmetricEcSink() {
- // hasKeySizeInInitMethod(this, "asymmetric-ec")
- // or
- //hasKeySizeInSpec(this, "asymmetric-ec")
- exists(AsymmInitMethodAccess ma, AsymmKeyGen kg |
+ exists(AsymmetricInitMethodAccess ma, AsymmetricKeyGenerator kg |
kg.getAlgoName().matches("EC%") and
DataFlow::localExprFlow(kg, ma.getQualifier()) and
this.asExpr() = ma.getKeySizeArg()
@@ -93,11 +92,11 @@ private class AsymmetricEcSink extends InsufficientKeySizeSink {
override predicate hasState(DataFlow::FlowState state) { state = "256" }
}
-/** A sink for an insufficient key size (symmetric). */
+/** A sink for an insufficient key size used in AES algorithms. */
private class SymmetricSink extends InsufficientKeySizeSink {
SymmetricSink() {
//hasKeySizeInInitMethod(this, "symmetric")
- exists(SymmInitMethodAccess ma, SymmKeyGen kg |
+ exists(SymmetricInitMethodAccess ma, SymmetricKeyGenerator kg |
kg.getAlgoName() = "AES" and
DataFlow::localExprFlow(kg, ma.getQualifier()) and
this.asExpr() = ma.getKeySizeArg()
@@ -107,43 +106,49 @@ private class SymmetricSink extends InsufficientKeySizeSink {
override predicate hasState(DataFlow::FlowState state) { state = "128" }
}
-abstract class InitMethodAccess extends MethodAccess {
+// ********************** SINKS HELPER CLASSES & PREDICATES **********************
+/** A call to a method that initializes a key generator. */
+abstract class KeyGenInitMethodAccess extends MethodAccess {
+ /** Gets the `keysize` argument of this call. */
Argument getKeySizeArg() { result = this.getArgument(0) }
}
-class AsymmInitMethodAccess extends InitMethodAccess {
- AsymmInitMethodAccess() { this.getMethod() instanceof KeyPairGeneratorInitMethod }
+/** A call to the `initialize` method declared in `java.security.KeyPairGenerator`. */
+private class AsymmetricInitMethodAccess extends KeyGenInitMethodAccess {
+ AsymmetricInitMethodAccess() { this.getMethod() instanceof KeyPairGeneratorInitMethod }
}
-class SymmInitMethodAccess extends InitMethodAccess {
- SymmInitMethodAccess() { this.getMethod() instanceof KeyGeneratorInitMethod }
+/** A call to the `init` method declared in `javax.crypto.KeyGenerator`. */
+private class SymmetricInitMethodAccess extends KeyGenInitMethodAccess {
+ SymmetricInitMethodAccess() { this.getMethod() instanceof KeyGeneratorInitMethod }
}
-abstract class KeyGen extends JavaxCryptoAlgoSpec {
+/** An instance of a key generator. */
+abstract class KeyGeneratorObject extends JavaxCryptoAlgoSpec {
string getAlgoName() { result = this.getAlgoSpec().(StringLiteral).getValue().toUpperCase() }
}
-class AsymmKeyGen extends KeyGen {
- AsymmKeyGen() { this instanceof JavaSecurityKeyPairGenerator }
+/** An instance of a `java.security.KeyPairGenerator`. */
+private class AsymmetricKeyGenerator extends KeyGeneratorObject {
+ AsymmetricKeyGenerator() { this instanceof JavaSecurityKeyPairGenerator }
- // ! this override is repetitive...
override Expr getAlgoSpec() { result = this.(MethodAccess).getArgument(0) }
}
-class SymmKeyGen extends KeyGen {
- SymmKeyGen() { this instanceof JavaxCryptoKeyGenerator }
+/** An instance of a `javax.crypto.KeyGenerator`. */
+private class SymmetricKeyGenerator extends KeyGeneratorObject {
+ SymmetricKeyGenerator() { this instanceof JavaxCryptoKeyGenerator }
- // ! this override is repetitive...
override Expr getAlgoSpec() { result = this.(MethodAccess).getArgument(0) }
}
-// ! use below instead of/in above?? (actually I don't think I need any of this, can just use AsymmetricNonEcSpec and EcGenParameterSpec directly???)
-// Algo spec
-abstract class AsymmetricAlgoSpec extends ClassInstanceExpr {
+/** An instance of an algorithm specification. */
+abstract class AlgoSpec extends ClassInstanceExpr {
Argument getKeySizeArg() { result = this.getArgument(0) }
}
-class AsymmetricNonEcSpec extends AsymmetricAlgoSpec {
+/** An instance of an RSA, DSA, or DH algorithm specification. */
+private class AsymmetricNonEcSpec extends AlgoSpec {
AsymmetricNonEcSpec() {
this.getConstructedType() instanceof RsaKeyGenParameterSpec or
this.getConstructedType() instanceof DsaGenParameterSpec or
@@ -151,11 +156,7 @@ class AsymmetricNonEcSpec extends AsymmetricAlgoSpec {
}
}
-class AsymmetricEcSpec extends AsymmetricAlgoSpec {
+/** An instance of an elliptic curve (EC) algorithm specification. */
+private class AsymmetricEcSpec extends AlgoSpec {
AsymmetricEcSpec() { this.getConstructedType() instanceof EcGenParameterSpec }
}
-// TODO:
-// todo #0: look into use of specs without keygen objects; should spec not be a sink in these cases?
-// todo #3: make list of algo names more easily reusable (either as constant-type variable at top of file, or model as own class to share, etc.)
-// todo: add barrier guard for !=0 conditional case
-// todo: only update key sizes (and key size strings) in one place in the code
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
index eb8e5441747..eb416d9647b 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySizeQuery.qll
@@ -1,8 +1,6 @@
/** Provides data flow configurations to be used in queries related to insufficient key sizes. */
-import semmle.code.java.security.Encryption
import semmle.code.java.dataflow.DataFlow
-import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.security.InsufficientKeySize
/** A data flow configuration for tracking key sizes used in cryptographic algorithms. */
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
index 8f0f54c7d9b..b82104db79a 100644
--- a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
+++ b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
@@ -16,9 +16,6 @@ import semmle.code.java.security.InsufficientKeySizeQuery
import DataFlow::PathGraph
from DataFlow::PathNode source, DataFlow::PathNode sink
-where exists(KeySizeConfiguration config1 | config1.hasFlowPath(source, sink))
-//or
-// exists(AsymmetricNonECKeyTrackingConfiguration cfg | cfg.hasFlowPath(source, sink)) or
-// exists(AsymmetricECKeyTrackingConfiguration cfg | cfg.hasFlowPath(source, sink)) or
-// exists(SymmetricKeyTrackingConfiguration cfg | cfg.hasFlowPath(source, sink))
-select sink.getNode(), source, sink, "This $@ is too small.", source.getNode(), "key size"
+where exists(KeySizeConfiguration cfg | cfg.hasFlowPath(source, sink))
+select sink.getNode(), source, sink, "This $@ is less than the recommended key size.",
+ source.getNode(), "key size"
From 2714c7fdcf327cb26c5cae65a35431e00bb4f3bf Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Fri, 14 Oct 2022 16:45:13 -0400
Subject: [PATCH 037/249] update tests
---
.../CWE-326/InsufficientKeySizeTest.java | 263 +++++++-----------
.../CWE-326/InsufficientKeySizeTest.ql | 7 +-
2 files changed, 109 insertions(+), 161 deletions(-)
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
index e6a61d7bb4c..3e6a1649ceb 100644
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
@@ -10,244 +10,197 @@ import javax.crypto.spec.DHGenParameterSpec;
public class InsufficientKeySizeTest {
public void keySizeTesting() throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
- // Test basic key generation for all algos
-
- // AES (Symmetric)
+ /* AES (Symmetric): minimum recommended key size is 128 */
{
- // BAD: Key size is less than 128
+ /* Test with keysize as int */
KeyGenerator keyGen1 = KeyGenerator.getInstance("AES");
keyGen1.init(64); // $ hasInsufficientKeySize
- // GOOD: Key size is no less than 128
KeyGenerator keyGen2 = KeyGenerator.getInstance("AES");
- keyGen2.init(128); // Safe
+ keyGen2.init(128); // Safe: Key size is no less than 128
+
+ /* Test with local variable as keysize */
+ final int size1 = 64; // compile-time constant
+ int size2 = 64; // not a compile-time constant
+
+ KeyGenerator keyGen3 = KeyGenerator.getInstance("AES");
+ keyGen3.init(size1); // $ hasInsufficientKeySize
+
+ KeyGenerator keyGen4 = KeyGenerator.getInstance("AES");
+ keyGen4.init(size2); // $ hasInsufficientKeySize
+
+ /* Test variables passed to another method */
+ KeyGenerator keyGen = KeyGenerator.getInstance("AES"); // MISSING: test KeyGenerator variable as argument
+ testSymmetricVariable(size2, keyGen); // test with variable as key size
+ testSymmetricInt(64); // test with int literal as key size
}
- // RSA (Asymmetric)
+ // RSA (Asymmetric): minimum recommended key size is 2048
{
- // BAD: Key size is less than 2048
+ /* Test with keysize as int */
KeyPairGenerator keyPairGen1 = KeyPairGenerator.getInstance("RSA");
keyPairGen1.initialize(1024); // $ hasInsufficientKeySize
- // GOOD: Key size is no less than 2048
KeyPairGenerator keyPairGen2 = KeyPairGenerator.getInstance("RSA");
- keyPairGen2.initialize(2048); // Safe
+ keyPairGen2.initialize(2048); // Safe: Key size is no less than 2048
- // test with spec
- // BAD: Key size is less than 2048
KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("RSA");
RSAKeyGenParameterSpec rsaSpec = new RSAKeyGenParameterSpec(1024, null); // $ hasInsufficientKeySize
keyPairGen3.initialize(rsaSpec);
- // BAD: Key size is less than 2048
KeyPairGenerator keyPairGen4 = KeyPairGenerator.getInstance("RSA");
keyPairGen4.initialize(new RSAKeyGenParameterSpec(1024, null)); // $ hasInsufficientKeySize
+
+ /* Test with local variable as keysize */
+ final int size1 = 1024; // compile-time constant
+ int size2 = 1024; // not a compile-time constant
+
+ KeyPairGenerator keyPairGen5 = KeyPairGenerator.getInstance("RSA");
+ keyPairGen5.initialize(size1); // $ hasInsufficientKeySize
+
+ KeyPairGenerator keyPairGen6 = KeyPairGenerator.getInstance("RSA");
+ keyPairGen6.initialize(size2); // $ hasInsufficientKeySize
+
+ /* Test variables passed to another method */
+ KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA"); // MISSING: test KeyGenerator variable as argument
+ testAsymmetricNonEcVariable(size2, keyPairGen); // test with variable as key size
+ testAsymmetricNonEcInt(1024); // test with int literal as key size
}
- // DSA (Asymmetric)
+ // DSA (Asymmetric): minimum recommended key size is 2048
{
- // BAD: Key size is less than 2048
+ /* Test with keysize as int */
+ KeyPairGenerator keyPairGen1 = KeyPairGenerator.getInstance("DSA");
+ keyPairGen1.initialize(1024); // $ hasInsufficientKeySize
+
+ KeyPairGenerator keyPairGen2 = KeyPairGenerator.getInstance("DSA");
+ keyPairGen2.initialize(2048); // Safe: Key size is no less than 2048
+
KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("DSA");
- keyPairGen3.initialize(1024); // $ hasInsufficientKeySize
-
- // GOOD: Key size is no less than 2048
- KeyPairGenerator keyPairGen4 = KeyPairGenerator.getInstance("DSA");
- keyPairGen4.initialize(2048); // Safe
-
- // test with spec
- // BAD: Key size is less than 2048
- KeyPairGenerator keyPairGen5 = KeyPairGenerator.getInstance("DSA");
DSAGenParameterSpec dsaSpec = new DSAGenParameterSpec(1024, 0); // $ hasInsufficientKeySize
- keyPairGen5.initialize(dsaSpec);
+ keyPairGen3.initialize(dsaSpec);
- // BAD: Key size is less than 2048
- KeyPairGenerator keyPairGen6 = KeyPairGenerator.getInstance("DSA");
- keyPairGen6.initialize(new DSAGenParameterSpec(1024, 0)); // $ hasInsufficientKeySize
+ KeyPairGenerator keyPairGen4 = KeyPairGenerator.getInstance("DSA");
+ keyPairGen4.initialize(new DSAGenParameterSpec(1024, 0)); // $ hasInsufficientKeySize
}
- // DH (Asymmetric)
+ // DH (Asymmetric): minimum recommended key size is 2048
{
- // BAD: Key size is less than 2048
- KeyPairGenerator keyPairGen16 = KeyPairGenerator.getInstance("dh");
- keyPairGen16.initialize(1024); // $ hasInsufficientKeySize
+ /* Test with keysize as int */
+ KeyPairGenerator keyPairGen1 = KeyPairGenerator.getInstance("dh");
+ keyPairGen1.initialize(1024); // $ hasInsufficientKeySize
- // GOOD: Key size is no less than 2048
- KeyPairGenerator keyPairGen17 = KeyPairGenerator.getInstance("DH");
- keyPairGen17.initialize(2048); // Safe
+ KeyPairGenerator keyPairGen2 = KeyPairGenerator.getInstance("DH");
+ keyPairGen2.initialize(2048); // Safe: Key size is no less than 2048
- // test with spec
- // BAD: Key size is less than 2048
KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("DH");
DHGenParameterSpec dhSpec = new DHGenParameterSpec(1024, 0); // $ hasInsufficientKeySize
keyPairGen3.initialize(dhSpec);
- // BAD: Key size is less than 2048
KeyPairGenerator keyPairGen4 = KeyPairGenerator.getInstance("DH");
keyPairGen4.initialize(new DHGenParameterSpec(1024, 0)); // $ hasInsufficientKeySize
}
- // EC (Asymmetric)
- // ! Check if I can re-use the same KeyPairGenerator instance with all of the below?
+ // EC (Asymmetric): minimum recommended key size is 256
{
- // BAD: Key size is less than 256
- KeyPairGenerator keyPairGen5 = KeyPairGenerator.getInstance("EC");
+ /* Test with keysize as int */
+ KeyPairGenerator keyPairGen1 = KeyPairGenerator.getInstance("EC");
+ keyPairGen1.initialize(128); // $ hasInsufficientKeySize
+
+ /* Test with keysize as curve name in spec */
+ KeyPairGenerator keyPairGen2 = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec ecSpec1 = new ECGenParameterSpec("secp112r1"); // $ hasInsufficientKeySize
- keyPairGen5.initialize(ecSpec1);
+ keyPairGen2.initialize(ecSpec1);
- // BAD: Key size is less than 256
- KeyPairGenerator keyPairGen6 = KeyPairGenerator.getInstance("EC");
- keyPairGen6.initialize(new ECGenParameterSpec("secp112r1")); // $ hasInsufficientKeySize
+ KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("EC");
+ keyPairGen3.initialize(new ECGenParameterSpec("secp112r1")); // $ hasInsufficientKeySize
- // GOOD: Key size is no less than 256
- KeyPairGenerator keyPairGen7 = KeyPairGenerator.getInstance("EC");
- ECGenParameterSpec ecSpec2 = new ECGenParameterSpec("secp256r1");
- keyPairGen7.initialize(ecSpec2); // Safe
+ KeyPairGenerator keyPairGen4 = KeyPairGenerator.getInstance("EC");
+ ECGenParameterSpec ecSpec2 = new ECGenParameterSpec("secp256r1"); // Safe: Key size is no less than 256
+ keyPairGen4.initialize(ecSpec2);
- // BAD: Key size is less than 256
- KeyPairGenerator keyPairGen8 = KeyPairGenerator.getInstance("EC");
+ KeyPairGenerator keyPairGen5 = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec ecSpec3 = new ECGenParameterSpec("X9.62 prime192v2"); // $ hasInsufficientKeySize
- keyPairGen8.initialize(ecSpec3);
+ keyPairGen5.initialize(ecSpec3);
- // BAD: Key size is less than 256
- KeyPairGenerator keyPairGen9 = KeyPairGenerator.getInstance("EC");
+ KeyPairGenerator keyPairGen6 = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec ecSpec4 = new ECGenParameterSpec("X9.62 c2tnb191v3"); // $ hasInsufficientKeySize
- keyPairGen9.initialize(ecSpec4);
+ keyPairGen6.initialize(ecSpec4);
- // BAD: Key size is less than 256
- KeyPairGenerator keyPairGen10 = KeyPairGenerator.getInstance("EC");
+ KeyPairGenerator keyPairGen7 = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec ecSpec5 = new ECGenParameterSpec("sect163k1"); // $ hasInsufficientKeySize
- keyPairGen10.initialize(ecSpec5);
+ keyPairGen7.initialize(ecSpec5);
- // GOOD: Key size is no less than 256
- KeyPairGenerator keyPairGen11 = KeyPairGenerator.getInstance("EC");
- ECGenParameterSpec ecSpec6 = new ECGenParameterSpec("X9.62 c2tnb359v1");
- keyPairGen11.initialize(ecSpec6); // Safe
+ KeyPairGenerator keyPairGen8 = KeyPairGenerator.getInstance("EC");
+ ECGenParameterSpec ecSpec6 = new ECGenParameterSpec("X9.62 c2tnb359v1"); // Safe: Key size is no less than 256
+ keyPairGen8.initialize(ecSpec6);
- // BAD: Key size is less than 256
- KeyPairGenerator keyPairGen12 = KeyPairGenerator.getInstance("EC");
+ KeyPairGenerator keyPairGen9 = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec ecSpec7 = new ECGenParameterSpec("prime192v2"); // $ hasInsufficientKeySize
- keyPairGen12.initialize(ecSpec7);
+ keyPairGen9.initialize(ecSpec7);
- // GOOD: Key size is no less than 256
- KeyPairGenerator keyPairGen13 = KeyPairGenerator.getInstance("EC");
- ECGenParameterSpec ecSpec8 = new ECGenParameterSpec("prime256v1");
- keyPairGen13.initialize(ecSpec8); // Safe
+ KeyPairGenerator keyPairGen10 = KeyPairGenerator.getInstance("EC");
+ ECGenParameterSpec ecSpec8 = new ECGenParameterSpec("prime256v1"); // Safe: Key size is no less than 256
+ keyPairGen10.initialize(ecSpec8);
- // BAD: Key size is less than 256
KeyPairGenerator keyPairGen14 = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec ecSpec9 = new ECGenParameterSpec("c2tnb191v1"); // $ hasInsufficientKeySize
keyPairGen14.initialize(ecSpec9);
- // GOOD: Key size is no less than 256
KeyPairGenerator keyPairGen15 = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec ecSpec10 = new ECGenParameterSpec("c2tnb431r1");
- keyPairGen15.initialize(ecSpec10); // Safe
+ keyPairGen15.initialize(ecSpec10); // Safe: Key size is no less than 256
+
+ /* Test variables passed to another method */
+ ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp112r1"); // $ hasInsufficientKeySize
+ KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("EC"); // MISSING: test KeyGenerator variable as argument
+ testAsymmetricEC(ecSpec, keyPairGen); // test spec as an argument
}
-
- // ! FN Testing Additions:
-
- // Test local variable usage - Symmetric
- {
- final int size1 = 64; // compile-time constant
- int size2 = 64; // NOT a compile-time constant
-
- // BAD: Key size is less than 128
- KeyGenerator keyGen3 = KeyGenerator.getInstance("AES");
- keyGen3.init(size1); // $ hasInsufficientKeySize
-
- // BAD: Key size is less than 128
- KeyGenerator keyGen4 = KeyGenerator.getInstance("AES");
- keyGen4.init(size2); // $ hasInsufficientKeySize
- }
-
- // Test local variable usage - Asymmetric, Not EC
- {
- final int size1 = 1024; // compile-time constant
- int size2 = 1024; // NOT a compile-time constant
-
- // BAD: Key size is less than 2048
- KeyPairGenerator keyPairGen18 = KeyPairGenerator.getInstance("RSA");
- keyPairGen18.initialize(size1); // $ hasInsufficientKeySize
-
- // BAD: Key size is less than 2048
- KeyPairGenerator keyPairGen19 = KeyPairGenerator.getInstance("RSA");
- keyPairGen19.initialize(size2); // $ hasInsufficientKeySize
- }
-
-
- // Test variable passed to other method(s) - Symmetric
- {
- int size = 64; // test integer variable
- KeyGenerator keyGen = KeyGenerator.getInstance("AES"); // test KeyGenerator variable
- testSymmetric(size, keyGen); // test with variable as key size
- testSymmetric2(64); // test with int literal as key size
- }
-
-
- // Test variables passed to other method(s) - Asymmetric, Not EC
- {
- int size = 1024; // test integer variable
- KeyPairGenerator keyPairGen21 = KeyPairGenerator.getInstance("RSA"); // test KeyPairGenerator variable
- testAsymmetricNonEC(size, keyPairGen21); // test with variable as key size
- testAsymmetricNonEC2(1024); // test with int literal as key size
- }
-
- // Test variable passed to other method(s) - Asymmetric, EC
- {
- ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp112r1"); // $ hasInsufficientKeySize // test ECGenParameterSpec variable
- KeyPairGenerator keyPairGen22 = KeyPairGenerator.getInstance("EC"); // test KeyPairGenerator variable
- testAsymmetricEC(ecSpec, keyPairGen22);
-
- }
-
}
- public static void testSymmetric(int keySize, KeyGenerator kg) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
- // BAD: Key size is less than 2048
+ public static void testSymmetricVariable(int keySize, KeyGenerator kg) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(keySize); // $ hasInsufficientKeySize
-
- // BAD: Key size is less than 2048
kg.init(64); // $ MISSING: hasInsufficientKeySize
}
- //! refactor this to use expected-value tag and combine with above method
- public static void testSymmetric2(int keySize) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
- // BAD: Key size is less than 2048
+ public static void testSymmetricInt(int keySize) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(keySize); // $ hasInsufficientKeySize
}
- public static void testAsymmetricNonEC(int keySize, KeyPairGenerator kpg) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
- // BAD: Key size is less than 2048
+ public static void testAsymmetricNonEcVariable(int keySize, KeyPairGenerator kpg) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(keySize); // $ hasInsufficientKeySize
-
- // BAD: Key size is less than 2048
kpg.initialize(1024); // $ MISSING: hasInsufficientKeySize
}
- //! refactor this to use expected-value tag and combine with above method
- public static void testAsymmetricNonEC2(int keySize) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
- // BAD: Key size is less than 2048
+ public static void testAsymmetricNonEcInt(int keySize) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(keySize); // $ hasInsufficientKeySize
}
- public static void testAsymmetricEC(ECGenParameterSpec spec, KeyPairGenerator kpg) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
- // BAD: Key size is less than 256
+ public static void testAsymmetricEcVariable(ECGenParameterSpec spec, KeyPairGenerator kpg) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("EC");
- keyPairGen.initialize(spec); // sink is now at above where `spec` variable is initialized
+ keyPairGen.initialize(spec); // sink is above where `spec` variable is initialized
- // BAD: Key size is less than 256
ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp112r1"); // $ hasInsufficientKeySize
- kpg.initialize(ecSpec);
+ kpg.initialize(ecSpec); // MISSING: test KeyGenerator variable as argument
}
- // ToDo testing:
- // ? todo #1: add tests for keysize variable passed to specs - not needed if spec is sink now
- // ? todo #3: add test for retrieving a key from elsewhere?
- // ? todo #4: add barrier-guard tests (see FP from OpenIdentityPlatform/OpenAM)
- // ? todo #5: add tests for updated keysize variable?: e.g. keysize = 1024; keysize += 1024; so when it's used it is correctly 2048.
- // ? todo #6: consider if some flow paths for keysize variables will be too hard to track how the keysize is updated (e.g. if calling some other method to get keysize, etc....)
+ public static void testAsymmetricEcInt(int keySize) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
+ KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("EC");
+ keyPairGen.initialize(keySize); // $ hasInsufficientKeySize
+ }
+
+ // public static void testVariable(int keySize, KeyGenerator kg) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
+ // KeyGenerator keyGen = KeyGenerator.getInstance("AES");
+ // keyGen.init(keySize); // $ hasInsufficientKeySize
+
+ // // BAD: Key size is less than 2048
+ // kg.init(64); // $ MISSING: hasInsufficientKeySize
+ // }
+
+ // public static void testInt(int keySize, KeyGenerator kg) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
+ // }
}
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
index 5dc7ecdf467..59b8a39ed0d 100644
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.ql
@@ -2,7 +2,6 @@ import java
import TestUtilities.InlineExpectationsTest
import semmle.code.java.security.InsufficientKeySizeQuery
-//import DataFlow::PathGraph // Note: importing this messes up tests - adds edges and nodes to actual file...
class InsufficientKeySizeTest extends InlineExpectationsTest {
InsufficientKeySizeTest() { this = "InsufficientKeySize" }
@@ -11,12 +10,8 @@ class InsufficientKeySizeTest extends InlineExpectationsTest {
override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "hasInsufficientKeySize" and
exists(DataFlow::PathNode source, DataFlow::PathNode sink |
- exists(KeySizeConfiguration config1 | config1.hasFlowPath(source, sink))
+ exists(KeySizeConfiguration cfg | cfg.hasFlowPath(source, sink))
|
- //or
- // exists(AsymmetricNonECKeyTrackingConfiguration cfg | cfg.hasFlowPath(source, sink)) or
- // exists(AsymmetricECKeyTrackingConfiguration cfg | cfg.hasFlowPath(source, sink)) or
- // exists(SymmetricKeyTrackingConfiguration cfg | cfg.hasFlowPath(source, sink))
sink.getNode().getLocation() = location and
element = sink.getNode().toString() and
value = ""
From 5f39888a2dc9512e9f542c890abee67122b9f781 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Mon, 17 Oct 2022 16:28:06 -0400
Subject: [PATCH 038/249] minor code restructure
---
.../semmle/code/java/security/Encryption.qll | 59 +++++++++++--------
.../java/security/InsufficientKeySize.qll | 2 +-
2 files changed, 37 insertions(+), 24 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/Encryption.qll b/java/ql/lib/semmle/code/java/security/Encryption.qll
index adb2dc74691..464f8fda0a8 100644
--- a/java/ql/lib/semmle/code/java/security/Encryption.qll
+++ b/java/ql/lib/semmle/code/java/security/Encryption.qll
@@ -323,20 +323,21 @@ class JavaxCryptoSecretKey extends JavaxCryptoAlgoSpec {
}
}
-/** The Java class `javax.crypto.spec.DHGenParameterSpec`. */
-class DhGenParameterSpec extends RefType {
- DhGenParameterSpec() { this.hasQualifiedName("javax.crypto.spec", "DHGenParameterSpec") }
-}
-
class JavaxCryptoKeyGenerator extends JavaxCryptoAlgoSpec {
JavaxCryptoKeyGenerator() {
+ exists(Constructor c | c.getAReference() = this | c.getDeclaringType() instanceof KeyGenerator)
+ or
exists(Method m | m.getAReference() = this |
m.getDeclaringType() instanceof KeyGenerator and
m.getName() = "getInstance"
)
}
- override Expr getAlgoSpec() { result = this.(MethodAccess).getArgument(0) }
+ override Expr getAlgoSpec() {
+ exists(Call c | c = this |
+ if c.getNumArgument() = 3 then result = c.getArgument(2) else result = c.getArgument(0)
+ )
+ }
}
class JavaxCryptoKeyAgreement extends JavaxCryptoAlgoSpec {
@@ -388,29 +389,41 @@ class JavaSecuritySignature extends JavaSecurityAlgoSpec {
override Expr getAlgoSpec() { result = this.(ConstructorCall).getArgument(0) }
}
-/** The Java class `java.security.spec.ECGenParameterSpec`. */
-class EcGenParameterSpec extends RefType {
- EcGenParameterSpec() { this.hasQualifiedName("java.security.spec", "ECGenParameterSpec") }
-}
-
-/** The Java class `java.security.spec.RSAKeyGenParameterSpec`. */
-class RsaKeyGenParameterSpec extends RefType {
- RsaKeyGenParameterSpec() { this.hasQualifiedName("java.security.spec", "RSAKeyGenParameterSpec") }
-}
-
-/** The Java class `java.security.spec.DSAGenParameterSpec`. */
-class DsaGenParameterSpec extends RefType {
- DsaGenParameterSpec() { this.hasQualifiedName("java.security.spec", "DSAGenParameterSpec") }
-}
-
/** A method call to the Java class `java.security.KeyPairGenerator`. */
-class JavaSecurityKeyPairGenerator extends JavaxCryptoAlgoSpec {
+class JavaSecurityKeyPairGenerator extends JavaSecurityAlgoSpec {
JavaSecurityKeyPairGenerator() {
+ exists(Constructor c | c.getAReference() = this |
+ c.getDeclaringType() instanceof KeyPairGenerator
+ )
+ or
exists(Method m | m.getAReference() = this |
m.getDeclaringType() instanceof KeyPairGenerator and
m.getName() = "getInstance"
)
}
- override Expr getAlgoSpec() { result = this.(MethodAccess).getArgument(0) }
+ override Expr getAlgoSpec() { result = this.(Call).getArgument(0) }
+}
+
+/** The Java interface `java.security.spec.AlgorithmParameterSpec` */
+abstract class AlgorithmParameterSpec extends RefType { }
+
+/** The Java class `java.security.spec.ECGenParameterSpec`. */
+class EcGenParameterSpec extends AlgorithmParameterSpec {
+ EcGenParameterSpec() { this.hasQualifiedName("java.security.spec", "ECGenParameterSpec") }
+}
+
+/** The Java class `java.security.spec.RSAKeyGenParameterSpec`. */
+class RsaKeyGenParameterSpec extends AlgorithmParameterSpec {
+ RsaKeyGenParameterSpec() { this.hasQualifiedName("java.security.spec", "RSAKeyGenParameterSpec") }
+}
+
+/** The Java class `java.security.spec.DSAGenParameterSpec`. */
+class DsaGenParameterSpec extends AlgorithmParameterSpec {
+ DsaGenParameterSpec() { this.hasQualifiedName("java.security.spec", "DSAGenParameterSpec") }
+}
+
+/** The Java class `javax.crypto.spec.DHGenParameterSpec`. */
+class DhGenParameterSpec extends AlgorithmParameterSpec {
+ DhGenParameterSpec() { this.hasQualifiedName("javax.crypto.spec", "DHGenParameterSpec") }
}
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
index 16f960f1b6d..be12385f5cf 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
@@ -124,7 +124,7 @@ private class SymmetricInitMethodAccess extends KeyGenInitMethodAccess {
}
/** An instance of a key generator. */
-abstract class KeyGeneratorObject extends JavaxCryptoAlgoSpec {
+abstract class KeyGeneratorObject extends CryptoAlgoSpec {
string getAlgoName() { result = this.getAlgoSpec().(StringLiteral).getValue().toUpperCase() }
}
From 55bda34a45f50e1b35dc060511f6cdb9b7c0653c Mon Sep 17 00:00:00 2001
From: Arthur Baars
Date: Tue, 18 Oct 2022 15:07:35 +0200
Subject: [PATCH 039/249] Ruby: drop beta notice
---
docs/codeql/codeql-language-guides/codeql-for-ruby.rst | 1 -
docs/codeql/query-help/codeql-cwe-coverage.rst | 1 -
docs/codeql/query-help/index.rst | 2 --
docs/codeql/reusables/ruby-beta-note.rst | 4 ----
4 files changed, 8 deletions(-)
delete mode 100644 docs/codeql/reusables/ruby-beta-note.rst
diff --git a/docs/codeql/codeql-language-guides/codeql-for-ruby.rst b/docs/codeql/codeql-language-guides/codeql-for-ruby.rst
index bfb29a012ef..b19f8abe230 100644
--- a/docs/codeql/codeql-language-guides/codeql-for-ruby.rst
+++ b/docs/codeql/codeql-language-guides/codeql-for-ruby.rst
@@ -15,4 +15,3 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
- :doc:`CodeQL library for Ruby `: When you're analyzing a Ruby program, you can make use of the large collection of classes in the CodeQL library for Ruby.
-.. include:: ../reusables/ruby-beta-note.rst
diff --git a/docs/codeql/query-help/codeql-cwe-coverage.rst b/docs/codeql/query-help/codeql-cwe-coverage.rst
index 30e7b569184..c0b36646df8 100644
--- a/docs/codeql/query-help/codeql-cwe-coverage.rst
+++ b/docs/codeql/query-help/codeql-cwe-coverage.rst
@@ -35,4 +35,3 @@ Note that the CWE coverage includes both "`supported queries `."
-.. include:: ../reusables/ruby-beta-note.rst
-
.. toctree::
:hidden:
:titlesonly:
diff --git a/docs/codeql/reusables/ruby-beta-note.rst b/docs/codeql/reusables/ruby-beta-note.rst
deleted file mode 100644
index 761381777c0..00000000000
--- a/docs/codeql/reusables/ruby-beta-note.rst
+++ /dev/null
@@ -1,4 +0,0 @@
- .. pull-quote:: Note
-
- CodeQL analysis for Ruby is currently in beta. During the beta, analysis of Ruby code,
- and the accompanying documentation, will not be as comprehensive as for other languages.
From 383b8a84e95b2017b45cd030546ee8bf4ee7ba91 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Tue, 18 Oct 2022 21:55:11 -0400
Subject: [PATCH 040/249] update select statement to be closer to cpp's
---
java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
index b82104db79a..23efca5483c 100644
--- a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
+++ b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
@@ -1,9 +1,9 @@
/**
- * @name Insufficient key size used with a cryptographic algorithm
- * @description Using cryptographic algorithms with too small of a key size can
+ * @name Use of a cryptographic algorithm with insufficient key size
+ * @description Using cryptographic algorithms with too small a key size can
* allow an attacker to compromise security.
* @kind path-problem
- * @problem.severity error
+ * @problem.severity warning
* @security-severity 7.5
* @precision high
* @id java/insufficient-key-size
@@ -17,5 +17,6 @@ import DataFlow::PathGraph
from DataFlow::PathNode source, DataFlow::PathNode sink
where exists(KeySizeConfiguration cfg | cfg.hasFlowPath(source, sink))
-select sink.getNode(), source, sink, "This $@ is less than the recommended key size.",
+select sink.getNode(), source, sink,
+ "This $@ is less than the recommended key size of " + source.getState() + " bits.",
source.getNode(), "key size"
From ff557a287f552a918e9410d3517dca63ab450d05 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Tue, 18 Oct 2022 23:08:54 -0400
Subject: [PATCH 041/249] add min key size predicates
---
.../java/security/InsufficientKeySize.qll | 40 +++++++++++++------
1 file changed, 28 insertions(+), 12 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
index be12385f5cf..8dfd2987eb0 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
@@ -18,32 +18,45 @@ abstract class InsufficientKeySizeSink extends DataFlow::Node {
// *********************************** SOURCES ***********************************
/** A source for an insufficient key size used in RSA, DSA, and DH algorithms. */
private class AsymmetricNonEcSource extends InsufficientKeySizeSource {
- AsymmetricNonEcSource() { getNodeIntValue(this) < 2048 }
+ AsymmetricNonEcSource() { getNodeIntValue(this) < getMinAsymNonEcKeySize() }
- override predicate hasState(DataFlow::FlowState state) { state = "2048" }
+ override predicate hasState(DataFlow::FlowState state) {
+ state = getMinAsymNonEcKeySize().toString()
+ }
}
/** A source for an insufficient key size used in elliptic curve (EC) algorithms. */
private class AsymmetricEcSource extends InsufficientKeySizeSource {
AsymmetricEcSource() {
- getNodeIntValue(this) < 256
+ getNodeIntValue(this) < getMinAsymEcKeySize()
or
// the below is needed for cases when the key size is embedded in the curve name
- getEcKeySize(this.asExpr().(StringLiteral).getValue()) < 256
+ getEcKeySize(this.asExpr().(StringLiteral).getValue()) < getMinAsymEcKeySize()
}
- override predicate hasState(DataFlow::FlowState state) { state = "256" }
+ override predicate hasState(DataFlow::FlowState state) {
+ state = getMinAsymEcKeySize().toString()
+ }
}
/** A source for an insufficient key size used in AES algorithms. */
private class SymmetricSource extends InsufficientKeySizeSource {
- SymmetricSource() { getNodeIntValue(this) < 128 }
+ SymmetricSource() { getNodeIntValue(this) < getMinSymKeySize() }
- override predicate hasState(DataFlow::FlowState state) { state = "128" }
+ override predicate hasState(DataFlow::FlowState state) { state = getMinSymKeySize().toString() }
}
// ************************** SOURCES HELPER PREDICATES **************************
-/** Returns the integer value of a given Node. */
+/** Returns the minimum recommended key size for RSA, DSA, and DH algorithms. */
+private int getMinAsymNonEcKeySize() { result = 2048 }
+
+/** Returns the minimum recommended key size for elliptic curve (EC) algorithms. */
+private int getMinAsymEcKeySize() { result = 256 }
+
+/** Returns the minimum recommended key size for AES algorithms. */
+private int getMinSymKeySize() { result = 128 }
+
+/** Returns the integer value of a given DataFlow::Node. */
private int getNodeIntValue(DataFlow::Node node) {
result = node.asExpr().(IntegerLiteral).getIntValue()
}
@@ -74,7 +87,9 @@ private class AsymmetricNonEcSink extends InsufficientKeySizeSink {
exists(AsymmetricNonEcSpec spec | this.asExpr() = spec.getKeySizeArg())
}
- override predicate hasState(DataFlow::FlowState state) { state = "2048" }
+ override predicate hasState(DataFlow::FlowState state) {
+ state = getMinAsymNonEcKeySize().toString()
+ }
}
/** A sink for an insufficient key size used in elliptic curve (EC) algorithms. */
@@ -89,13 +104,14 @@ private class AsymmetricEcSink extends InsufficientKeySizeSink {
exists(AsymmetricEcSpec s | this.asExpr() = s.getKeySizeArg())
}
- override predicate hasState(DataFlow::FlowState state) { state = "256" }
+ override predicate hasState(DataFlow::FlowState state) {
+ state = getMinAsymEcKeySize().toString()
+ }
}
/** A sink for an insufficient key size used in AES algorithms. */
private class SymmetricSink extends InsufficientKeySizeSink {
SymmetricSink() {
- //hasKeySizeInInitMethod(this, "symmetric")
exists(SymmetricInitMethodAccess ma, SymmetricKeyGenerator kg |
kg.getAlgoName() = "AES" and
DataFlow::localExprFlow(kg, ma.getQualifier()) and
@@ -103,7 +119,7 @@ private class SymmetricSink extends InsufficientKeySizeSink {
)
}
- override predicate hasState(DataFlow::FlowState state) { state = "128" }
+ override predicate hasState(DataFlow::FlowState state) { state = getMinSymKeySize().toString() }
}
// ********************** SINKS HELPER CLASSES & PREDICATES **********************
From dc8b62baa0c68de6f66004c81e2b32c20adcd220 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Wed, 19 Oct 2022 00:11:59 -0400
Subject: [PATCH 042/249] add support for AlgorithmParameterGenerator
---
.../semmle/code/java/security/Encryption.qll | 37 ++++++++++++++++++-
.../java/security/InsufficientKeySize.qll | 14 +++++--
2 files changed, 46 insertions(+), 5 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/Encryption.qll b/java/ql/lib/semmle/code/java/security/Encryption.qll
index 464f8fda0a8..7fb3b16542f 100644
--- a/java/ql/lib/semmle/code/java/security/Encryption.qll
+++ b/java/ql/lib/semmle/code/java/security/Encryption.qll
@@ -389,7 +389,7 @@ class JavaSecuritySignature extends JavaSecurityAlgoSpec {
override Expr getAlgoSpec() { result = this.(ConstructorCall).getArgument(0) }
}
-/** A method call to the Java class `java.security.KeyPairGenerator`. */
+/** An instance of a `java.security.KeyPairGenerator`. */
class JavaSecurityKeyPairGenerator extends JavaSecurityAlgoSpec {
JavaSecurityKeyPairGenerator() {
exists(Constructor c | c.getAReference() = this |
@@ -405,6 +405,41 @@ class JavaSecurityKeyPairGenerator extends JavaSecurityAlgoSpec {
override Expr getAlgoSpec() { result = this.(Call).getArgument(0) }
}
+/** The Java class `java.security.AlgorithmParameterGenerator`. */
+class AlgorithmParameterGenerator extends RefType {
+ AlgorithmParameterGenerator() {
+ this.hasQualifiedName("java.security", "AlgorithmParameterGenerator")
+ }
+}
+
+/** The `init` method declared in `java.security.AlgorithmParameterGenerator`. */
+class AlgoParamGeneratorInitMethod extends Method {
+ AlgoParamGeneratorInitMethod() {
+ this.getDeclaringType() instanceof AlgorithmParameterGenerator and
+ this.hasName("init")
+ }
+}
+
+/** An instance of a `java.security.AlgorithmParameterGenerator`. */
+class JavaSecurityAlgoParamGenerator extends JavaSecurityAlgoSpec {
+ JavaSecurityAlgoParamGenerator() {
+ exists(Constructor c | c.getAReference() = this |
+ c.getDeclaringType() instanceof AlgorithmParameterGenerator
+ )
+ or
+ exists(Method m | m.getAReference() = this |
+ m.getDeclaringType() instanceof AlgorithmParameterGenerator and
+ m.getName() = "getInstance"
+ )
+ }
+
+ override Expr getAlgoSpec() {
+ exists(Call c | c = this |
+ if c.getNumArgument() = 3 then result = c.getArgument(2) else result = c.getArgument(0)
+ )
+ }
+}
+
/** The Java interface `java.security.spec.AlgorithmParameterSpec` */
abstract class AlgorithmParameterSpec extends RefType { }
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
index 8dfd2987eb0..73203c3e6f2 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
@@ -131,7 +131,10 @@ abstract class KeyGenInitMethodAccess extends MethodAccess {
/** A call to the `initialize` method declared in `java.security.KeyPairGenerator`. */
private class AsymmetricInitMethodAccess extends KeyGenInitMethodAccess {
- AsymmetricInitMethodAccess() { this.getMethod() instanceof KeyPairGeneratorInitMethod }
+ AsymmetricInitMethodAccess() {
+ this.getMethod() instanceof KeyPairGeneratorInitMethod or
+ this.getMethod() instanceof AlgoParamGeneratorInitMethod
+ }
}
/** A call to the `init` method declared in `javax.crypto.KeyGenerator`. */
@@ -146,16 +149,19 @@ abstract class KeyGeneratorObject extends CryptoAlgoSpec {
/** An instance of a `java.security.KeyPairGenerator`. */
private class AsymmetricKeyGenerator extends KeyGeneratorObject {
- AsymmetricKeyGenerator() { this instanceof JavaSecurityKeyPairGenerator }
+ AsymmetricKeyGenerator() {
+ this instanceof JavaSecurityKeyPairGenerator or
+ this instanceof JavaSecurityAlgoParamGenerator
+ }
- override Expr getAlgoSpec() { result = this.(MethodAccess).getArgument(0) }
+ override Expr getAlgoSpec() { result = this.getAlgoSpec() }
}
/** An instance of a `javax.crypto.KeyGenerator`. */
private class SymmetricKeyGenerator extends KeyGeneratorObject {
SymmetricKeyGenerator() { this instanceof JavaxCryptoKeyGenerator }
- override Expr getAlgoSpec() { result = this.(MethodAccess).getArgument(0) }
+ override Expr getAlgoSpec() { result = this.getAlgoSpec() }
}
/** An instance of an algorithm specification. */
From 4df0fbcce1ca04bd9e8de33031ff9da34f2bb6f5 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Wed, 19 Oct 2022 01:17:57 -0400
Subject: [PATCH 043/249] update tests
---
.../semmle/code/java/security/Encryption.qll | 24 ++-------
.../CWE-326/InsufficientKeySizeTest.java | 50 +++++++++++--------
2 files changed, 33 insertions(+), 41 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/Encryption.qll b/java/ql/lib/semmle/code/java/security/Encryption.qll
index 7fb3b16542f..208343da0cc 100644
--- a/java/ql/lib/semmle/code/java/security/Encryption.qll
+++ b/java/ql/lib/semmle/code/java/security/Encryption.qll
@@ -325,19 +325,13 @@ class JavaxCryptoSecretKey extends JavaxCryptoAlgoSpec {
class JavaxCryptoKeyGenerator extends JavaxCryptoAlgoSpec {
JavaxCryptoKeyGenerator() {
- exists(Constructor c | c.getAReference() = this | c.getDeclaringType() instanceof KeyGenerator)
- or
exists(Method m | m.getAReference() = this |
m.getDeclaringType() instanceof KeyGenerator and
m.getName() = "getInstance"
)
}
- override Expr getAlgoSpec() {
- exists(Call c | c = this |
- if c.getNumArgument() = 3 then result = c.getArgument(2) else result = c.getArgument(0)
- )
- }
+ override Expr getAlgoSpec() { result = this.(MethodAccess).getArgument(0) }
}
class JavaxCryptoKeyAgreement extends JavaxCryptoAlgoSpec {
@@ -392,17 +386,13 @@ class JavaSecuritySignature extends JavaSecurityAlgoSpec {
/** An instance of a `java.security.KeyPairGenerator`. */
class JavaSecurityKeyPairGenerator extends JavaSecurityAlgoSpec {
JavaSecurityKeyPairGenerator() {
- exists(Constructor c | c.getAReference() = this |
- c.getDeclaringType() instanceof KeyPairGenerator
- )
- or
exists(Method m | m.getAReference() = this |
m.getDeclaringType() instanceof KeyPairGenerator and
m.getName() = "getInstance"
)
}
- override Expr getAlgoSpec() { result = this.(Call).getArgument(0) }
+ override Expr getAlgoSpec() { result = this.(MethodAccess).getArgument(0) }
}
/** The Java class `java.security.AlgorithmParameterGenerator`. */
@@ -423,21 +413,13 @@ class AlgoParamGeneratorInitMethod extends Method {
/** An instance of a `java.security.AlgorithmParameterGenerator`. */
class JavaSecurityAlgoParamGenerator extends JavaSecurityAlgoSpec {
JavaSecurityAlgoParamGenerator() {
- exists(Constructor c | c.getAReference() = this |
- c.getDeclaringType() instanceof AlgorithmParameterGenerator
- )
- or
exists(Method m | m.getAReference() = this |
m.getDeclaringType() instanceof AlgorithmParameterGenerator and
m.getName() = "getInstance"
)
}
- override Expr getAlgoSpec() {
- exists(Call c | c = this |
- if c.getNumArgument() = 3 then result = c.getArgument(2) else result = c.getArgument(0)
- )
- }
+ override Expr getAlgoSpec() { result = this.(MethodAccess).getArgument(0) }
}
/** The Java interface `java.security.spec.AlgorithmParameterSpec` */
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
index 3e6a1649ceb..e60544eb6dc 100644
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
@@ -1,5 +1,6 @@
import javax.crypto.KeyGenerator;
import java.security.KeyPairGenerator;
+import java.security.AlgorithmParameterGenerator;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.RSAKeyGenParameterSpec;
@@ -30,8 +31,8 @@ public class InsufficientKeySizeTest {
keyGen4.init(size2); // $ hasInsufficientKeySize
/* Test variables passed to another method */
- KeyGenerator keyGen = KeyGenerator.getInstance("AES"); // MISSING: test KeyGenerator variable as argument
- testSymmetricVariable(size2, keyGen); // test with variable as key size
+ KeyGenerator keyGen5 = KeyGenerator.getInstance("AES"); // MISSING: test KeyGenerator variable as argument
+ testSymmetricVariable(size2, keyGen5); // test with variable as key size
testSymmetricInt(64); // test with int literal as key size
}
@@ -62,9 +63,13 @@ public class InsufficientKeySizeTest {
keyPairGen6.initialize(size2); // $ hasInsufficientKeySize
/* Test variables passed to another method */
- KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA"); // MISSING: test KeyGenerator variable as argument
- testAsymmetricNonEcVariable(size2, keyPairGen); // test with variable as key size
+ KeyPairGenerator keyPairGen7 = KeyPairGenerator.getInstance("RSA"); // MISSING: test KeyGenerator variable as argument
+ testAsymmetricNonEcVariable(size2, keyPairGen7); // test with variable as key size
testAsymmetricNonEcInt(1024); // test with int literal as key size
+
+ /* Test getting key size as return value of another method */
+ KeyPairGenerator keyPairGen8 = KeyPairGenerator.getInstance("RSA");
+ keyPairGen8.initialize(getRSAKeySize()); // $ hasInsufficientKeySize
}
// DSA (Asymmetric): minimum recommended key size is 2048
@@ -82,6 +87,10 @@ public class InsufficientKeySizeTest {
KeyPairGenerator keyPairGen4 = KeyPairGenerator.getInstance("DSA");
keyPairGen4.initialize(new DSAGenParameterSpec(1024, 0)); // $ hasInsufficientKeySize
+
+ /* Test `AlgorithmParameterGenerator` */
+ AlgorithmParameterGenerator paramGen = AlgorithmParameterGenerator.getInstance("DSA");
+ paramGen.init(1024); // $ hasInsufficientKeySize
}
// DH (Asymmetric): minimum recommended key size is 2048
@@ -99,6 +108,10 @@ public class InsufficientKeySizeTest {
KeyPairGenerator keyPairGen4 = KeyPairGenerator.getInstance("DH");
keyPairGen4.initialize(new DHGenParameterSpec(1024, 0)); // $ hasInsufficientKeySize
+
+ /* Test `AlgorithmParameterGenerator` */
+ AlgorithmParameterGenerator paramGen = AlgorithmParameterGenerator.getInstance("DH");
+ paramGen.init(1024); // $ hasInsufficientKeySize
}
// EC (Asymmetric): minimum recommended key size is 256
@@ -153,8 +166,11 @@ public class InsufficientKeySizeTest {
/* Test variables passed to another method */
ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp112r1"); // $ hasInsufficientKeySize
+ testAsymmetricEcSpecVariable(ecSpec); // test spec as an argument
+ int size = 128;
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("EC"); // MISSING: test KeyGenerator variable as argument
- testAsymmetricEC(ecSpec, keyPairGen); // test spec as an argument
+ testAsymmetricEcIntVariable(size, keyPairGen); // test with variable as key size
+ testAsymmetricEcIntLiteral(128); // test with int literal as key size
}
}
@@ -180,27 +196,21 @@ public class InsufficientKeySizeTest {
keyPairGen.initialize(keySize); // $ hasInsufficientKeySize
}
- public static void testAsymmetricEcVariable(ECGenParameterSpec spec, KeyPairGenerator kpg) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
+ public static void testAsymmetricEcSpecVariable(ECGenParameterSpec spec) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("EC");
keyPairGen.initialize(spec); // sink is above where `spec` variable is initialized
-
- ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp112r1"); // $ hasInsufficientKeySize
- kpg.initialize(ecSpec); // MISSING: test KeyGenerator variable as argument
}
- public static void testAsymmetricEcInt(int keySize) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
+ public static void testAsymmetricEcIntVariable(int keySize, KeyPairGenerator kpg) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
+ KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("EC");
+ keyPairGen.initialize(keySize); // $ hasInsufficientKeySize
+ kpg.initialize(128); // $ MISSING: hasInsufficientKeySize
+ }
+
+ public static void testAsymmetricEcIntLiteral(int keySize) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("EC");
keyPairGen.initialize(keySize); // $ hasInsufficientKeySize
}
- // public static void testVariable(int keySize, KeyGenerator kg) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
- // KeyGenerator keyGen = KeyGenerator.getInstance("AES");
- // keyGen.init(keySize); // $ hasInsufficientKeySize
-
- // // BAD: Key size is less than 2048
- // kg.init(64); // $ MISSING: hasInsufficientKeySize
- // }
-
- // public static void testInt(int keySize, KeyGenerator kg) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
- // }
+ public int getRSAKeySize(){ return 1024; }
}
From 961e5c72a334e45ffd60151b626247bc9fc158d9 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Wed, 19 Oct 2022 08:44:35 -0400
Subject: [PATCH 044/249] minor updates
---
.../code/java/security/InsufficientKeySize.qll | 16 +++++++++++-----
.../CWE-326/InsufficientKeySizeTest.java | 1 -
2 files changed, 11 insertions(+), 6 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
index 73203c3e6f2..e4bb8e0a646 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
@@ -124,12 +124,15 @@ private class SymmetricSink extends InsufficientKeySizeSink {
// ********************** SINKS HELPER CLASSES & PREDICATES **********************
/** A call to a method that initializes a key generator. */
-abstract class KeyGenInitMethodAccess extends MethodAccess {
+abstract private class KeyGenInitMethodAccess extends MethodAccess {
/** Gets the `keysize` argument of this call. */
Argument getKeySizeArg() { result = this.getArgument(0) }
}
-/** A call to the `initialize` method declared in `java.security.KeyPairGenerator`. */
+/**
+ * A call to the `initialize` method declared in `java.security.KeyPairGenerator`
+ * or to the `init` method declared in `java.security.AlgorithmParameterGenerator`.
+ */
private class AsymmetricInitMethodAccess extends KeyGenInitMethodAccess {
AsymmetricInitMethodAccess() {
this.getMethod() instanceof KeyPairGeneratorInitMethod or
@@ -143,11 +146,14 @@ private class SymmetricInitMethodAccess extends KeyGenInitMethodAccess {
}
/** An instance of a key generator. */
-abstract class KeyGeneratorObject extends CryptoAlgoSpec {
+abstract private class KeyGeneratorObject extends CryptoAlgoSpec {
string getAlgoName() { result = this.getAlgoSpec().(StringLiteral).getValue().toUpperCase() }
}
-/** An instance of a `java.security.KeyPairGenerator`. */
+/**
+ * An instance of a `java.security.KeyPairGenerator`
+ * or of a `java.security.AlgorithmParameterGenerator`.
+ */
private class AsymmetricKeyGenerator extends KeyGeneratorObject {
AsymmetricKeyGenerator() {
this instanceof JavaSecurityKeyPairGenerator or
@@ -165,7 +171,7 @@ private class SymmetricKeyGenerator extends KeyGeneratorObject {
}
/** An instance of an algorithm specification. */
-abstract class AlgoSpec extends ClassInstanceExpr {
+abstract private class AlgoSpec extends ClassInstanceExpr {
Argument getKeySizeArg() { result = this.getArgument(0) }
}
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
index e60544eb6dc..746239a846f 100644
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
@@ -1,7 +1,6 @@
import javax.crypto.KeyGenerator;
import java.security.KeyPairGenerator;
import java.security.AlgorithmParameterGenerator;
-
import java.security.spec.ECGenParameterSpec;
import java.security.spec.RSAKeyGenParameterSpec;
import java.security.spec.DSAGenParameterSpec;
From e5982f19fa8c2321d9bfc746333fdb0f716fdead Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Wed, 19 Oct 2022 11:05:40 -0400
Subject: [PATCH 045/249] minor updates
---
.../java/security/InsufficientKeySize.qll | 11 +-
.../CWE/CWE-326/InsufficientKeySize.qhelp | 31 +---
.../CWE/CWE-326/InsufficientKeySizeBad.java | 19 +-
.../CWE/CWE-326/InsufficientKeySizeGood.java | 16 --
.../CWE-326/InsufficientKeySizeTest.java | 3 +
.../security/CWE-326/SignatureTest.java | 167 +-----------------
6 files changed, 23 insertions(+), 224 deletions(-)
delete mode 100644 java/ql/src/Security/CWE/CWE-326/InsufficientKeySizeGood.java
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
index e4bb8e0a646..41852e1aeda 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
@@ -61,7 +61,7 @@ private int getNodeIntValue(DataFlow::Node node) {
result = node.asExpr().(IntegerLiteral).getIntValue()
}
-/** Returns the key size from an EC algorithm curve name string */
+/** Returns the key size from an EC algorithm's curve name string */
bindingset[algorithm]
private int getEcKeySize(string algorithm) {
algorithm.matches("sec%") and // specification such as "secp256r1"
@@ -145,8 +145,9 @@ private class SymmetricInitMethodAccess extends KeyGenInitMethodAccess {
SymmetricInitMethodAccess() { this.getMethod() instanceof KeyGeneratorInitMethod }
}
-/** An instance of a key generator. */
-abstract private class KeyGeneratorObject extends CryptoAlgoSpec {
+/** An instance of a generator that specifies an encryption algorithm. */
+abstract private class AlgoGeneratorObject extends CryptoAlgoSpec {
+ /** Returns an uppercase string representing the algorithm name specified by this generator object. */
string getAlgoName() { result = this.getAlgoSpec().(StringLiteral).getValue().toUpperCase() }
}
@@ -154,7 +155,7 @@ abstract private class KeyGeneratorObject extends CryptoAlgoSpec {
* An instance of a `java.security.KeyPairGenerator`
* or of a `java.security.AlgorithmParameterGenerator`.
*/
-private class AsymmetricKeyGenerator extends KeyGeneratorObject {
+private class AsymmetricKeyGenerator extends AlgoGeneratorObject {
AsymmetricKeyGenerator() {
this instanceof JavaSecurityKeyPairGenerator or
this instanceof JavaSecurityAlgoParamGenerator
@@ -164,7 +165,7 @@ private class AsymmetricKeyGenerator extends KeyGeneratorObject {
}
/** An instance of a `javax.crypto.KeyGenerator`. */
-private class SymmetricKeyGenerator extends KeyGeneratorObject {
+private class SymmetricKeyGenerator extends AlgoGeneratorObject {
SymmetricKeyGenerator() { this instanceof JavaxCryptoKeyGenerator }
override Expr getAlgoSpec() { result = this.getAlgoSpec() }
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.qhelp b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.qhelp
index 47ef1124624..3a0b74bebd4 100644
--- a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.qhelp
+++ b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.qhelp
@@ -6,20 +6,19 @@
Modern encryption relies on the computational infeasibility of breaking a cipher and decoding its
message without the key. As computational power increases, the ability to break ciphers grows, and key
- sizes need to become larger as a result. Encryption algorithms that use too small of a key size are
+ sizes need to become larger as a result. Cryptographic algorithms that use too small of a key size are
vulnerable to brute force attacks, which can reveal sensitive data.
- Use a key of the recommended size or larger. The key size should be at least 2048 bits for RSA or
- DSA encryption, 256 bits for elliptic curve (EC) encryption, and 128 bits for symmetric encryption,
- such as AES.
+ Use a key of the recommended size or larger. The key size should be at least 128 bits for AES encryption,
+ 256 bits for elliptic-curve cryptography (ECC), and 2048 bits for RSA, DSA, or DH encryption.
- The following code uses encryption with insufficient key sizes.
+ The following code uses cryptographic algorithms with insufficient key sizes.
@@ -29,12 +28,6 @@
larger for each algorithm.
-
-
@@ -45,22 +38,6 @@
Wikipedia: Strong cryptography.
-
OWASP:
Cryptographic Storage Cheat Sheet.
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySizeBad.java b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySizeBad.java
index 641543ca964..8393143e86c 100644
--- a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySizeBad.java
+++ b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySizeBad.java
@@ -1,16 +1,15 @@
KeyPairGenerator keyPairGen1 = KeyPairGenerator.getInstance("RSA");
- // BAD: Key size is less than 2048
- keyPairGen1.initialize(1024);
+ keyPairGen1.initialize(1024); // BAD: Key size is less than 2048
KeyPairGenerator keyPairGen2 = KeyPairGenerator.getInstance("DSA");
- // BAD: Key size is less than 2048
- keyPairGen2.initialize(1024);
+ keyPairGen2.initialize(1024); // BAD: Key size is less than 2048
- KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("EC");
- // BAD: Key size is less than 256
- ECGenParameterSpec ecSpec1 = new ECGenParameterSpec("secp112r1");
- keyPairGen3.initialize(ecSpec1);
+ KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("DH");
+ keyPairGen3.initialize(1024); // BAD: Key size is less than 2048
+
+ KeyPairGenerator keyPairGen4 = KeyPairGenerator.getInstance("EC");
+ ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp112r1"); // BAD: Key size is less than 256
+ keyPairGen4.initialize(ecSpec);
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
- // BAD: Key size is less than 128
- keyGen.init(64);
+ keyGen.init(64); // BAD: Key size is less than 128
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySizeGood.java b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySizeGood.java
deleted file mode 100644
index 051f7dd2597..00000000000
--- a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySizeGood.java
+++ /dev/null
@@ -1,16 +0,0 @@
- KeyPairGenerator keyPairGen1 = KeyPairGenerator.getInstance("RSA");
- // GOOD: Key size is no less than 2048
- keyPairGen1.initialize(2048);
-
- KeyPairGenerator keyPairGen2 = KeyPairGenerator.getInstance("DSA");
- // GOOD: Key size is no less than 2048
- keyPairGen2.initialize(2048);
-
- KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("EC");
- // GOOD: Key size is no less than 256
- ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256r1");
- keyPairGen3.initialize(ecSpec);
-
- KeyGenerator keyGen = KeyGenerator.getInstance("AES");
- // GOOD: Key size is no less than 128
- keyGen.init(128);
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
index 746239a846f..e356f35d998 100644
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
@@ -44,6 +44,7 @@ public class InsufficientKeySizeTest {
KeyPairGenerator keyPairGen2 = KeyPairGenerator.getInstance("RSA");
keyPairGen2.initialize(2048); // Safe: Key size is no less than 2048
+ /* Test spec */
KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("RSA");
RSAKeyGenParameterSpec rsaSpec = new RSAKeyGenParameterSpec(1024, null); // $ hasInsufficientKeySize
keyPairGen3.initialize(rsaSpec);
@@ -80,6 +81,7 @@ public class InsufficientKeySizeTest {
KeyPairGenerator keyPairGen2 = KeyPairGenerator.getInstance("DSA");
keyPairGen2.initialize(2048); // Safe: Key size is no less than 2048
+ /* Test spec */
KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("DSA");
DSAGenParameterSpec dsaSpec = new DSAGenParameterSpec(1024, 0); // $ hasInsufficientKeySize
keyPairGen3.initialize(dsaSpec);
@@ -101,6 +103,7 @@ public class InsufficientKeySizeTest {
KeyPairGenerator keyPairGen2 = KeyPairGenerator.getInstance("DH");
keyPairGen2.initialize(2048); // Safe: Key size is no less than 2048
+ /* Test spec */
KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("DH");
DHGenParameterSpec dhSpec = new DHGenParameterSpec(1024, 0); // $ hasInsufficientKeySize
keyPairGen3.initialize(dhSpec);
diff --git a/java/ql/test/query-tests/security/CWE-326/SignatureTest.java b/java/ql/test/query-tests/security/CWE-326/SignatureTest.java
index 016a62a41f8..d7942581590 100644
--- a/java/ql/test/query-tests/security/CWE-326/SignatureTest.java
+++ b/java/ql/test/query-tests/security/CWE-326/SignatureTest.java
@@ -1,4 +1,4 @@
-//package org.bouncycastle.jce.provider.test;
+/* Adds tests to check for FPs related to RSA/DSA versus EC */
import java.security.KeyPair;
import java.security.KeyPairGenerator;
@@ -6,191 +6,26 @@ import java.security.SecureRandom;
import java.security.Security;
import java.security.Signature;
-// import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
-// import org.bouncycastle.jce.provider.BouncyCastleProvider;
-// import org.bouncycastle.jce.spec.ECNamedCurveGenParameterSpec;
-// import org.bouncycastle.jce.spec.GOST3410ParameterSpec;
-// import org.bouncycastle.util.encoders.Hex;
-// import org.bouncycastle.util.test.SimpleTest;
-
public class SignatureTest
- //extends SimpleTest
{
- // private static final byte[] DATA = Hex.decode("00000000deadbeefbeefdeadffffffff00000000");
-
- private void checkSig(KeyPair kp, String name)
- throws Exception
- {
- // Signature sig = Signature.getInstance(name, "BC");
-
- // sig.initSign(kp.getPrivate());
- // sig.update(DATA);
-
- // byte[] signature1 = sig.sign();
-
- // sig.update(DATA);
-
- // byte[] signature2 = sig.sign();
-
- // sig.initVerify(kp.getPublic());
-
- // sig.update(DATA);
- // if (!sig.verify(signature1))
- // {
- // fail("did not verify: " + name);
- // }
-
- // // After verify, should be reusable as if we are after initVerify
- // sig.update(DATA);
- // if (!sig.verify(signature1))
- // {
- // fail("second verify failed: " + name);
- // }
-
- // sig.update(DATA);
- // if (!sig.verify(signature2))
- // {
- // fail("second verify failed (2): " + name);
- // }
- }
public void performTest()
throws Exception
{
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC");
-
kpGen.initialize(2048); // Safe
-
KeyPair kp = kpGen.generateKeyPair();
- checkSig(kp, "SHA1withRSA");
- checkSig(kp, "SHA224withRSA");
- checkSig(kp, "SHA256withRSA");
- checkSig(kp, "SHA384withRSA");
- checkSig(kp, "SHA512withRSA");
-
- checkSig(kp, "SHA3-224withRSA");
- checkSig(kp, "SHA3-256withRSA");
- checkSig(kp, "SHA3-384withRSA");
- checkSig(kp, "SHA3-512withRSA");
-
- checkSig(kp, "MD2withRSA");
- checkSig(kp, "MD4withRSA");
- checkSig(kp, "MD5withRSA");
- checkSig(kp, "RIPEMD160withRSA");
- checkSig(kp, "RIPEMD128withRSA");
- checkSig(kp, "RIPEMD256withRSA");
-
- checkSig(kp, "SHA1withRSAandMGF1");
- checkSig(kp, "SHA1withRSAandMGF1");
- checkSig(kp, "SHA224withRSAandMGF1");
- checkSig(kp, "SHA256withRSAandMGF1");
- checkSig(kp, "SHA384withRSAandMGF1");
- checkSig(kp, "SHA512withRSAandMGF1");
-
- checkSig(kp, "SHA1withRSAandSHAKE128");
- checkSig(kp, "SHA1withRSAandSHAKE128");
- checkSig(kp, "SHA224withRSAandSHAKE128");
- checkSig(kp, "SHA256withRSAandSHAKE128");
- checkSig(kp, "SHA384withRSAandSHAKE128");
- checkSig(kp, "SHA512withRSAandSHAKE128");
-
- checkSig(kp, "SHA1withRSAandSHAKE256");
- checkSig(kp, "SHA1withRSAandSHAKE256");
- checkSig(kp, "SHA224withRSAandSHAKE256");
- checkSig(kp, "SHA256withRSAandSHAKE256");
- checkSig(kp, "SHA384withRSAandSHAKE256");
- checkSig(kp, "SHA512withRSAandSHAKE256");
-
- checkSig(kp, "SHAKE128withRSAPSS");
- checkSig(kp, "SHAKE256withRSAPSS");
-
- checkSig(kp, "SHA1withRSA/ISO9796-2");
- checkSig(kp, "MD5withRSA/ISO9796-2");
- checkSig(kp, "RIPEMD160withRSA/ISO9796-2");
-
-// checkSig(kp, "SHA1withRSA/ISO9796-2PSS");
-// checkSig(kp, "MD5withRSA/ISO9796-2PSS");
-// checkSig(kp, "RIPEMD160withRSA/ISO9796-2PSS");
-
- checkSig(kp, "RIPEMD128withRSA/X9.31");
- checkSig(kp, "RIPEMD160withRSA/X9.31");
- checkSig(kp, "SHA1withRSA/X9.31");
- checkSig(kp, "SHA224withRSA/X9.31");
- checkSig(kp, "SHA256withRSA/X9.31");
- checkSig(kp, "SHA384withRSA/X9.31");
- checkSig(kp, "SHA512withRSA/X9.31");
- checkSig(kp, "WhirlpoolwithRSA/X9.31");
-
kpGen = KeyPairGenerator.getInstance("DSA", "BC");
-
kpGen.initialize(2048); // Safe
-
kp = kpGen.generateKeyPair();
- checkSig(kp, "SHA1withDSA");
- checkSig(kp, "SHA224withDSA");
- checkSig(kp, "SHA256withDSA");
- checkSig(kp, "SHA384withDSA");
- checkSig(kp, "SHA512withDSA");
- checkSig(kp, "NONEwithDSA");
-
kpGen = KeyPairGenerator.getInstance("EC", "BC");
-
kpGen.initialize(256); // Safe
-
kp = kpGen.generateKeyPair();
- checkSig(kp, "SHA1withECDSA");
- checkSig(kp, "SHA224withECDSA");
- checkSig(kp, "SHA256withECDSA");
- checkSig(kp, "SHA384withECDSA");
- checkSig(kp, "SHA512withECDSA");
- checkSig(kp, "RIPEMD160withECDSA");
- checkSig(kp, "SHAKE128withECDSA");
- checkSig(kp, "SHAKE256withECDSA");
-
kpGen = KeyPairGenerator.getInstance("EC", "BC");
-
kpGen.initialize(521); // Safe
-
kp = kpGen.generateKeyPair();
-
- checkSig(kp, "SHA1withECNR");
- checkSig(kp, "SHA224withECNR");
- checkSig(kp, "SHA256withECNR");
- checkSig(kp, "SHA384withECNR");
- checkSig(kp, "SHA512withECNR");
-
- // kpGen = KeyPairGenerator.getInstance("ECGOST3410", "BC");
-
- // kpGen.initialize(new ECNamedCurveGenParameterSpec("GostR3410-2001-CryptoPro-A"), new SecureRandom());
-
- // kp = kpGen.generateKeyPair();
-
- // checkSig(kp, "GOST3411withECGOST3410");
-
- // kpGen = KeyPairGenerator.getInstance("GOST3410", "BC");
-
- // GOST3410ParameterSpec gost3410P = new GOST3410ParameterSpec(CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_A.getId());
-
- // kpGen.initialize(gost3410P);
-
- // kp = kpGen.generateKeyPair();
-
- // checkSig(kp, "GOST3411withGOST3410");
}
-
- public String getName()
- {
- return "SigNameTest";
- }
-
- // public static void main(
- // String[] args)
- // {
- // //Security.addProvider(new BouncyCastleProvider());
-
- // //runTest(new SignatureTest());
- // }
}
From b7f360647eca7790999e7bf04842974d9602acd5 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Wed, 19 Oct 2022 11:37:42 -0400
Subject: [PATCH 046/249] rename change note
---
...sufficient-key-size.md => 2022-10-19-insufficient-key-size.md} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename java/ql/src/change-notes/{2022-10-03-insufficient-key-size.md => 2022-10-19-insufficient-key-size.md} (100%)
diff --git a/java/ql/src/change-notes/2022-10-03-insufficient-key-size.md b/java/ql/src/change-notes/2022-10-19-insufficient-key-size.md
similarity index 100%
rename from java/ql/src/change-notes/2022-10-03-insufficient-key-size.md
rename to java/ql/src/change-notes/2022-10-19-insufficient-key-size.md
From 345e4e0e8f54453082bcb95b712e483526657965 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Thu, 20 Oct 2022 23:52:31 -0400
Subject: [PATCH 047/249] remove unnecessary 'exists'
---
java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
index 23efca5483c..2cf3d9115b3 100644
--- a/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
+++ b/java/ql/src/Security/CWE/CWE-326/InsufficientKeySize.ql
@@ -15,8 +15,8 @@ import java
import semmle.code.java.security.InsufficientKeySizeQuery
import DataFlow::PathGraph
-from DataFlow::PathNode source, DataFlow::PathNode sink
-where exists(KeySizeConfiguration cfg | cfg.hasFlowPath(source, sink))
+from DataFlow::PathNode source, DataFlow::PathNode sink, KeySizeConfiguration cfg
+where cfg.hasFlowPath(source, sink)
select sink.getNode(), source, sink,
"This $@ is less than the recommended key size of " + source.getState() + " bits.",
source.getNode(), "key size"
From aab1e1f5b441a508a5da1a321c846ee15623bce4 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Wed, 12 Oct 2022 11:48:31 +0200
Subject: [PATCH 048/249] Ruby: add some helpers at the AST level
---
ruby/ql/lib/codeql/ruby/ast/Module.qll | 34 ++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/ruby/ql/lib/codeql/ruby/ast/Module.qll b/ruby/ql/lib/codeql/ruby/ast/Module.qll
index 5b0309ddb21..e13506d2eb9 100644
--- a/ruby/ql/lib/codeql/ruby/ast/Module.qll
+++ b/ruby/ql/lib/codeql/ruby/ast/Module.qll
@@ -63,6 +63,33 @@ class Module extends TModule {
loc.getStartColumn()
)
}
+
+ /** Gets a constant or `self` access that refers to this module. */
+ private Expr getAnImmediateReferenceBase() {
+ resolveConstantReadAccess(result) = this
+ or
+ result.(SelfVariableAccess).getVariable() = this.getADeclaration().getModuleSelfVariable()
+ }
+
+ /** Gets a singleton class that augments this module object. */
+ SingletonClass getASingletonClass() { result.getValue() = this.getAnImmediateReferenceBase() }
+
+ /**
+ * Gets a singleton method on this module, either declared as a singleton method
+ * or an instance method on a singleton class.
+ */
+ MethodBase getASingletonMethod() {
+ result.(SingletonMethod).getObject() = this.getAnImmediateReferenceBase()
+ or
+ result = this.getASingletonClass().getAMethod().(Method)
+ }
+
+ /** Gets a constant or `self` access that refers to this module. */
+ Expr getAnImmediateReference() {
+ result = this.getAnImmediateReferenceBase()
+ or
+ result.(SelfVariableAccess).getVariable().getDeclaringScope() = this.getASingletonMethod()
+ }
}
/**
@@ -141,6 +168,13 @@ class ModuleBase extends BodyStmt, Scope, TModuleBase {
/** Gets the representation of the run-time value of this module or class. */
Module getModule() { none() }
+
+ /**
+ * Gets the `self` variable in the module-level scope.
+ *
+ * Does not include the `self` variable from any of the methods in the module.
+ */
+ SelfVariable getModuleSelfVariable() { result.getDeclaringScope() = this }
}
/**
From 65add15416651e375ab71c7cc75a540124a371c7 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Wed, 12 Oct 2022 11:48:46 +0200
Subject: [PATCH 049/249] Ruby: add getALocalUse()
This is the inverse of getALocalSource()
---
.../lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
index 7b56f2e6a93..25e23d8e0c4 100644
--- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
+++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
@@ -174,6 +174,13 @@ class LocalSourceNode extends Node {
*/
pragma[inline]
LocalSourceNode backtrack(TypeBackTracker t2, TypeBackTracker t) { t2 = t.step(result, this) }
+
+ /**
+ * Gets a node to which data may flow from this node in zero or
+ * more local data-flow steps.
+ */
+ pragma[inline]
+ Node getALocalUse() { hasLocalSource(result, this) }
}
/**
From ac4cac889f42109efceb8210582761b1d6667c96 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Wed, 12 Oct 2022 11:02:34 +0200
Subject: [PATCH 050/249] Ruby: add DataFlow::ModuleNode
sdf
---
.../ruby/dataflow/internal/DataFlowPublic.qll | 43 +++++++++++++++++++
1 file changed, 43 insertions(+)
diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
index 25e23d8e0c4..6d63397ad23 100644
--- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
+++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
@@ -566,3 +566,46 @@ abstract deprecated class BarrierGuard extends CfgNodes::ExprCfgNode {
result.asExpr() = this.getAMaybeGuardedCapturedDef().getARead()
}
}
+
+/**
+ * A representation of a run-time module or class.
+ *
+ * This is equivalent to the type `Ast::Module` but provides data-flow specific methods.
+ */
+class ModuleNode instanceof Module {
+ /** Gets a declaration of this module, if any. */
+ final ModuleBase getADeclaration() { result = super.getADeclaration() }
+
+ /** Gets the super class of this module, if any. */
+ final ModuleNode getSuperClass() { result = super.getSuperClass() }
+
+ /** Gets an immediate sub class of this module, if any. */
+ final ModuleNode getASubClass() { result = super.getASubClass() }
+
+ /** Gets a `prepend`ed module. */
+ final ModuleNode getAPrependedModule() { result = super.getAPrependedModule() }
+
+ /** Gets an `include`d module. */
+ final ModuleNode getAnIncludedModule() { result = super.getAnIncludedModule() }
+
+ /** Holds if this module is a class. */
+ predicate isClass() { super.isClass() }
+
+ /** Gets a textual representation of this module. */
+ final string toString() { result = super.toString() }
+
+ /**
+ * Gets the qualified name of this module, if any.
+ *
+ * Only modules that can be resolved will have a qualified name.
+ */
+ final string getQualifiedName() { result = super.getQualifiedName() }
+
+ /** Gets the location of this module. */
+ final Location getLocation() { result = super.getLocation() }
+
+ /** Gets a constant or `self` variable that refers to this module. */
+ LocalSourceNode getAnImmediateReference() {
+ result.asExpr().getExpr() = super.getAnImmediateReference()
+ }
+}
From 4c8e0a7648a6cb2ffbce812a65ae92887442d98e Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Mon, 24 Oct 2022 15:05:05 -0400
Subject: [PATCH 051/249] update qldoc of JavaSecurityKeyPairGenerator and
JavaSecurityAlgoParamGenerator
---
java/ql/lib/semmle/code/java/security/Encryption.qll | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/Encryption.qll b/java/ql/lib/semmle/code/java/security/Encryption.qll
index 208343da0cc..12c1a9a1c58 100644
--- a/java/ql/lib/semmle/code/java/security/Encryption.qll
+++ b/java/ql/lib/semmle/code/java/security/Encryption.qll
@@ -383,7 +383,7 @@ class JavaSecuritySignature extends JavaSecurityAlgoSpec {
override Expr getAlgoSpec() { result = this.(ConstructorCall).getArgument(0) }
}
-/** An instance of a `java.security.KeyPairGenerator`. */
+/** A call to the `getInstance` method declared in `java.security.KeyPairGenerator`. */
class JavaSecurityKeyPairGenerator extends JavaSecurityAlgoSpec {
JavaSecurityKeyPairGenerator() {
exists(Method m | m.getAReference() = this |
@@ -410,7 +410,7 @@ class AlgoParamGeneratorInitMethod extends Method {
}
}
-/** An instance of a `java.security.AlgorithmParameterGenerator`. */
+/** A call to the `getInstance` method declared in `java.security.AlgorithmParameterGenerator`. */
class JavaSecurityAlgoParamGenerator extends JavaSecurityAlgoSpec {
JavaSecurityAlgoParamGenerator() {
exists(Method m | m.getAReference() = this |
From 2ee23f004e0ee0c7c962e799dc4f548866b4eb64 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Mon, 24 Oct 2022 15:22:33 -0400
Subject: [PATCH 052/249] update qldoc for AlgorithmParameterSpec
---
java/ql/lib/semmle/code/java/security/Encryption.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/ql/lib/semmle/code/java/security/Encryption.qll b/java/ql/lib/semmle/code/java/security/Encryption.qll
index 12c1a9a1c58..042018d3e34 100644
--- a/java/ql/lib/semmle/code/java/security/Encryption.qll
+++ b/java/ql/lib/semmle/code/java/security/Encryption.qll
@@ -422,7 +422,7 @@ class JavaSecurityAlgoParamGenerator extends JavaSecurityAlgoSpec {
override Expr getAlgoSpec() { result = this.(MethodAccess).getArgument(0) }
}
-/** The Java interface `java.security.spec.AlgorithmParameterSpec` */
+/** An implementation of the `java.security.spec.AlgorithmParameterSpec` interface. */
abstract class AlgorithmParameterSpec extends RefType { }
/** The Java class `java.security.spec.ECGenParameterSpec`. */
From eb69b98dffd95802ef451ff3e93fb4ce9bc50a20 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Mon, 24 Oct 2022 15:28:31 -0400
Subject: [PATCH 053/249] remove separators
---
java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll | 4 ----
1 file changed, 4 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
index 41852e1aeda..823b95c31af 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
@@ -15,7 +15,6 @@ abstract class InsufficientKeySizeSink extends DataFlow::Node {
predicate hasState(DataFlow::FlowState state) { state instanceof DataFlow::FlowStateEmpty }
}
-// *********************************** SOURCES ***********************************
/** A source for an insufficient key size used in RSA, DSA, and DH algorithms. */
private class AsymmetricNonEcSource extends InsufficientKeySizeSource {
AsymmetricNonEcSource() { getNodeIntValue(this) < getMinAsymNonEcKeySize() }
@@ -46,7 +45,6 @@ private class SymmetricSource extends InsufficientKeySizeSource {
override predicate hasState(DataFlow::FlowState state) { state = getMinSymKeySize().toString() }
}
-// ************************** SOURCES HELPER PREDICATES **************************
/** Returns the minimum recommended key size for RSA, DSA, and DH algorithms. */
private int getMinAsymNonEcKeySize() { result = 2048 }
@@ -74,7 +72,6 @@ private int getEcKeySize(string algorithm) {
result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
}
-// ************************************ SINKS ************************************
/** A sink for an insufficient key size used in RSA, DSA, and DH algorithms. */
private class AsymmetricNonEcSink extends InsufficientKeySizeSink {
AsymmetricNonEcSink() {
@@ -122,7 +119,6 @@ private class SymmetricSink extends InsufficientKeySizeSink {
override predicate hasState(DataFlow::FlowState state) { state = getMinSymKeySize().toString() }
}
-// ********************** SINKS HELPER CLASSES & PREDICATES **********************
/** A call to a method that initializes a key generator. */
abstract private class KeyGenInitMethodAccess extends MethodAccess {
/** Gets the `keysize` argument of this call. */
From 8bc0a648637a91bd3c9a0e09516fb95eeba8a18c Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Mon, 24 Oct 2022 15:42:36 -0400
Subject: [PATCH 054/249] remove KeyGenInitMethodAccess class
---
.../code/java/security/InsufficientKeySize.qll | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
index 823b95c31af..e3e3c53dab0 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
@@ -119,26 +119,26 @@ private class SymmetricSink extends InsufficientKeySizeSink {
override predicate hasState(DataFlow::FlowState state) { state = getMinSymKeySize().toString() }
}
-/** A call to a method that initializes a key generator. */
-abstract private class KeyGenInitMethodAccess extends MethodAccess {
- /** Gets the `keysize` argument of this call. */
- Argument getKeySizeArg() { result = this.getArgument(0) }
-}
-
/**
* A call to the `initialize` method declared in `java.security.KeyPairGenerator`
* or to the `init` method declared in `java.security.AlgorithmParameterGenerator`.
*/
-private class AsymmetricInitMethodAccess extends KeyGenInitMethodAccess {
+private class AsymmetricInitMethodAccess extends MethodAccess {
AsymmetricInitMethodAccess() {
this.getMethod() instanceof KeyPairGeneratorInitMethod or
this.getMethod() instanceof AlgoParamGeneratorInitMethod
}
+
+ /** Gets the `keysize` argument of this call. */
+ Argument getKeySizeArg() { result = this.getArgument(0) }
}
/** A call to the `init` method declared in `javax.crypto.KeyGenerator`. */
-private class SymmetricInitMethodAccess extends KeyGenInitMethodAccess {
+private class SymmetricInitMethodAccess extends MethodAccess {
SymmetricInitMethodAccess() { this.getMethod() instanceof KeyGeneratorInitMethod }
+
+ /** Gets the `keysize` argument of this call. */
+ Argument getKeySizeArg() { result = this.getArgument(0) }
}
/** An instance of a generator that specifies an encryption algorithm. */
From 09829d7f7ae1070dcce6dae992de7b17997e5862 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Mon, 24 Oct 2022 15:49:41 -0400
Subject: [PATCH 055/249] simplify instanceof usage
---
java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
index e3e3c53dab0..222e4c73f07 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
@@ -161,9 +161,7 @@ private class AsymmetricKeyGenerator extends AlgoGeneratorObject {
}
/** An instance of a `javax.crypto.KeyGenerator`. */
-private class SymmetricKeyGenerator extends AlgoGeneratorObject {
- SymmetricKeyGenerator() { this instanceof JavaxCryptoKeyGenerator }
-
+private class SymmetricKeyGenerator extends AlgoGeneratorObject instanceof JavaxCryptoKeyGenerator {
override Expr getAlgoSpec() { result = this.getAlgoSpec() }
}
From d569f93e7846d3b21caae6f71463971dfb8236ca Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Mon, 24 Oct 2022 16:05:57 -0400
Subject: [PATCH 056/249] update getAlgoSpec
---
.../semmle/code/java/security/InsufficientKeySize.qll | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
index 222e4c73f07..3e304ad14ce 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
@@ -157,12 +157,18 @@ private class AsymmetricKeyGenerator extends AlgoGeneratorObject {
this instanceof JavaSecurityAlgoParamGenerator
}
- override Expr getAlgoSpec() { result = this.getAlgoSpec() }
+ override Expr getAlgoSpec() {
+ result =
+ [
+ this.(JavaSecurityKeyPairGenerator).getAlgoSpec(),
+ this.(JavaSecurityAlgoParamGenerator).getAlgoSpec()
+ ]
+ }
}
/** An instance of a `javax.crypto.KeyGenerator`. */
private class SymmetricKeyGenerator extends AlgoGeneratorObject instanceof JavaxCryptoKeyGenerator {
- override Expr getAlgoSpec() { result = this.getAlgoSpec() }
+ override Expr getAlgoSpec() { result = JavaxCryptoKeyGenerator.super.getAlgoSpec() }
}
/** An instance of an algorithm specification. */
From c742a09defcab8ef7bc4fee078ff11451a9777ed Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Mon, 24 Oct 2022 16:15:18 -0400
Subject: [PATCH 057/249] remove AlgoSpec class
---
.../code/java/security/InsufficientKeySize.qll | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
index 3e304ad14ce..c7ed75a84f3 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
@@ -171,21 +171,22 @@ private class SymmetricKeyGenerator extends AlgoGeneratorObject instanceof Javax
override Expr getAlgoSpec() { result = JavaxCryptoKeyGenerator.super.getAlgoSpec() }
}
-/** An instance of an algorithm specification. */
-abstract private class AlgoSpec extends ClassInstanceExpr {
- Argument getKeySizeArg() { result = this.getArgument(0) }
-}
-
/** An instance of an RSA, DSA, or DH algorithm specification. */
-private class AsymmetricNonEcSpec extends AlgoSpec {
+private class AsymmetricNonEcSpec extends ClassInstanceExpr {
AsymmetricNonEcSpec() {
this.getConstructedType() instanceof RsaKeyGenParameterSpec or
this.getConstructedType() instanceof DsaGenParameterSpec or
this.getConstructedType() instanceof DhGenParameterSpec
}
+
+ /** Gets the `keysize` argument of this instance. */
+ Argument getKeySizeArg() { result = this.getArgument(0) }
}
/** An instance of an elliptic curve (EC) algorithm specification. */
-private class AsymmetricEcSpec extends AlgoSpec {
+private class AsymmetricEcSpec extends ClassInstanceExpr {
AsymmetricEcSpec() { this.getConstructedType() instanceof EcGenParameterSpec }
+
+ /** Gets the `keysize` argument of this instance. */
+ Argument getKeySizeArg() { result = this.getArgument(0) }
}
From 1a1245343deb643219b8914c590e78a57d1fdf36 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Mon, 24 Oct 2022 17:09:24 -0400
Subject: [PATCH 058/249] remove getNodeIntValue
---
.../code/java/security/InsufficientKeySize.qll | 13 +++++--------
1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
index c7ed75a84f3..b7bebf7da11 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
@@ -17,7 +17,9 @@ abstract class InsufficientKeySizeSink extends DataFlow::Node {
/** A source for an insufficient key size used in RSA, DSA, and DH algorithms. */
private class AsymmetricNonEcSource extends InsufficientKeySizeSource {
- AsymmetricNonEcSource() { getNodeIntValue(this) < getMinAsymNonEcKeySize() }
+ AsymmetricNonEcSource() {
+ this.asExpr().(IntegerLiteral).getIntValue() < getMinAsymNonEcKeySize()
+ }
override predicate hasState(DataFlow::FlowState state) {
state = getMinAsymNonEcKeySize().toString()
@@ -27,7 +29,7 @@ private class AsymmetricNonEcSource extends InsufficientKeySizeSource {
/** A source for an insufficient key size used in elliptic curve (EC) algorithms. */
private class AsymmetricEcSource extends InsufficientKeySizeSource {
AsymmetricEcSource() {
- getNodeIntValue(this) < getMinAsymEcKeySize()
+ this.asExpr().(IntegerLiteral).getIntValue() < getMinAsymEcKeySize()
or
// the below is needed for cases when the key size is embedded in the curve name
getEcKeySize(this.asExpr().(StringLiteral).getValue()) < getMinAsymEcKeySize()
@@ -40,7 +42,7 @@ private class AsymmetricEcSource extends InsufficientKeySizeSource {
/** A source for an insufficient key size used in AES algorithms. */
private class SymmetricSource extends InsufficientKeySizeSource {
- SymmetricSource() { getNodeIntValue(this) < getMinSymKeySize() }
+ SymmetricSource() { this.asExpr().(IntegerLiteral).getIntValue() < getMinSymKeySize() }
override predicate hasState(DataFlow::FlowState state) { state = getMinSymKeySize().toString() }
}
@@ -54,11 +56,6 @@ private int getMinAsymEcKeySize() { result = 256 }
/** Returns the minimum recommended key size for AES algorithms. */
private int getMinSymKeySize() { result = 128 }
-/** Returns the integer value of a given DataFlow::Node. */
-private int getNodeIntValue(DataFlow::Node node) {
- result = node.asExpr().(IntegerLiteral).getIntValue()
-}
-
/** Returns the key size from an EC algorithm's curve name string */
bindingset[algorithm]
private int getEcKeySize(string algorithm) {
From 1e80fa118c08df3a13469354d154be8be7bccba9 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Tue, 25 Oct 2022 18:26:00 -0400
Subject: [PATCH 059/249] add modules
---
.../java/security/InsufficientKeySize.qll | 310 +++++++++---------
1 file changed, 159 insertions(+), 151 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
index b7bebf7da11..46124cd4128 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
@@ -15,175 +15,183 @@ abstract class InsufficientKeySizeSink extends DataFlow::Node {
predicate hasState(DataFlow::FlowState state) { state instanceof DataFlow::FlowStateEmpty }
}
-/** A source for an insufficient key size used in RSA, DSA, and DH algorithms. */
-private class AsymmetricNonEcSource extends InsufficientKeySizeSource {
- AsymmetricNonEcSource() {
- this.asExpr().(IntegerLiteral).getIntValue() < getMinAsymNonEcKeySize()
+private module Asymmetric {
+ private module NonEllipticCurve {
+ /** A source for an insufficient key size used in RSA, DSA, and DH algorithms. */
+ private class AsymmetricNonEcSource extends InsufficientKeySizeSource {
+ AsymmetricNonEcSource() {
+ this.asExpr().(IntegerLiteral).getIntValue() < getMinAsymNonEcKeySize()
+ }
+
+ override predicate hasState(DataFlow::FlowState state) {
+ state = getMinAsymNonEcKeySize().toString()
+ }
+ }
+
+ /** A sink for an insufficient key size used in RSA, DSA, and DH algorithms. */
+ private class AsymmetricNonEcSink extends InsufficientKeySizeSink {
+ AsymmetricNonEcSink() {
+ exists(AsymmetricInitMethodAccess ma, AsymmetricKeyGenerator kg |
+ kg.getAlgoName().matches(["RSA", "DSA", "DH"]) and
+ DataFlow::localExprFlow(kg, ma.getQualifier()) and
+ this.asExpr() = ma.getKeySizeArg()
+ )
+ or
+ exists(AsymmetricNonEcSpec spec | this.asExpr() = spec.getKeySizeArg())
+ }
+
+ override predicate hasState(DataFlow::FlowState state) {
+ state = getMinAsymNonEcKeySize().toString()
+ }
+ }
+
+ /** Returns the minimum recommended key size for RSA, DSA, and DH algorithms. */
+ private int getMinAsymNonEcKeySize() { result = 2048 }
+
+ /** An instance of an RSA, DSA, or DH algorithm specification. */
+ private class AsymmetricNonEcSpec extends ClassInstanceExpr {
+ AsymmetricNonEcSpec() {
+ this.getConstructedType() instanceof RsaKeyGenParameterSpec or
+ this.getConstructedType() instanceof DsaGenParameterSpec or
+ this.getConstructedType() instanceof DhGenParameterSpec
+ }
+
+ /** Gets the `keysize` argument of this instance. */
+ Argument getKeySizeArg() { result = this.getArgument(0) }
+ }
}
- override predicate hasState(DataFlow::FlowState state) {
- state = getMinAsymNonEcKeySize().toString()
+ private module EllipticCurve {
+ /** A source for an insufficient key size used in elliptic curve (EC) algorithms. */
+ private class AsymmetricEcSource extends InsufficientKeySizeSource {
+ AsymmetricEcSource() {
+ this.asExpr().(IntegerLiteral).getIntValue() < getMinAsymEcKeySize()
+ or
+ // the below is needed for cases when the key size is embedded in the curve name
+ getEcKeySize(this.asExpr().(StringLiteral).getValue()) < getMinAsymEcKeySize()
+ }
+
+ override predicate hasState(DataFlow::FlowState state) {
+ state = getMinAsymEcKeySize().toString()
+ }
+ }
+
+ /** A sink for an insufficient key size used in elliptic curve (EC) algorithms. */
+ private class AsymmetricEcSink extends InsufficientKeySizeSink {
+ AsymmetricEcSink() {
+ exists(AsymmetricInitMethodAccess ma, AsymmetricKeyGenerator kg |
+ kg.getAlgoName().matches("EC%") and
+ DataFlow::localExprFlow(kg, ma.getQualifier()) and
+ this.asExpr() = ma.getKeySizeArg()
+ )
+ or
+ exists(AsymmetricEcSpec s | this.asExpr() = s.getKeySizeArg())
+ }
+
+ override predicate hasState(DataFlow::FlowState state) {
+ state = getMinAsymEcKeySize().toString()
+ }
+ }
+
+ /** Returns the minimum recommended key size for elliptic curve (EC) algorithms. */
+ private int getMinAsymEcKeySize() { result = 256 }
+
+ /** Returns the key size from an EC algorithm's curve name string */
+ bindingset[algorithm]
+ private int getEcKeySize(string algorithm) {
+ algorithm.matches("sec%") and // specification such as "secp256r1"
+ result = algorithm.regexpCapture("sec[p|t](\\d+)[a-zA-Z].*", 1).toInt()
+ or
+ algorithm.matches("X9.62%") and //specification such as "X9.62 prime192v2"
+ result = algorithm.regexpCapture("X9\\.62 .*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
+ or
+ (algorithm.matches("prime%") or algorithm.matches("c2tnb%")) and //specification such as "prime192v2"
+ result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
+ }
+
+ /** An instance of an elliptic curve (EC) algorithm specification. */
+ private class AsymmetricEcSpec extends ClassInstanceExpr {
+ AsymmetricEcSpec() { this.getConstructedType() instanceof EcGenParameterSpec }
+
+ /** Gets the `keysize` argument of this instance. */
+ Argument getKeySizeArg() { result = this.getArgument(0) }
+ }
+ }
+
+ /**
+ * A call to the `initialize` method declared in `java.security.KeyPairGenerator`
+ * or to the `init` method declared in `java.security.AlgorithmParameterGenerator`.
+ */
+ private class AsymmetricInitMethodAccess extends MethodAccess {
+ AsymmetricInitMethodAccess() {
+ this.getMethod() instanceof KeyPairGeneratorInitMethod or
+ this.getMethod() instanceof AlgoParamGeneratorInitMethod
+ }
+
+ /** Gets the `keysize` argument of this call. */
+ Argument getKeySizeArg() { result = this.getArgument(0) }
+ }
+
+ /**
+ * An instance of a `java.security.KeyPairGenerator`
+ * or of a `java.security.AlgorithmParameterGenerator`.
+ */
+ private class AsymmetricKeyGenerator extends AlgoGeneratorObject {
+ AsymmetricKeyGenerator() {
+ this instanceof JavaSecurityKeyPairGenerator or
+ this instanceof JavaSecurityAlgoParamGenerator
+ }
+
+ override Expr getAlgoSpec() {
+ result =
+ [
+ this.(JavaSecurityKeyPairGenerator).getAlgoSpec(),
+ this.(JavaSecurityAlgoParamGenerator).getAlgoSpec()
+ ]
+ }
}
}
-/** A source for an insufficient key size used in elliptic curve (EC) algorithms. */
-private class AsymmetricEcSource extends InsufficientKeySizeSource {
- AsymmetricEcSource() {
- this.asExpr().(IntegerLiteral).getIntValue() < getMinAsymEcKeySize()
- or
- // the below is needed for cases when the key size is embedded in the curve name
- getEcKeySize(this.asExpr().(StringLiteral).getValue()) < getMinAsymEcKeySize()
+private module Symmetric {
+ /** A source for an insufficient key size used in AES algorithms. */
+ private class SymmetricSource extends InsufficientKeySizeSource {
+ SymmetricSource() { this.asExpr().(IntegerLiteral).getIntValue() < getMinSymKeySize() }
+
+ override predicate hasState(DataFlow::FlowState state) { state = getMinSymKeySize().toString() }
}
- override predicate hasState(DataFlow::FlowState state) {
- state = getMinAsymEcKeySize().toString()
- }
-}
+ /** A sink for an insufficient key size used in AES algorithms. */
+ private class SymmetricSink extends InsufficientKeySizeSink {
+ SymmetricSink() {
+ exists(SymmetricInitMethodAccess ma, SymmetricKeyGenerator kg |
+ kg.getAlgoName() = "AES" and
+ DataFlow::localExprFlow(kg, ma.getQualifier()) and
+ this.asExpr() = ma.getKeySizeArg()
+ )
+ }
-/** A source for an insufficient key size used in AES algorithms. */
-private class SymmetricSource extends InsufficientKeySizeSource {
- SymmetricSource() { this.asExpr().(IntegerLiteral).getIntValue() < getMinSymKeySize() }
-
- override predicate hasState(DataFlow::FlowState state) { state = getMinSymKeySize().toString() }
-}
-
-/** Returns the minimum recommended key size for RSA, DSA, and DH algorithms. */
-private int getMinAsymNonEcKeySize() { result = 2048 }
-
-/** Returns the minimum recommended key size for elliptic curve (EC) algorithms. */
-private int getMinAsymEcKeySize() { result = 256 }
-
-/** Returns the minimum recommended key size for AES algorithms. */
-private int getMinSymKeySize() { result = 128 }
-
-/** Returns the key size from an EC algorithm's curve name string */
-bindingset[algorithm]
-private int getEcKeySize(string algorithm) {
- algorithm.matches("sec%") and // specification such as "secp256r1"
- result = algorithm.regexpCapture("sec[p|t](\\d+)[a-zA-Z].*", 1).toInt()
- or
- algorithm.matches("X9.62%") and //specification such as "X9.62 prime192v2"
- result = algorithm.regexpCapture("X9\\.62 .*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
- or
- (algorithm.matches("prime%") or algorithm.matches("c2tnb%")) and //specification such as "prime192v2"
- result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
-}
-
-/** A sink for an insufficient key size used in RSA, DSA, and DH algorithms. */
-private class AsymmetricNonEcSink extends InsufficientKeySizeSink {
- AsymmetricNonEcSink() {
- exists(AsymmetricInitMethodAccess ma, AsymmetricKeyGenerator kg |
- kg.getAlgoName().matches(["RSA", "DSA", "DH"]) and
- DataFlow::localExprFlow(kg, ma.getQualifier()) and
- this.asExpr() = ma.getKeySizeArg()
- )
- or
- exists(AsymmetricNonEcSpec spec | this.asExpr() = spec.getKeySizeArg())
+ override predicate hasState(DataFlow::FlowState state) { state = getMinSymKeySize().toString() }
}
- override predicate hasState(DataFlow::FlowState state) {
- state = getMinAsymNonEcKeySize().toString()
- }
-}
+ /** Returns the minimum recommended key size for AES algorithms. */
+ private int getMinSymKeySize() { result = 128 }
-/** A sink for an insufficient key size used in elliptic curve (EC) algorithms. */
-private class AsymmetricEcSink extends InsufficientKeySizeSink {
- AsymmetricEcSink() {
- exists(AsymmetricInitMethodAccess ma, AsymmetricKeyGenerator kg |
- kg.getAlgoName().matches("EC%") and
- DataFlow::localExprFlow(kg, ma.getQualifier()) and
- this.asExpr() = ma.getKeySizeArg()
- )
- or
- exists(AsymmetricEcSpec s | this.asExpr() = s.getKeySizeArg())
+ /** A call to the `init` method declared in `javax.crypto.KeyGenerator`. */
+ private class SymmetricInitMethodAccess extends MethodAccess {
+ SymmetricInitMethodAccess() { this.getMethod() instanceof KeyGeneratorInitMethod }
+
+ /** Gets the `keysize` argument of this call. */
+ Argument getKeySizeArg() { result = this.getArgument(0) }
}
- override predicate hasState(DataFlow::FlowState state) {
- state = getMinAsymEcKeySize().toString()
+ /** An instance of a `javax.crypto.KeyGenerator`. */
+ private class SymmetricKeyGenerator extends AlgoGeneratorObject instanceof JavaxCryptoKeyGenerator {
+ override Expr getAlgoSpec() { result = JavaxCryptoKeyGenerator.super.getAlgoSpec() }
}
}
-/** A sink for an insufficient key size used in AES algorithms. */
-private class SymmetricSink extends InsufficientKeySizeSink {
- SymmetricSink() {
- exists(SymmetricInitMethodAccess ma, SymmetricKeyGenerator kg |
- kg.getAlgoName() = "AES" and
- DataFlow::localExprFlow(kg, ma.getQualifier()) and
- this.asExpr() = ma.getKeySizeArg()
- )
- }
-
- override predicate hasState(DataFlow::FlowState state) { state = getMinSymKeySize().toString() }
-}
-
-/**
- * A call to the `initialize` method declared in `java.security.KeyPairGenerator`
- * or to the `init` method declared in `java.security.AlgorithmParameterGenerator`.
- */
-private class AsymmetricInitMethodAccess extends MethodAccess {
- AsymmetricInitMethodAccess() {
- this.getMethod() instanceof KeyPairGeneratorInitMethod or
- this.getMethod() instanceof AlgoParamGeneratorInitMethod
- }
-
- /** Gets the `keysize` argument of this call. */
- Argument getKeySizeArg() { result = this.getArgument(0) }
-}
-
-/** A call to the `init` method declared in `javax.crypto.KeyGenerator`. */
-private class SymmetricInitMethodAccess extends MethodAccess {
- SymmetricInitMethodAccess() { this.getMethod() instanceof KeyGeneratorInitMethod }
-
- /** Gets the `keysize` argument of this call. */
- Argument getKeySizeArg() { result = this.getArgument(0) }
-}
-
/** An instance of a generator that specifies an encryption algorithm. */
abstract private class AlgoGeneratorObject extends CryptoAlgoSpec {
/** Returns an uppercase string representing the algorithm name specified by this generator object. */
string getAlgoName() { result = this.getAlgoSpec().(StringLiteral).getValue().toUpperCase() }
}
-
-/**
- * An instance of a `java.security.KeyPairGenerator`
- * or of a `java.security.AlgorithmParameterGenerator`.
- */
-private class AsymmetricKeyGenerator extends AlgoGeneratorObject {
- AsymmetricKeyGenerator() {
- this instanceof JavaSecurityKeyPairGenerator or
- this instanceof JavaSecurityAlgoParamGenerator
- }
-
- override Expr getAlgoSpec() {
- result =
- [
- this.(JavaSecurityKeyPairGenerator).getAlgoSpec(),
- this.(JavaSecurityAlgoParamGenerator).getAlgoSpec()
- ]
- }
-}
-
-/** An instance of a `javax.crypto.KeyGenerator`. */
-private class SymmetricKeyGenerator extends AlgoGeneratorObject instanceof JavaxCryptoKeyGenerator {
- override Expr getAlgoSpec() { result = JavaxCryptoKeyGenerator.super.getAlgoSpec() }
-}
-
-/** An instance of an RSA, DSA, or DH algorithm specification. */
-private class AsymmetricNonEcSpec extends ClassInstanceExpr {
- AsymmetricNonEcSpec() {
- this.getConstructedType() instanceof RsaKeyGenParameterSpec or
- this.getConstructedType() instanceof DsaGenParameterSpec or
- this.getConstructedType() instanceof DhGenParameterSpec
- }
-
- /** Gets the `keysize` argument of this instance. */
- Argument getKeySizeArg() { result = this.getArgument(0) }
-}
-
-/** An instance of an elliptic curve (EC) algorithm specification. */
-private class AsymmetricEcSpec extends ClassInstanceExpr {
- AsymmetricEcSpec() { this.getConstructedType() instanceof EcGenParameterSpec }
-
- /** Gets the `keysize` argument of this instance. */
- Argument getKeySizeArg() { result = this.getArgument(0) }
-}
From 1bfdfc954bb00c6b6c160dbfdf35472556d49614 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Wed, 26 Oct 2022 16:30:14 -0400
Subject: [PATCH 060/249] shorten class/predicate names
---
.../java/security/InsufficientKeySize.qll | 112 +++++++++---------
1 file changed, 53 insertions(+), 59 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
index 46124cd4128..68190758d4f 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
@@ -15,42 +15,38 @@ abstract class InsufficientKeySizeSink extends DataFlow::Node {
predicate hasState(DataFlow::FlowState state) { state instanceof DataFlow::FlowStateEmpty }
}
+/** Provides models for asymmetric cryptography. */
private module Asymmetric {
+ /** Provides models for non-elliptic-curve asymmetric cryptography. */
private module NonEllipticCurve {
/** A source for an insufficient key size used in RSA, DSA, and DH algorithms. */
- private class AsymmetricNonEcSource extends InsufficientKeySizeSource {
- AsymmetricNonEcSource() {
- this.asExpr().(IntegerLiteral).getIntValue() < getMinAsymNonEcKeySize()
- }
+ private class Source extends InsufficientKeySizeSource {
+ Source() { this.asExpr().(IntegerLiteral).getIntValue() < getMinKeySize() }
- override predicate hasState(DataFlow::FlowState state) {
- state = getMinAsymNonEcKeySize().toString()
- }
+ override predicate hasState(DataFlow::FlowState state) { state = getMinKeySize().toString() }
}
/** A sink for an insufficient key size used in RSA, DSA, and DH algorithms. */
- private class AsymmetricNonEcSink extends InsufficientKeySizeSink {
- AsymmetricNonEcSink() {
- exists(AsymmetricInitMethodAccess ma, AsymmetricKeyGenerator kg |
- kg.getAlgoName().matches(["RSA", "DSA", "DH"]) and
- DataFlow::localExprFlow(kg, ma.getQualifier()) and
- this.asExpr() = ma.getKeySizeArg()
+ private class Sink extends InsufficientKeySizeSink {
+ Sink() {
+ exists(KeyPairGenInit kpgInit, KeyPairGen kpg |
+ kpg.getAlgoName().matches(["RSA", "DSA", "DH"]) and
+ DataFlow::localExprFlow(kpg, kpgInit.getQualifier()) and
+ this.asExpr() = kpgInit.getKeySizeArg()
)
or
- exists(AsymmetricNonEcSpec spec | this.asExpr() = spec.getKeySizeArg())
+ exists(Spec spec | this.asExpr() = spec.getKeySizeArg())
}
- override predicate hasState(DataFlow::FlowState state) {
- state = getMinAsymNonEcKeySize().toString()
- }
+ override predicate hasState(DataFlow::FlowState state) { state = getMinKeySize().toString() }
}
/** Returns the minimum recommended key size for RSA, DSA, and DH algorithms. */
- private int getMinAsymNonEcKeySize() { result = 2048 }
+ private int getMinKeySize() { result = 2048 }
/** An instance of an RSA, DSA, or DH algorithm specification. */
- private class AsymmetricNonEcSpec extends ClassInstanceExpr {
- AsymmetricNonEcSpec() {
+ private class Spec extends ClassInstanceExpr {
+ Spec() {
this.getConstructedType() instanceof RsaKeyGenParameterSpec or
this.getConstructedType() instanceof DsaGenParameterSpec or
this.getConstructedType() instanceof DhGenParameterSpec
@@ -61,44 +57,41 @@ private module Asymmetric {
}
}
+ /** Provides models for elliptic-curve asymmetric cryptography. */
private module EllipticCurve {
/** A source for an insufficient key size used in elliptic curve (EC) algorithms. */
- private class AsymmetricEcSource extends InsufficientKeySizeSource {
- AsymmetricEcSource() {
- this.asExpr().(IntegerLiteral).getIntValue() < getMinAsymEcKeySize()
+ private class Source extends InsufficientKeySizeSource {
+ Source() {
+ this.asExpr().(IntegerLiteral).getIntValue() < getMinKeySize()
or
// the below is needed for cases when the key size is embedded in the curve name
- getEcKeySize(this.asExpr().(StringLiteral).getValue()) < getMinAsymEcKeySize()
+ getKeySize(this.asExpr().(StringLiteral).getValue()) < getMinKeySize()
}
- override predicate hasState(DataFlow::FlowState state) {
- state = getMinAsymEcKeySize().toString()
- }
+ override predicate hasState(DataFlow::FlowState state) { state = getMinKeySize().toString() }
}
/** A sink for an insufficient key size used in elliptic curve (EC) algorithms. */
- private class AsymmetricEcSink extends InsufficientKeySizeSink {
- AsymmetricEcSink() {
- exists(AsymmetricInitMethodAccess ma, AsymmetricKeyGenerator kg |
- kg.getAlgoName().matches("EC%") and
- DataFlow::localExprFlow(kg, ma.getQualifier()) and
- this.asExpr() = ma.getKeySizeArg()
+ private class Sink extends InsufficientKeySizeSink {
+ Sink() {
+ exists(KeyPairGenInit kpgInit, KeyPairGen kpg |
+ kpg.getAlgoName().matches("EC%") and
+ DataFlow::localExprFlow(kpg, kpgInit.getQualifier()) and
+ this.asExpr() = kpgInit.getKeySizeArg()
)
or
- exists(AsymmetricEcSpec s | this.asExpr() = s.getKeySizeArg())
+ exists(Spec s | this.asExpr() = s.getKeySizeArg())
}
- override predicate hasState(DataFlow::FlowState state) {
- state = getMinAsymEcKeySize().toString()
- }
+ override predicate hasState(DataFlow::FlowState state) { state = getMinKeySize().toString() }
}
/** Returns the minimum recommended key size for elliptic curve (EC) algorithms. */
- private int getMinAsymEcKeySize() { result = 256 }
+ private int getMinKeySize() { result = 256 }
/** Returns the key size from an EC algorithm's curve name string */
bindingset[algorithm]
- private int getEcKeySize(string algorithm) {
+ private int getKeySize(string algorithm) {
algorithm.matches("sec%") and // specification such as "secp256r1"
result = algorithm.regexpCapture("sec[p|t](\\d+)[a-zA-Z].*", 1).toInt()
or
@@ -110,8 +103,8 @@ private module Asymmetric {
}
/** An instance of an elliptic curve (EC) algorithm specification. */
- private class AsymmetricEcSpec extends ClassInstanceExpr {
- AsymmetricEcSpec() { this.getConstructedType() instanceof EcGenParameterSpec }
+ private class Spec extends ClassInstanceExpr {
+ Spec() { this.getConstructedType() instanceof EcGenParameterSpec }
/** Gets the `keysize` argument of this instance. */
Argument getKeySizeArg() { result = this.getArgument(0) }
@@ -122,8 +115,8 @@ private module Asymmetric {
* A call to the `initialize` method declared in `java.security.KeyPairGenerator`
* or to the `init` method declared in `java.security.AlgorithmParameterGenerator`.
*/
- private class AsymmetricInitMethodAccess extends MethodAccess {
- AsymmetricInitMethodAccess() {
+ private class KeyPairGenInit extends MethodAccess {
+ KeyPairGenInit() {
this.getMethod() instanceof KeyPairGeneratorInitMethod or
this.getMethod() instanceof AlgoParamGeneratorInitMethod
}
@@ -136,8 +129,8 @@ private module Asymmetric {
* An instance of a `java.security.KeyPairGenerator`
* or of a `java.security.AlgorithmParameterGenerator`.
*/
- private class AsymmetricKeyGenerator extends AlgoGeneratorObject {
- AsymmetricKeyGenerator() {
+ private class KeyPairGen extends GeneratorAlgoSpec {
+ KeyPairGen() {
this instanceof JavaSecurityKeyPairGenerator or
this instanceof JavaSecurityAlgoParamGenerator
}
@@ -152,46 +145,47 @@ private module Asymmetric {
}
}
+/** Provides models for symmetric cryptography. */
private module Symmetric {
/** A source for an insufficient key size used in AES algorithms. */
- private class SymmetricSource extends InsufficientKeySizeSource {
- SymmetricSource() { this.asExpr().(IntegerLiteral).getIntValue() < getMinSymKeySize() }
+ private class Source extends InsufficientKeySizeSource {
+ Source() { this.asExpr().(IntegerLiteral).getIntValue() < getMinKeySize() }
- override predicate hasState(DataFlow::FlowState state) { state = getMinSymKeySize().toString() }
+ override predicate hasState(DataFlow::FlowState state) { state = getMinKeySize().toString() }
}
/** A sink for an insufficient key size used in AES algorithms. */
- private class SymmetricSink extends InsufficientKeySizeSink {
- SymmetricSink() {
- exists(SymmetricInitMethodAccess ma, SymmetricKeyGenerator kg |
+ private class Sink extends InsufficientKeySizeSink {
+ Sink() {
+ exists(KeyGenInit kgInit, KeyGen kg |
kg.getAlgoName() = "AES" and
- DataFlow::localExprFlow(kg, ma.getQualifier()) and
- this.asExpr() = ma.getKeySizeArg()
+ DataFlow::localExprFlow(kg, kgInit.getQualifier()) and
+ this.asExpr() = kgInit.getKeySizeArg()
)
}
- override predicate hasState(DataFlow::FlowState state) { state = getMinSymKeySize().toString() }
+ override predicate hasState(DataFlow::FlowState state) { state = getMinKeySize().toString() }
}
/** Returns the minimum recommended key size for AES algorithms. */
- private int getMinSymKeySize() { result = 128 }
+ private int getMinKeySize() { result = 128 }
/** A call to the `init` method declared in `javax.crypto.KeyGenerator`. */
- private class SymmetricInitMethodAccess extends MethodAccess {
- SymmetricInitMethodAccess() { this.getMethod() instanceof KeyGeneratorInitMethod }
+ private class KeyGenInit extends MethodAccess {
+ KeyGenInit() { this.getMethod() instanceof KeyGeneratorInitMethod }
/** Gets the `keysize` argument of this call. */
Argument getKeySizeArg() { result = this.getArgument(0) }
}
/** An instance of a `javax.crypto.KeyGenerator`. */
- private class SymmetricKeyGenerator extends AlgoGeneratorObject instanceof JavaxCryptoKeyGenerator {
+ private class KeyGen extends GeneratorAlgoSpec instanceof JavaxCryptoKeyGenerator {
override Expr getAlgoSpec() { result = JavaxCryptoKeyGenerator.super.getAlgoSpec() }
}
}
/** An instance of a generator that specifies an encryption algorithm. */
-abstract private class AlgoGeneratorObject extends CryptoAlgoSpec {
+abstract private class GeneratorAlgoSpec extends CryptoAlgoSpec {
/** Returns an uppercase string representing the algorithm name specified by this generator object. */
string getAlgoName() { result = this.getAlgoSpec().(StringLiteral).getValue().toUpperCase() }
}
From 6f646be733495ad9c78e09e7ae1c55d1cae8580b Mon Sep 17 00:00:00 2001
From: Arthur Baars
Date: Tue, 18 Oct 2022 14:06:59 +0200
Subject: [PATCH 061/249] Ruby: document API graphs
---
.../codeql-for-ruby.rst | 3 +
.../using-api-graphs-in-ruby.rst | 183 ++++++++++++++++++
2 files changed, 186 insertions(+)
create mode 100644 docs/codeql/codeql-language-guides/using-api-graphs-in-ruby.rst
diff --git a/docs/codeql/codeql-language-guides/codeql-for-ruby.rst b/docs/codeql/codeql-language-guides/codeql-for-ruby.rst
index bfb29a012ef..7066c108200 100644
--- a/docs/codeql/codeql-language-guides/codeql-for-ruby.rst
+++ b/docs/codeql/codeql-language-guides/codeql-for-ruby.rst
@@ -10,9 +10,12 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
basic-query-for-ruby-code
codeql-library-for-ruby
+ using-api-graphs-in-ruby
- :doc:`Basic query for Ruby code `: Learn to write and run a simple CodeQL query using LGTM.
- :doc:`CodeQL library for Ruby `: When you're analyzing a Ruby program, you can make use of the large collection of classes in the CodeQL library for Ruby.
+- :doc:`Using API graphs in Ruby `: API graphs are a uniform interface for referring to functions, classes, and methods defined in external libraries.
+
.. include:: ../reusables/ruby-beta-note.rst
diff --git a/docs/codeql/codeql-language-guides/using-api-graphs-in-ruby.rst b/docs/codeql/codeql-language-guides/using-api-graphs-in-ruby.rst
new file mode 100644
index 00000000000..af3b1ecfdec
--- /dev/null
+++ b/docs/codeql/codeql-language-guides/using-api-graphs-in-ruby.rst
@@ -0,0 +1,183 @@
+.. _using-api-graphs-in-ruby:
+
+Using API graphs in Ruby
+==========================
+
+API graphs are a uniform interface for referring to functions, classes, and methods defined in
+external libraries.
+
+About this article
+------------------
+
+This article describes how to use API graphs to reference classes and functions defined in library
+code. You can use API graphs to conveniently refer to external library functions when defining things like
+remote flow sources.
+
+
+Module and class references
+---------------------------
+
+The most common entry point into the API graph will be the point where a toplevel module or class is
+accessed. For example, you can access the API graph node corresponding to the ``::Regexp`` class
+by using the ``API::getTopLevelMember`` method defined in the ``codeql.ruby.ApiGraphs`` module, as the
+following snippet demonstrates.
+
+.. code-block:: ql
+
+ import codeql.ruby.ApiGraphs
+
+ select API::getTopLevelMember("Regexp")
+
+This query selects the API graph nodes corresponding to references to the ``Regexp`` class. For nested
+modules and classes, you can use the ``getMember` method. For example the following query selects
+references to the ``Net::HTTP`` class.
+
+.. code-block:: ql
+
+ import codeql.ruby.ApiGraphs
+
+ select API::getTopLevelMember("Net").getMember("HTTP")
+
+Note that the given module name *must not* contain any ```::`` symbols. Thus, something like
+`API::getTopLevelMember("Net::HTTP")`` will not do what you expect. Instead, this should be decomposed
+into an access of the ``HTTP`` member of the API graph node for ``Net``, as in the example above.
+
+Calls and class instantiations
+------------------------------
+
+To track the calls of externally defined functions, you can use the ``getMethod`` method. The
+following snippet finds all calls of ``Regexp.compile``:
+
+.. code-block:: ql
+
+ import codeql.ruby.ApiGraphs
+
+ select API::getTopLevelMember("Regexp").getMethod("compile")
+
+The example above is for a call to a class method. Tracking calls to instance methods, is a two-step
+process, first you need to find instances of the class before you can find the calls
+to methods on those instances. The following snippet finds instantiations of the ``Regexp`` class:
+
+.. code-block:: ql
+
+ import codeql.ruby.ApiGraphs
+
+ select API::getTopLevelMember("Regexp").getInstance()
+
+Note that the ``getInstance`` method also includes subclasses. For example if there is a
+``class SpecialRegexp < Regexp`` then ``getInstance`` also finds ``SpecialRegexp.new``.
+
+The following snippet builds on the above to find calls of the ``Regexp#match?`` instance method:
+
+.. code-block:: ql
+
+ import codeql.ruby.ApiGraphs
+
+ select API::getTopLevelMember("Regexp").getInstance().getMethod("match?")
+
+Subclasses
+----------
+
+For many libraries, the main mode of usage is to extend one or more library classes. To track this
+in the API graph, you can use the ``getASubclass`` method to get the API graph node corresponding to
+all the immediate subclasses of this node. To find *all* subclasses, use ``*`` or ``+`` to apply the
+method repeatedly, as in ``getASubclass*``.
+
+Note that ``getASubclass`` does not account for any subclassing that takes place in library code
+that has not been extracted. Thus, it may be necessary to account for this in the models you write.
+For example, the ``ActionController::Base`` class has a predefined subclass ``Rails::ApplicationController``. To find
+all subclasses of ``ActionController::Base``, you must explicitly include the subclasses of ``Rails::ApplicationController`` as well.
+
+.. code-block:: ql
+
+ import codeql.ruby.ApiGraphs
+
+
+ API::Node actionController() {
+ result =
+ [
+ API::getTopLevelMember("ActionController").getMember("Base"),
+ API::getTopLevelMember("Rails").getMember("ApplicationController")
+ ].getASubclass*()
+ }
+
+ select actionController()
+
+
+Using the API graph in dataflow queries
+---------------------------------------
+
+Dataflow queries often search for points where data from external sources enters the code base
+as well as places where data leaves the code base. API graphs provide a convenient way to refer
+to external API components such as library functions and their inputs and outputs. API graph nodes
+cannot be used directly in dataflow queries they model entities that are defined externally,
+while dataflow nodes correspond to entities defined in the current code base. To brigde this gap
+the API node classes provide the ``asSource()`` and ``asSink()`` methods.
+
+The ``asSource()`` method is used to select dataflow nodes where a value from an external source
+enters the current code base. A typical example is the return value of a library function such as
+``File.read(path)``:
+
+.. code-block:: ql
+
+ import codeql.ruby.ApiGraphs
+
+ select API::getTopLevelMember("File").getMethod("read").getParameter(1).asSource()
+
+
+The ``asSink()`` method is used to select dataflow nodes where a value leaves the
+current code base and flows into an external library. For example the second parameter
+of the ``File.write(path, value)`` method.
+
+.. code-block:: ql
+
+ import codeql.ruby.ApiGraphs
+
+ select API::getTopLevelMember("File").getMethod("write").getParameter(1).asSink()
+
+A more complex example is a call to ``File.open`` with a block argument. This function creates a ```File`` instance
+and passes it to the supplied block. In this case the first parameter of the block is the place where an
+externally created value enters the code base, i.e. the ``|file|`` in the example below:
+
+.. code-block:: ruby
+
+ File.open("/my/file.txt", "w") { |file| file << "Hello world" }
+
+The following snippet finds parameters of blocks of ``File.open`` method calls:
+
+.. code-block:: ql
+
+ import codeql.ruby.ApiGraphs
+
+ select API::getTopLevelMember("File").getMethod("open").getBlock().getParameter(0).asSource()
+
+The following example is a dataflow query that that uses API graphs to find cases where data that
+is read flows into a call to ```File.write``.
+
+.. code-block:: ql
+
+ import codeql.ruby.DataFlow
+ import codeql.ruby.ApiGraphs
+
+ class Configuration extends DataFlow::Configuration {
+ Configuration() { this = "File read/write Configuration" }
+
+ override predicate isSource(DataFlow::Node source) {
+ source = API::getTopLevelMember("File").getMethod("read").getReturn().asSource()
+ }
+
+ override predicate isSink(DataFlow::Node sink) {
+ sink = API::getTopLevelMember("File").getMethod("write").getParameter(1).asSink()
+ }
+ }
+
+ from DataFlow::Node src, DataFlow::Node sink, Configuration config
+ where config.hasFlow(src, sink)
+ select src, "The data read here flows into a $@ call.", sink, "File.write"
+
+Further reading
+---------------
+
+
+.. include:: ../reusables/ruby-further-reading.rst
+.. include:: ../reusables/codeql-ref-tools-further-reading.rst
From b1da636be0cab23ce4fca168b77d730c022af52b Mon Sep 17 00:00:00 2001
From: Nick Rolfe
Date: Fri, 21 Oct 2022 15:11:43 +0100
Subject: [PATCH 062/249] Ruby: first draft of data flow docs
---
.../analyzing-data-flow-in-ruby.rst | 390 ++++++++++++++++++
.../codeql-for-ruby.rst | 2 +
2 files changed, 392 insertions(+)
create mode 100644 docs/codeql/codeql-language-guides/analyzing-data-flow-in-ruby.rst
diff --git a/docs/codeql/codeql-language-guides/analyzing-data-flow-in-ruby.rst b/docs/codeql/codeql-language-guides/analyzing-data-flow-in-ruby.rst
new file mode 100644
index 00000000000..feaa6415486
--- /dev/null
+++ b/docs/codeql/codeql-language-guides/analyzing-data-flow-in-ruby.rst
@@ -0,0 +1,390 @@
+.. _analyzing-data-flow-in-ruby:
+
+Analyzing data flow in Ruby
+=============================
+
+You can use CodeQL to track the flow of data through a Ruby program to places where the data is used.
+
+About this article
+------------------
+
+This article describes how data flow analysis is implemented in the CodeQL libraries for Ruby and includes examples to help you write your own data flow queries.
+The following sections describe how to use the libraries for local data flow, global data flow, and taint tracking.
+For a more general introduction to modeling data flow, see ":ref:`About data flow analysis `."
+
+Local data flow
+---------------
+
+Local data flow is data flow within a single method or callable. Local data flow is easier, faster, and more precise than global data flow, and is sufficient for many queries.
+
+Using local data flow
+~~~~~~~~~~~~~~~~~~~~~
+
+The local data flow library is in the module ``DataFlow`` and it defines the class ``Node``, representing any element through which data can flow.
+``Node``\ s are divided into expression nodes (``ExprNode``) and parameter nodes (``ParameterNode``).
+You can map between a data flow ``ParameterNode`` and its corresponding ``Parameter`` AST node using the ``asParameter`` member predicate.
+Meanwhile, the ``asExpr`` member predicate maps between a data flow ``ExprNode`` and its corresponding ``ExprCfgNode`` in the control-flow library.
+
+.. code-block:: ql
+
+ class Node {
+ /** Gets the expression corresponding to this node, if any. */
+ CfgNodes::ExprCfgNode asExpr() { ... }
+
+ /** Gets the parameter corresponding to this node, if any. */
+ Parameter asParameter() { ... }
+
+ ...
+ }
+
+You can also use the predicates ``exprNode`` and ``parameterNode``:
+
+.. code-block:: ql
+
+ /**
+ * Gets a node corresponding to expression `e`.
+ */
+ ExprNode exprNode(CfgNodes::ExprCfgNode e) { ... }
+
+ /**
+ * Gets the node corresponding to the value of parameter `p` at function entry.
+ */
+ ParameterNode parameterNode(Parameter p) { ... }
+
+Note that since ``asExpr`` and ``exprNode`` map between data-flow and control-flow nodes, you then need to call the ``getExpr`` member predicate on the control-flow node to map to the corresponding AST node,
+e.g. by writing ``node.asExpr().getExpr()``.
+Due to the control-flow graph being split, there can be multiple data-flow and control-flow nodes associated with a single expression AST node.
+
+The predicate ``localFlowStep(Node nodeFrom, Node nodeTo)`` holds if there is an immediate data flow edge from the node ``nodeFrom`` to the node ``nodeTo``.
+You can apply the predicate recursively, by using the ``+`` and ``*`` operators, or you can use the predefined recursive predicate ``localFlow``.
+
+For example, you can find flow from an expression ``source`` to an expression ``sink`` in zero or more local steps:
+
+.. code-block:: ql
+
+ DataFlow::localFlow(source, sink)
+
+Using local taint tracking
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Local taint tracking extends local data flow by including non-value-preserving flow steps.
+For example:
+
+.. code-block:: ruby
+
+ temp = x
+ y = temp + ", " + temp
+
+If ``x`` is a tainted string then ``y`` is also tainted.
+
+The local taint tracking library is in the module ``TaintTracking``.
+Like local data flow, a predicate ``localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo)`` holds if there is an immediate taint propagation edge from the node ``nodeFrom`` to the node ``nodeTo``.
+You can apply the predicate recursively, by using the ``+`` and ``*`` operators, or you can use the predefined recursive predicate ``localTaint``.
+
+For example, you can find taint propagation from an expression ``source`` to an expression ``sink`` in zero or more local steps:
+
+.. code-block:: ql
+
+ TaintTracking::localTaint(source, sink)
+
+
+Using local sources
+~~~~~~~~~~~~~~~~~~~
+
+When asking for local data flow or taint propagation between two expressions as above, you would normally constrain the expressions to be relevant to a certain investigation.
+The next section will give some concrete examples, but there is a more abstract concept that we should call out explicitly, namely that of a local source.
+
+A local source is a data-flow node with no local data flow into it.
+As such, it is a local origin of data flow, a place where a new value is created.
+This includes parameters (which only receive global data flow) and most expressions (because they are not value-preserving).
+Restricting attention to such local sources gives a much lighter and more performant data-flow graph and in most cases also a more suitable abstraction for the investigation of interest.
+The class ``LocalSourceNode`` represents data-flow nodes that are also local sources.
+It comes with a useful member predicate ``flowsTo(DataFlow::Node node)``, which holds if there is local data flow from the local source to ``node``.
+
+Examples
+~~~~~~~~
+
+This query finds the filename argument passed in each call to ``File.open``:
+
+.. code-block:: ql
+
+ import codeql.ruby.DataFlow
+ import codeql.ruby.ApiGraphs
+
+ from DataFlow::CallNode call
+ where call = API::getTopLevelMember("File").getAMethodCall("open")
+ select call.getArgument(0)
+
+Notice the use of the ``API`` module for referring to library methods.
+For more information, see ":doc:`Using API graphs in Ruby `."
+
+Unfortunately this will only give the expression in the argument, not the values which could be passed to it.
+So we use local data flow to find all expressions that flow into the argument:
+
+.. code-block:: ql
+
+ import codeql.ruby.DataFlow
+ import codeql.ruby.ApiGraphs
+
+ from DataFlow::CallNode call, DataFlow::ExprNode expr
+ where
+ call = API::getTopLevelMember("File").getAMethodCall("open") and
+ DataFlow::localFlow(expr, call.getArgument(0))
+ select call, expr
+
+Many expressions flow to the same call.
+If you run this query, you may notice that you get several data-flow nodes for an expression as it flows towards a call (notice repeated locations in the ``call`` column).
+We are mostly interested in the "first" of these, what might be called the local source for the file name.
+To restrict attention to such local sources, and to simultaneously make the analysis more performant, we have the QL class ``LocalSourceNode``.
+We could demand that ``expr`` is such a node:
+
+.. code-block:: ql
+
+ import codeql.ruby.DataFlow
+ import codeql.ruby.ApiGraphs
+
+ from DataFlow::CallNode call, DataFlow::ExprNode expr
+ where
+ call = API::getTopLevelMember("File").getAMethodCall("open") and
+ DataFlow::localFlow(expr, call.getArgument(0)) and
+ expr instanceof DataFlow::LocalSourceNode
+ select call, expr
+
+However, we could also enforce this by casting.
+That would allow us to use the member predicate ``flowsTo`` on ``LocalSourceNode`` like so:
+
+.. code-block:: ql
+
+ import codeql.ruby.DataFlow
+ import codeql.ruby.ApiGraphs
+
+ from DataFlow::CallNode call, DataFlow::ExprNode expr
+ where
+ call = API::getTopLevelMember("File").getAMethodCall("open") and
+ expr.(DataFlow::LocalSourceNode).flowsTo(call.getArgument(0))
+ select call, expr
+
+As an alternative, we can ask more directly that ``expr`` is a local source of the first argument, via the predicate ``getALocalSource``:
+
+.. code-block:: ql
+
+ import codeql.ruby.DataFlow
+ import codeql.ruby.ApiGraphs
+
+ from DataFlow::CallNode call, DataFlow::ExprNode expr
+ where
+ call = API::getTopLevelMember("File").getAMethodCall("open") and
+ expr = call.getArgument(0).getALocalSource()
+ select call, expr
+
+All these three queries give identical results.
+We now mostly have one expression per call.
+
+We may still have cases of more than one expression flowing to a call, but then they flow through different code paths (possibly due to control-flow splitting).
+
+We might want to make the source more specific, for example a parameter to a method or block.
+This query finds instances where a parameter is used as the name when opening a file:
+
+.. code-block:: ql
+
+ import codeql.ruby.DataFlow
+ import codeql.ruby.ApiGraphs
+
+ from DataFlow::CallNode call, DataFlow::ParameterNode p
+ where
+ call = API::getTopLevelMember("File").getAMethodCall("open") and
+ DataFlow::localFlow(p, call.getArgument(0))
+ select call, p
+
+Using the exact name supplied via the parameter may be too strict.
+If we want to know if the parameter influences the file name, we can use taint tracking instead of data flow.
+This query finds calls to ``File.open`` where the filename is derived from a parameter:
+
+.. code-block:: ql
+
+ import codeql.ruby.DataFlow
+ import codeql.ruby.TaintTracking
+ import codeql.ruby.ApiGraphs
+
+ from DataFlow::CallNode call, DataFlow::ParameterNode p
+ where
+ call = API::getTopLevelMember("File").getAMethodCall("open") and
+ TaintTracking::localTaint(p, call.getArgument(0))
+ select call, p
+
+Global data flow
+----------------
+
+Global data flow tracks data flow throughout the entire program, and is therefore more powerful than local data flow.
+However, global data flow is less precise than local data flow, and the analysis typically requires significantly more time and memory to perform.
+
+.. pull-quote:: Note
+
+ .. include:: ../reusables/path-problem.rst
+
+Using global data flow
+~~~~~~~~~~~~~~~~~~~~~~
+
+The global data flow library is used by extending the class ``DataFlow::Configuration``:
+
+.. code-block:: ql
+
+ import codeql.ruby.DataFlow
+
+ class MyDataFlowConfiguration extends DataFlow::Configuration {
+ MyDataFlowConfiguration() { this = "..." }
+
+ override predicate isSource(DataFlow::Node source) {
+ ...
+ }
+
+ override predicate isSink(DataFlow::Node sink) {
+ ...
+ }
+ }
+
+These predicates are defined in the configuration:
+
+- ``isSource`` - defines where data may flow from.
+- ``isSink`` - defines where data may flow to.
+- ``isBarrier`` - optionally, restricts the data flow.
+- ``isAdditionalFlowStep`` - optionally, adds additional flow steps.
+
+The characteristic predicate (``MyDataFlowConfiguration()``) defines the name of the configuration, so ``"..."`` must be replaced with a unique name (for instance the class name).
+
+The data flow analysis is performed using the predicate ``hasFlow(DataFlow::Node source, DataFlow::Node sink)``:
+
+.. code-block:: ql
+
+ from MyDataFlowConfiguation dataflow, DataFlow::Node source, DataFlow::Node sink
+ where dataflow.hasFlow(source, sink)
+ select source, "Dataflow to $@.", sink, sink.toString()
+
+Using global taint tracking
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Global taint tracking is to global data flow what local taint tracking is to local data flow.
+That is, global taint tracking extends global data flow with additional non-value-preserving steps.
+The global taint tracking library is used by extending the class ``TaintTracking::Configuration``:
+
+.. code-block:: ql
+
+ import codeql.ruby.DataFlow
+ import codeql.ruby.TaintTracking
+
+ class MyTaintTrackingConfiguration extends TaintTracking::Configuration {
+ MyTaintTrackingConfiguration() { this = "..." }
+
+ override predicate isSource(DataFlow::Node source) {
+ ...
+ }
+
+ override predicate isSink(DataFlow::Node sink) {
+ ...
+ }
+ }
+
+These predicates are defined in the configuration:
+
+- ``isSource`` - defines where taint may flow from.
+- ``isSink`` - defines where taint may flow to.
+- ``isSanitizer`` - optionally, restricts the taint flow.
+- ``isAdditionalTaintStep`` - optionally, adds additional taint steps.
+
+Similar to global data flow, the characteristic predicate (``MyTaintTrackingConfiguration()``) defines the unique name of the configuration and the taint analysis is performed using the predicate ``hasFlow(DataFlow::Node source, DataFlow::Node sink)``.
+
+Predefined sources and sinks
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The data flow library contains a number of predefined sources and sinks, providing a good starting point for defining data flow based security queries.
+
+- The class ``RemoteFlowSource`` (defined in module ``codeql.ruby.dataflow.RemoteFlowSources``) represents data flow from remote network inputs. This is useful for finding security problems in networked services.
+- The library ``Concepts`` (defined in module ``codeql.ruby.Concepts``) contains several subclasses of ``DataFlow::Node`` that are security relevant, such as ``FileSystemAccess`` and ``SqlExecution``.
+
+For global flow, it is also useful to restrict sources to instances of ``LocalSourceNode``.
+The predefined sources generally do that.
+
+Class hierarchy
+~~~~~~~~~~~~~~~
+
+- ``DataFlow::Configuration`` - base class for custom global data flow analysis.
+- ``DataFlow::Node`` - an element behaving as a data-flow node.
+
+ - ``DataFlow::CfgNode`` - a control-flow node behaving as a data-flow node.
+
+ - ``DataFlow::ExprNode`` - an expression behaving as a data-flow node.
+ - ``DataFlow::ParameterNode`` - a parameter data-flow node representing the value of a parameter at method/block entry.
+
+ - ``RemoteFlowSource`` - data flow from network/remote input.
+ - ``Concepts::SystemCommandExecution`` - a data-flow node that executes an operating system command, for instance by spawning a new process.
+ - ``Concepts::FileSystemAccess`` - a data-flow node that performs a file system access, including reading and writing data, creating and deleting files and folders, checking and updating permissions, and so on.
+ - ``Concepts::Path::PathNormalization`` - a data-flow node that performs path normalization. This is often needed in order to safely access paths.
+ - ``Concepts::CodeExecution`` - a data-flow node that dynamically executes Python code.
+ - ``Concepts::SqlExecution`` - a data-flow node that executes SQL statements.
+ - ``Concepts::HTTP::Server::RouteSetup`` - a data-flow node that sets up a route on a server.
+ - ``Concepts::HTTP::Server::HttpResponse`` - a data-flow node that creates an HTTP response on a server.
+
+- ``TaintTracking::Configuration`` - base class for custom global taint tracking analysis.
+
+Examples
+~~~~~~~~
+
+This query shows a data flow configuration that uses all network input as data sources:
+
+.. code-block:: ql
+
+ import codeql.ruby.DataFlow
+ import codeql.ruby.TaintTracking
+ import codeql.ruby.Concepts
+ import codeql.ruby.dataflow.RemoteFlowSources
+
+ class RemoteToFileConfiguration extends TaintTracking::Configuration {
+ RemoteToFileConfiguration() { this = "RemoteToFileConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
+
+ override predicate isSink(DataFlow::Node sink) {
+ sink = any(FileSystemAccess fa).getAPathArgument()
+ }
+ }
+
+ from DataFlow::Node input, DataFlow::Node fileAccess, RemoteToFileConfiguration config
+ where config.hasFlow(input, fileAccess)
+ select fileAccess, "This file access uses data from $@.", input, "user-controllable input."
+
+This data flow configuration tracks data flow from environment variables to opening files:
+
+.. code-block:: ql
+
+ import codeql.ruby.DataFlow
+ import codeql.ruby.controlflow.CfgNodes
+ import codeql.ruby.ApiGraphs
+
+ class EnvironmentToFileConfiguration extends DataFlow::Configuration {
+ EnvironmentToFileConfiguration() { this = "EnvironmentToFileConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) {
+ exists(ExprNodes::ConstantReadAccessCfgNode env |
+ env.getExpr().getName() = "ENV" and
+ env = source.asExpr().(ExprNodes::ElementReferenceCfgNode).getReceiver()
+ )
+ }
+
+ override predicate isSink(DataFlow::Node sink) {
+ sink = API::getTopLevelMember("File").getAMethodCall("open").getArgument(0)
+ }
+ }
+
+ from EnvironmentToFileConfiguration config, DataFlow::Node environment, DataFlow::Node fileOpen
+ where config.hasFlow(environment, fileOpen)
+ select fileOpen, "This call to 'File.open' uses data from $@.", environment,
+ "an environment variable"
+
+Further reading
+---------------
+
+- ":ref:`Exploring data flow with path queries `"
+
+
+.. include:: ../reusables/ruby-further-reading.rst
+.. include:: ../reusables/codeql-ref-tools-further-reading.rst
diff --git a/docs/codeql/codeql-language-guides/codeql-for-ruby.rst b/docs/codeql/codeql-language-guides/codeql-for-ruby.rst
index bfb29a012ef..8e2dfe267e3 100644
--- a/docs/codeql/codeql-language-guides/codeql-for-ruby.rst
+++ b/docs/codeql/codeql-language-guides/codeql-for-ruby.rst
@@ -15,4 +15,6 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
- :doc:`CodeQL library for Ruby `: When you're analyzing a Ruby program, you can make use of the large collection of classes in the CodeQL library for Ruby.
+- :doc:`Analyzing data flow in Ruby `: You can use CodeQL to track the flow of data through a Ruby program to places where the data is used.
+
.. include:: ../reusables/ruby-beta-note.rst
From 65f74741103383fdd8e16ebb2c5e4d248e0e7c6c Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Thu, 27 Oct 2022 16:44:03 -0400
Subject: [PATCH 063/249] simplify algorithm.matches
---
java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
index 68190758d4f..256cbef022b 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
@@ -95,10 +95,10 @@ private module Asymmetric {
algorithm.matches("sec%") and // specification such as "secp256r1"
result = algorithm.regexpCapture("sec[p|t](\\d+)[a-zA-Z].*", 1).toInt()
or
- algorithm.matches("X9.62%") and //specification such as "X9.62 prime192v2"
+ algorithm.matches("X9.62%") and // specification such as "X9.62 prime192v2"
result = algorithm.regexpCapture("X9\\.62 .*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
or
- (algorithm.matches("prime%") or algorithm.matches("c2tnb%")) and //specification such as "prime192v2"
+ algorithm.matches(["prime%", "c2tnb%"]) and // specification such as "prime192v2"
result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
}
From f40eefce57fda15bdf03953a7d6fcda89070b040 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Thu, 27 Oct 2022 17:11:07 -0400
Subject: [PATCH 064/249] use CompileTimeConstantExpr instead of StringLiteral
---
.../java/security/InsufficientKeySize.qll | 4 +-
.../CWE-326/InsufficientKeySizeTest.java | 38 +++++++++++++++++++
2 files changed, 41 insertions(+), 1 deletion(-)
diff --git a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
index 256cbef022b..e09bffca8e1 100644
--- a/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
+++ b/java/ql/lib/semmle/code/java/security/InsufficientKeySize.qll
@@ -187,5 +187,7 @@ private module Symmetric {
/** An instance of a generator that specifies an encryption algorithm. */
abstract private class GeneratorAlgoSpec extends CryptoAlgoSpec {
/** Returns an uppercase string representing the algorithm name specified by this generator object. */
- string getAlgoName() { result = this.getAlgoSpec().(StringLiteral).getValue().toUpperCase() }
+ string getAlgoName() {
+ result = this.getAlgoSpec().(CompileTimeConstantExpr).getStringValue().toUpperCase()
+ }
}
diff --git a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
index e356f35d998..6f0d1f7115c 100644
--- a/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
+++ b/java/ql/test/query-tests/security/CWE-326/InsufficientKeySizeTest.java
@@ -33,6 +33,15 @@ public class InsufficientKeySizeTest {
KeyGenerator keyGen5 = KeyGenerator.getInstance("AES"); // MISSING: test KeyGenerator variable as argument
testSymmetricVariable(size2, keyGen5); // test with variable as key size
testSymmetricInt(64); // test with int literal as key size
+
+ /* Test with variable as algo name argument in `getInstance` method. */
+ final String algoName1 = "AES"; // compile-time constant
+ KeyGenerator keyGen6 = KeyGenerator.getInstance(algoName1);
+ keyGen6.init(64); // $ hasInsufficientKeySize
+
+ String algoName2 = "AES"; // not a compile-time constant
+ KeyGenerator keyGen7 = KeyGenerator.getInstance(algoName2);
+ keyGen7.init(64); // $ MISSING: hasInsufficientKeySize
}
// RSA (Asymmetric): minimum recommended key size is 2048
@@ -70,6 +79,15 @@ public class InsufficientKeySizeTest {
/* Test getting key size as return value of another method */
KeyPairGenerator keyPairGen8 = KeyPairGenerator.getInstance("RSA");
keyPairGen8.initialize(getRSAKeySize()); // $ hasInsufficientKeySize
+
+ /* Test with variable as algo name argument in `getInstance` method. */
+ final String algoName1 = "RSA"; // compile-time constant
+ KeyPairGenerator keyPairGen9 = KeyPairGenerator.getInstance(algoName1);
+ keyPairGen9.initialize(1024); // $ hasInsufficientKeySize
+
+ String algoName2 = "RSA"; // not a compile-time constant
+ KeyPairGenerator keyPairGen10 = KeyPairGenerator.getInstance(algoName2);
+ keyPairGen10.initialize(1024); // $ MISSING: hasInsufficientKeySize
}
// DSA (Asymmetric): minimum recommended key size is 2048
@@ -92,6 +110,15 @@ public class InsufficientKeySizeTest {
/* Test `AlgorithmParameterGenerator` */
AlgorithmParameterGenerator paramGen = AlgorithmParameterGenerator.getInstance("DSA");
paramGen.init(1024); // $ hasInsufficientKeySize
+
+ /* Test with variable as algo name argument in `getInstance` method. */
+ final String algoName1 = "DSA"; // compile-time constant
+ AlgorithmParameterGenerator paramGen1 = AlgorithmParameterGenerator.getInstance(algoName1);
+ paramGen1.init(1024); // $ hasInsufficientKeySize
+
+ String algoName2 = "DSA"; // not a compile-time constant
+ AlgorithmParameterGenerator paramGen2 = AlgorithmParameterGenerator.getInstance(algoName2);
+ paramGen2.init(1024); // $ MISSING: hasInsufficientKeySize
}
// DH (Asymmetric): minimum recommended key size is 2048
@@ -173,6 +200,17 @@ public class InsufficientKeySizeTest {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("EC"); // MISSING: test KeyGenerator variable as argument
testAsymmetricEcIntVariable(size, keyPairGen); // test with variable as key size
testAsymmetricEcIntLiteral(128); // test with int literal as key size
+
+ /* Test with variable as curve name argument in `ECGenParameterSpec` constructor. */
+ final String curveName1 = "secp112r1"; // compile-time constant
+ KeyPairGenerator keyPairGen16 = KeyPairGenerator.getInstance("EC");
+ ECGenParameterSpec ecSpec11 = new ECGenParameterSpec(curveName1); // $ hasInsufficientKeySize
+ keyPairGen16.initialize(ecSpec11);
+
+ String curveName2 = "secp112r1"; // not a compile-time constant
+ KeyPairGenerator keyPairGen17 = KeyPairGenerator.getInstance("EC");
+ ECGenParameterSpec ecSpec12 = new ECGenParameterSpec(curveName2); // $ hasInsufficientKeySize
+ keyPairGen17.initialize(ecSpec12);
}
}
From bb9205226a6d012eafafbe2478c00cee165b621d Mon Sep 17 00:00:00 2001
From: Alex Ford
Date: Fri, 28 Oct 2022 13:36:45 +0100
Subject: [PATCH 065/249] Ruby: fix whitespace in basic query doc table
---
.../codeql/codeql-language-guides/basic-query-for-ruby-code.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/codeql/codeql-language-guides/basic-query-for-ruby-code.rst b/docs/codeql/codeql-language-guides/basic-query-for-ruby-code.rst
index 1881dfc71a7..4acc85e6a85 100644
--- a/docs/codeql/codeql-language-guides/basic-query-for-ruby-code.rst
+++ b/docs/codeql/codeql-language-guides/basic-query-for-ruby-code.rst
@@ -80,7 +80,7 @@ After the initial ``import`` statement, this simple query comprises three parts
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+
| Query part | Purpose | Details |
+===============================================================+===================================================================================================================+========================================================================================================================+
-| ``import codeql.ruby.AST`` | Imports the standard CodeQL AST libraries for Ruby. | Every query begins with one or more ``import`` statements. |
+| ``import codeql.ruby.AST`` | Imports the standard CodeQL AST libraries for Ruby. | Every query begins with one or more ``import`` statements. |
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+
| ``from IfExpr ifexpr`` | Defines the variables for the query. | We use: an ``IfExpr`` variable for ``if`` expressions. |
| | Declarations are of the form: | |
From 8976ba5583a519afb031334656f6479d2d55d5f9 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Wed, 12 Oct 2022 13:09:21 +0200
Subject: [PATCH 066/249] Ruby: Add CallableNode, MethodNode, and accessors
---
ruby/ql/lib/codeql/ruby/ast/Module.qll | 32 +-
.../ruby/dataflow/internal/DataFlowPublic.qll | 295 +++++++++++++++++-
2 files changed, 315 insertions(+), 12 deletions(-)
diff --git a/ruby/ql/lib/codeql/ruby/ast/Module.qll b/ruby/ql/lib/codeql/ruby/ast/Module.qll
index e13506d2eb9..c11b42c4bf6 100644
--- a/ruby/ql/lib/codeql/ruby/ast/Module.qll
+++ b/ruby/ql/lib/codeql/ruby/ast/Module.qll
@@ -77,18 +77,46 @@ class Module extends TModule {
/**
* Gets a singleton method on this module, either declared as a singleton method
* or an instance method on a singleton class.
+ *
+ * Does not take inheritance into account.
*/
- MethodBase getASingletonMethod() {
+ MethodBase getAnOwnSingletonMethod() {
result.(SingletonMethod).getObject() = this.getAnImmediateReferenceBase()
or
result = this.getASingletonClass().getAMethod().(Method)
}
+ /**
+ * Gets an instance method named `name` declared in this module.
+ *
+ * Does not take inheritance into account.
+ */
+ Method getOwnInstanceMethod(string name) { result = this.getADeclaration().getMethod(name) }
+
+ /**
+ * Gets an instance method declared in this module.
+ *
+ * Does not take inheritance into account.
+ */
+ Method getAnOwnInstanceMethod() { result = this.getADeclaration().getMethod(_) }
+
+ /**
+ * Gets the instance method named `name` available in this module, including methods inherited
+ * from ancestors.
+ */
+ Method getInstanceMethod(string name) { result = lookupMethod(this, name) }
+
+ /**
+ * Gets an instance method available in this module, including methods inherited
+ * from ancestors.
+ */
+ Method getAnInstanceMethod() { result = lookupMethod(this, _) }
+
/** Gets a constant or `self` access that refers to this module. */
Expr getAnImmediateReference() {
result = this.getAnImmediateReferenceBase()
or
- result.(SelfVariableAccess).getVariable().getDeclaringScope() = this.getASingletonMethod()
+ result.(SelfVariableAccess).getVariable().getDeclaringScope() = this.getAnOwnSingletonMethod()
}
}
diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
index 6d63397ad23..50ec56019e8 100644
--- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
+++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
@@ -54,6 +54,32 @@ class Node extends TNode {
* Gets a data flow node to which data may flow from this node in one local step.
*/
Node getASuccessor() { localFlowStep(this, result) }
+
+ /** Gets the constant value of this expression, if any. */
+ ConstantValue getConstantValue() { result = asExpr().getExpr().getConstantValue() }
+
+ /**
+ * Gets the callable corresponding to this block, lambda expression, or call to `proc` or `lambda`.
+ *
+ * For example, gets the callable in each of the following cases:
+ * ```rb
+ * { |x| x } # block expression
+ * ->(x) { x } # lambda expression
+ * proc { |x| x } # call to 'proc'
+ * lambda { |x| x } # call to 'lambda'
+ * ```
+ */
+ pragma[noinline]
+ CallableNode asCallable() {
+ result = this
+ or
+ exists(CallNode call |
+ call.getReceiver().asExpr().getExpr() instanceof SelfVariableAccess and
+ call.getMethodName() = ["proc", "lambda"] and
+ call.getBlock() = result and
+ this = call
+ )
+ }
}
/** A data-flow node corresponding to a call in the control-flow graph. */
@@ -181,6 +207,12 @@ class LocalSourceNode extends Node {
*/
pragma[inline]
Node getALocalUse() { hasLocalSource(result, this) }
+
+ /** Gets a method call where this node flows to the receiver. */
+ CallNode getAMethodCall() { Cached::hasMethodCall(this, result, _) }
+
+ /** Gets a call to a method named `name`, where this node flows to the receiver. */
+ CallNode getAMethodCall(string name) { Cached::hasMethodCall(this, result, name) }
}
/**
@@ -200,18 +232,38 @@ class PostUpdateNode extends Node instanceof PostUpdateNodeImpl {
}
cached
-private predicate hasLocalSource(Node sink, Node source) {
- // Declaring `source` to be a `SourceNode` currently causes a redundant check in the
- // recursive case, so instead we check it explicitly here.
- source = sink and
- source instanceof LocalSourceNode
- or
- exists(Node mid |
- hasLocalSource(mid, source) and
- localFlowStepTypeTracker(mid, sink)
- )
+private module Cached {
+ cached
+ predicate hasLocalSource(Node sink, Node source) {
+ // Declaring `source` to be a `SourceNode` currently causes a redundant check in the
+ // recursive case, so instead we check it explicitly here.
+ source = sink and
+ source instanceof LocalSourceNode
+ or
+ exists(Node mid |
+ hasLocalSource(mid, source) and
+ localFlowStepTypeTracker(mid, sink)
+ )
+ }
+
+ cached
+ predicate hasMethodCall(LocalSourceNode source, CallNode call, string name) {
+ source.flowsTo(call.getReceiver()) and
+ call.getMethodName() = name
+ }
+
+ cached
+ predicate hasYieldCall(BlockParameterNode block, CallNode yield) {
+ exists(MethodBase method, YieldCall call |
+ block.getMethod() = method and
+ call.getEnclosingMethod() = method and
+ yield.asExpr().getExpr() = call
+ )
+ }
}
+private import Cached
+
/** Gets a node corresponding to expression `e`. */
ExprNode exprNode(CfgNodes::ExprCfgNode e) { result.getExprNode() = e }
@@ -604,8 +656,231 @@ class ModuleNode instanceof Module {
/** Gets the location of this module. */
final Location getLocation() { result = super.getLocation() }
+ /**
+ * Gets `self` in a declaration of this module.
+ *
+ * This only gets `self` at the module level, not inside any (singleton) method.
+ */
+ LocalSourceNode getModuleLevelSelf() {
+ result.(SsaDefinitionNode).getVariable() = super.getADeclaration().getModuleSelfVariable()
+ }
+
+ /**
+ * Gets `self` in the module declaration or in one of its singleton methods.
+ *
+ * Does not take inheritance into account.
+ */
+ LocalSourceNode getAnOwnModuleSelf() {
+ result = this.getModuleLevelSelf()
+ or
+ result = this.getAnOwnSingletonMethod().getSelfParameter()
+ }
+
+ /**
+ * Gets a call to method `name` on `self` in the module-level scope of this module.
+ *
+ * For example,
+ * ```rb
+ * module M
+ * include A # getAModuleLevelCall("include")
+ * foo :bar # getAModuleLevelCall("foo")
+ * end
+ * ```
+ */
+ CallNode getAModuleLevelCall(string name) {
+ result = this.getModuleLevelSelf().getAMethodCall(name)
+ }
+
/** Gets a constant or `self` variable that refers to this module. */
LocalSourceNode getAnImmediateReference() {
result.asExpr().getExpr() = super.getAnImmediateReference()
}
+
+ /**
+ * Gets a singleton method declared in this module (or in a singleton class
+ * augmenting this module).
+ *
+ * Does not take inheritance into account.
+ */
+ MethodNode getAnOwnSingletonMethod() { result.asMethod() = super.getAnOwnSingletonMethod() }
+
+ /**
+ * Gets the singleton method named `name` declared in this module (or in a singleton class
+ * augmenting this module).
+ *
+ * Does not take inheritance into account.
+ */
+ MethodNode getOwnSingletonMethod(string name) {
+ result = this.getAnOwnSingletonMethod() and
+ result.getMethodName() = name
+ }
+
+ /**
+ * Gets an instance method declared in this module.
+ *
+ * Does not take inheritance into account.
+ */
+ MethodNode getAnOwnInstanceMethod() {
+ result.asMethod() = this.getADeclaration().getAMethod().(Method)
+ }
+
+ /**
+ * Gets an instance method named `name` declared in this module.
+ *
+ * Does not take inheritance into account.
+ */
+ MethodNode getOwnInstanceMethod(string name) {
+ result = this.getAnOwnInstanceMethod() and
+ result.getMethodName() = name
+ }
+
+ /**
+ * Gets the `self` parameter of an instance method declared in this module.
+ *
+ * Does not take inheritance into account.
+ */
+ ParameterNode getAnOwnInstanceSelf() {
+ result = TSelfParameterNode(this.getAnOwnInstanceMethod().asMethod())
+ }
+
+ /**
+ * Gets the `self` parameter of an instance method available in this module,
+ * including those inherited from ancestors.
+ */
+ ParameterNode getAnInstanceSelf() {
+ result = TSelfParameterNode(this.getAnInstanceMethod().asMethod())
+ }
+
+ private InstanceVariableAccess getAnOwnInstanceVariableAccess(string name) {
+ exists(InstanceVariable v |
+ v.getDeclaringScope() = this.getADeclaration() and
+ v.getName() = name and
+ result.getVariable() = v
+ )
+ }
+
+ /**
+ * Gets an access to the instance variable `name` in this module.
+ *
+ * Does not take inheritance into account.
+ */
+ LocalSourceNode getAnOwnInstanceVariableRead(string name) {
+ result.asExpr().getExpr() =
+ this.getAnOwnInstanceVariableAccess(name).(InstanceVariableReadAccess)
+ }
+
+ /**
+ * Gets the right-hand side of an assignment to the instance variable `name` in this module.
+ *
+ * Does not take inheritance into account.
+ */
+ Node getAnOwnInstanceVariableWriteValue(string name) {
+ exists(Assignment assignment |
+ assignment.getLeftOperand() = this.getAnOwnInstanceVariableAccess(name) and
+ result.asExpr().getExpr() = assignment.getRightOperand()
+ )
+ }
+
+ /**
+ * Gets the instance method named `name` available in this module, including methods inherited
+ * from ancestors.
+ */
+ MethodNode getInstanceMethod(string name) {
+ result.asCallableAstNode() = super.getInstanceMethod(name)
+ }
+
+ /**
+ * Gets an instance method named available in this module, including methods inherited
+ * from ancestors.
+ */
+ MethodNode getAnInstanceMethod() { result = this.getInstanceMethod(_) }
+}
+
+/**
+ * A representation of a run-time class.
+ */
+class ClassNode extends ModuleNode {
+ ClassNode() { isClass() }
+}
+
+/**
+ * A data flow node corresponding to a method, block, or lambda expression.
+ */
+class CallableNode extends ExprNode {
+ private Callable callable;
+
+ CallableNode() { this.asExpr().getExpr() = callable }
+
+ /** Gets the underlying AST node as a `Callable`. */
+ Callable asCallableAstNode() { result = callable }
+
+ private ParameterPosition getParameterPosition(ParameterNodeImpl node) {
+ node.isSourceParameterOf(callable, result)
+ }
+
+ /** Gets the `n`th positional parameter. */
+ ParameterNode getParameter(int n) { getParameterPosition(result).isPositional(n) }
+
+ /** Gets the keyword parameter of the given name. */
+ ParameterNode getKeywordParameter(string name) { getParameterPosition(result).isKeyword(name) }
+
+ /** Gets the `self` parameter of this callable, if any. */
+ ParameterNode getSelfParameter() { getParameterPosition(result).isSelf() }
+
+ /**
+ * Gets the `hash-splat` parameter. This is a synthetic parameter holding
+ * a hash object with entries for each keyword argument passed to the function.
+ */
+ ParameterNode getHashSplatParameter() { getParameterPosition(result).isHashSplat() }
+
+ /**
+ * Gets the block parameter of this method, if any.
+ */
+ ParameterNode getBlockParameter() { getParameterPosition(result).isBlock() }
+
+ /**
+ * Gets a `yield` in this method call or `.call` on the block parameter.
+ */
+ CallNode getABlockCall() {
+ hasYieldCall(getBlockParameter(), result)
+ or
+ result = getBlockParameter().getAMethodCall("call")
+ }
+
+ /**
+ * Gets the canonical return node from this callable.
+ *
+ * Each callable has exactly one such node, and its location may not correspond
+ * to any particular return site - consider using `getAReturningNode` to get nodes
+ * whose locations correspond to return sites.
+ */
+ Node getReturn() { result.(SynthReturnNode).getCfgScope() = callable }
+
+ /**
+ * Gets a data flow node whose value is about to be returned by this callable.
+ */
+ Node getAReturningNode() { result = this.getReturn().(SynthReturnNode).getAnInput() }
+}
+
+/**
+ * A data flow node corresponding to a method (possibly a singleton method).
+ */
+class MethodNode extends CallableNode {
+ MethodNode() { super.asCallableAstNode() instanceof MethodBase }
+
+ /** Gets the underlying AST node for this method. */
+ MethodBase asMethod() { result = this.asCallableAstNode() }
+
+ /** Gets the name of this method. */
+ string getMethodName() { result = asMethod().getName() }
+}
+
+/**
+ * A data flow node corresponding to a block argument.
+ */
+class BlockNode extends CallableNode {
+ BlockNode() { super.asCallableAstNode() instanceof Block }
+
+ /** Gets the underlying AST node for this block. */
+ Block asBlock() { result = this.asCallableAstNode() }
}
From 67772bbc433a93214812ccadace4e7d6cf819fa7 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Sat, 15 Oct 2022 17:24:07 +0200
Subject: [PATCH 067/249] Ruby: Accessors for attributes and elements
---
.../ruby/dataflow/internal/DataFlowPublic.qll | 63 +++++++++++++++++++
1 file changed, 63 insertions(+)
diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
index 50ec56019e8..2370ec3a9aa 100644
--- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
+++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
@@ -213,6 +213,69 @@ class LocalSourceNode extends Node {
/** Gets a call to a method named `name`, where this node flows to the receiver. */
CallNode getAMethodCall(string name) { Cached::hasMethodCall(this, result, name) }
+
+ /** Gets a call `obj.name` with no arguments, where this node flows to `obj`. */
+ CallNode getAnAttributeRead(string name) {
+ result = this.getAMethodCall(name) and
+ result.getNumberOfArguments() = 0
+ }
+
+ /**
+ * Gets a value assigned to `name` on this object, such as the `x` in `obj.name = x`.
+ *
+ * Concretely, this gets the argument of any call to `name=` where this node flows to the receiver.
+ */
+ Node getAnAttributeWriteValue(string name) {
+ result = this.getAMethodCall(name + "=").getArgument(0)
+ }
+
+ /**
+ * Gets an access to an element on this node, such as `obj[key]`.
+ *
+ * Concretely this gets a call to `[]` with 1 argument, where this node flows to the receiver.
+ */
+ CallNode getAnElementRead() {
+ result = this.getAMethodCall("[]") and result.getNumberOfArguments() = 1
+ }
+
+ /**
+ * Gets an access to the element `key` on this node, such as `obj[:key]`.
+ *
+ * Concretely this gets a call to `[]` where this node flows to the receiver
+ * and the first and only argument has the constant value `key`.
+ */
+ CallNode getAnElementRead(ConstantValue key) {
+ result = this.getAnElementRead() and
+ key = result.getArgument(0).getConstantValue()
+ }
+
+ /**
+ * Gets a value stored as an element on this node, such as the `x` in `obj[key] = x`.
+ *
+ * Concretely, this gets the second argument from any call to `[]=` where this node flows to the receiver.
+ */
+ Node getAnElementWriteValue() {
+ exists(CallNode call |
+ call = this.getAMethodCall("[]=") and
+ call.getNumberOfArguments() = 2 and
+ result = call.getArgument(1)
+ )
+ }
+
+ /**
+ * Gets the `x` in `obj[key] = x`, where this node flows to `obj`.
+ *
+ * Concretely, this gets the second argument from any call to `[]=` where this node flows to the receiver
+ * and the first argument has constant value `key`.
+ */
+ Node getAnElementWriteValue(ConstantValue key) {
+ exists(CallNode call |
+ call = this.getAMethodCall("[]=") and
+ call.getNumberOfArguments() = 2 and
+ call.getArgument(0).getConstantValue() = key and
+ result = call.getArgument(1)
+ )
+ }
}
/**
From 156964bfc9ed727814a6bd2221cddbd22ed93d22 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Mon, 17 Oct 2022 13:31:53 +0200
Subject: [PATCH 068/249] Ruby: add getEnclosingModule and getNestedModule
---
ruby/ql/lib/codeql/ruby/ast/Module.qll | 38 +++++++++++++++++++
.../ruby/dataflow/internal/DataFlowPublic.qll | 19 ++++++++++
2 files changed, 57 insertions(+)
diff --git a/ruby/ql/lib/codeql/ruby/ast/Module.qll b/ruby/ql/lib/codeql/ruby/ast/Module.qll
index c11b42c4bf6..91ffbfb2c91 100644
--- a/ruby/ql/lib/codeql/ruby/ast/Module.qll
+++ b/ruby/ql/lib/codeql/ruby/ast/Module.qll
@@ -118,6 +118,44 @@ class Module extends TModule {
or
result.(SelfVariableAccess).getVariable().getDeclaringScope() = this.getAnOwnSingletonMethod()
}
+
+ pragma[nomagic]
+ private string getEnclosingModuleName() {
+ exists(string qname |
+ qname = this.getQualifiedName() and
+ result = qname.regexpReplaceAll("::[^:]*$", "") and
+ qname != result
+ )
+ }
+
+ pragma[nomagic]
+ private string getOwnModuleName() {
+ result = this.getQualifiedName().regexpReplaceAll("^.*::", "")
+ }
+
+ /**
+ * Gets the enclosing module, as it appears in the qualified name of this module.
+ *
+ * For example, the canonical enclosing module of `A::B` is `A`, and `A` itself has no canonical enclosing module.
+ */
+ pragma[nomagic]
+ Module getCanonicalEnclosingModule() { result.getQualifiedName() = this.getEnclosingModuleName() }
+
+ /**
+ * Gets a module named `name` declared inside this one (not aliased), provided
+ * that such a module is defined or reopened in the current codebase.
+ *
+ * For example, for `A::B` the canonical nested module named `C` would be `A::B::C`.
+ *
+ * Note that this is not the same as constant lookup. If `A::B::C` would resolve to a
+ * module whose qualified name is not `A::B::C`, then it will not be found by
+ * this predicate.
+ */
+ pragma[nomagic]
+ Module getCanonicalNestedModule(string name) {
+ result.getCanonicalEnclosingModule() = this and
+ result.getOwnModuleName() = name
+ }
}
/**
diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
index 2370ec3a9aa..f4934500efd 100644
--- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
+++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
@@ -857,6 +857,25 @@ class ModuleNode instanceof Module {
* from ancestors.
*/
MethodNode getAnInstanceMethod() { result = this.getInstanceMethod(_) }
+
+ /**
+ * Gets the enclosing module, as it appears in the qualified name of this module.
+ *
+ * For example, the canonical enclosing module of `A::B` is `A`, and `A` itself has no canonical enclosing module.
+ */
+ ModuleNode getCanonicalEnclosingModule() { result = super.getCanonicalEnclosingModule() }
+
+ /**
+ * Gets a module named `name` declared inside this one (not aliased), provided
+ * that such a module is defined or reopened in the current codebase.
+ *
+ * For example, for `A::B` the canonical nested module named `C` would be `A::B::C`.
+ *
+ * Note that this is not the same as constant lookup. If `A::B::C` would resolve to a
+ * module whose qualified name is not `A::B::C`, then it will not be found by
+ * this predicate.
+ */
+ ModuleNode getCanonicalNestedModule(string name) { result = super.getCanonicalNestedModule(name) }
}
/**
From 436cc601389b5f3e5d60ef25ce8e5ce42745584b Mon Sep 17 00:00:00 2001
From: Asger F
Date: Mon, 17 Oct 2022 14:44:47 +0200
Subject: [PATCH 069/249] Ruby: update some uses of getConstantValue()
---
ruby/ql/lib/codeql/ruby/Concepts.qll | 15 +++++++--------
.../codeql/ruby/frameworks/ActionController.qll | 4 ++--
.../lib/codeql/ruby/frameworks/ActiveRecord.qll | 4 +---
ruby/ql/lib/codeql/ruby/frameworks/Rails.qll | 7 +------
.../UnsafeDeserializationCustomizations.qll | 6 +-----
ruby/ql/lib/codeql/ruby/security/XSS.qll | 5 ++---
.../security/cwe-078/NonConstantKernelOpen.ql | 2 +-
.../library-tests/dataflow/summaries/Summaries.ql | 2 +-
8 files changed, 16 insertions(+), 29 deletions(-)
diff --git a/ruby/ql/lib/codeql/ruby/Concepts.qll b/ruby/ql/lib/codeql/ruby/Concepts.qll
index dd7c75565b7..031b64440bd 100644
--- a/ruby/ql/lib/codeql/ruby/Concepts.qll
+++ b/ruby/ql/lib/codeql/ruby/Concepts.qll
@@ -271,10 +271,7 @@ module Http {
/** Gets the URL pattern for this route, if it can be statically determined. */
string getUrlPattern() {
- exists(CfgNodes::ExprNodes::StringlikeLiteralCfgNode strNode |
- this.getUrlPatternArg().getALocalSource() = DataFlow::exprNode(strNode) and
- result = strNode.getExpr().getConstantValue().getStringlikeValue()
- )
+ result = this.getUrlPatternArg().getALocalSource().getConstantValue().getStringlikeValue()
}
/**
@@ -538,10 +535,12 @@ module Http {
/** Gets the mimetype of this HTTP response, if it can be statically determined. */
string getMimetype() {
- exists(CfgNodes::ExprNodes::StringlikeLiteralCfgNode strNode |
- this.getMimetypeOrContentTypeArg().getALocalSource() = DataFlow::exprNode(strNode) and
- result = strNode.getExpr().getConstantValue().getStringlikeValue().splitAt(";", 0)
- )
+ result =
+ this.getMimetypeOrContentTypeArg()
+ .getALocalSource()
+ .getConstantValue()
+ .getStringlikeValue()
+ .splitAt(";", 0)
or
not exists(this.getMimetypeOrContentTypeArg()) and
result = this.getMimetypeDefault()
diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll
index e56df1465f6..1093d406ab8 100644
--- a/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll
+++ b/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll
@@ -234,7 +234,7 @@ private module Request {
// Request headers are prefixed with `HTTP_` to distinguish them from
// "headers" supplied by Rack middleware.
this.getMethodName() = ["get_header", "fetch_header"] and
- this.getArgument(0).asExpr().getExpr().getConstantValue().getString().regexpMatch("^HTTP_.+")
+ this.getArgument(0).getConstantValue().getString().regexpMatch("^HTTP_.+")
}
override Http::Server::RequestInputKind getKind() { result = Http::Server::headerInputKind() }
@@ -292,7 +292,7 @@ private module Request {
EnvHttpAccess() {
any(EnvCall c).(DataFlow::LocalSourceNode).flowsTo(this.getReceiver()) and
this.getMethodName() = "[]" and
- this.getArgument(0).asExpr().getExpr().getConstantValue().getString().regexpMatch("^HTTP_.+")
+ this.getArgument(0).getConstantValue().getString().regexpMatch("^HTTP_.+")
}
override Http::Server::RequestInputKind getKind() { result = Http::Server::headerInputKind() }
diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll
index eff81adde23..e9109514af2 100644
--- a/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll
+++ b/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll
@@ -571,9 +571,7 @@ class ActiveRecordAssociation extends DataFlow::CallNode {
* For example, in `has_many :posts`, this is `post`.
*/
string getTargetModelName() {
- exists(string s |
- s = this.getArgument(0).asExpr().getExpr().getConstantValue().getStringlikeValue()
- |
+ exists(string s | s = this.getArgument(0).getConstantValue().getStringlikeValue() |
// has_one :profile
// belongs_to :user
this.isSingular() and
diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Rails.qll b/ruby/ql/lib/codeql/ruby/frameworks/Rails.qll
index 97bd63453ee..74bd3c0a7e0 100644
--- a/ruby/ql/lib/codeql/ruby/frameworks/Rails.qll
+++ b/ruby/ql/lib/codeql/ruby/frameworks/Rails.qll
@@ -212,12 +212,7 @@ private module Settings {
private class LiteralSetting extends Setting {
ConstantValue value;
- LiteralSetting() {
- exists(DataFlow::LocalSourceNode lsn |
- lsn.asExpr().getConstantValue() = value and
- lsn.flowsTo(this.getArgument(0))
- )
- }
+ LiteralSetting() { value = this.getArgument(0).getALocalSource().getConstantValue() }
string getValueText() { result = value.toString() }
diff --git a/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll b/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll
index da759ea28e9..f12db1435d6 100644
--- a/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll
+++ b/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll
@@ -88,11 +88,7 @@ module UnsafeDeserialization {
private predicate isOjModePair(CfgNodes::ExprNodes::PairCfgNode p, string modeValue) {
p.getKey().getConstantValue().isStringlikeValue("mode") and
- exists(DataFlow::LocalSourceNode symbolLiteral, DataFlow::Node value |
- symbolLiteral.asExpr().getExpr().getConstantValue().isSymbol(modeValue) and
- symbolLiteral.flowsTo(value) and
- value.asExpr() = p.getValue()
- )
+ DataFlow::exprNode(p.getValue()).getALocalSource().getConstantValue().isSymbol(modeValue)
}
/**
diff --git a/ruby/ql/lib/codeql/ruby/security/XSS.qll b/ruby/ql/lib/codeql/ruby/security/XSS.qll
index 7a3b4d2f0e7..8ab1d66446e 100644
--- a/ruby/ql/lib/codeql/ruby/security/XSS.qll
+++ b/ruby/ql/lib/codeql/ruby/security/XSS.qll
@@ -180,11 +180,10 @@ private module Shared {
private predicate isFlowFromLocals0(
CfgNodes::ExprNodes::ElementReferenceCfgNode refNode, string hashKey, ErbFile erb
) {
- exists(DataFlow::Node argNode, CfgNodes::ExprNodes::StringlikeLiteralCfgNode strNode |
+ exists(DataFlow::Node argNode |
argNode.asExpr() = refNode.getArgument(0) and
refNode.getReceiver().getExpr().(MethodCall).getMethodName() = "local_assigns" and
- argNode.getALocalSource() = DataFlow::exprNode(strNode) and
- strNode.getExpr().getConstantValue().isStringlikeValue(hashKey) and
+ argNode.getALocalSource().getConstantValue().isStringlikeValue(hashKey) and
erb = refNode.getFile()
)
}
diff --git a/ruby/ql/src/queries/security/cwe-078/NonConstantKernelOpen.ql b/ruby/ql/src/queries/security/cwe-078/NonConstantKernelOpen.ql
index da490fd9ae3..a7cb2500c54 100644
--- a/ruby/ql/src/queries/security/cwe-078/NonConstantKernelOpen.ql
+++ b/ruby/ql/src/queries/security/cwe-078/NonConstantKernelOpen.ql
@@ -20,7 +20,7 @@ import codeql.ruby.ast.Literal
from AmbiguousPathCall call
where
// there is not a constant string argument
- not exists(call.getPathArgument().asExpr().getExpr().getConstantValue()) and
+ not exists(call.getPathArgument().getConstantValue()) and
// if it's a format string, then the first argument is not a constant string
not call.getPathArgument().getALocalSource().asExpr().getExpr().(StringLiteral).getComponent(0)
instanceof StringTextComponent
diff --git a/ruby/ql/test/library-tests/dataflow/summaries/Summaries.ql b/ruby/ql/test/library-tests/dataflow/summaries/Summaries.ql
index fbe8f0af9d4..416a8635deb 100644
--- a/ruby/ql/test/library-tests/dataflow/summaries/Summaries.ql
+++ b/ruby/ql/test/library-tests/dataflow/summaries/Summaries.ql
@@ -112,7 +112,7 @@ private class TypeFromCodeQL extends ModelInput::TypeModel {
override DataFlow::Node getASource(string package, string type) {
package = "test" and
type = "FooOrBar" and
- result.asExpr().getExpr().getConstantValue().getString() = "magic_string"
+ result.getConstantValue().getString() = "magic_string"
}
override API::Node getAnApiNode(string package, string type) {
From 1f644a9c1dcba8615e58e5630187674f107231b3 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Wed, 12 Oct 2022 16:37:42 +0200
Subject: [PATCH 070/249] Ruby: add getEnclosingToplevel
---
ruby/ql/lib/codeql/ruby/AST.qll | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/ruby/ql/lib/codeql/ruby/AST.qll b/ruby/ql/lib/codeql/ruby/AST.qll
index 6ba833b2108..d517c8f4d44 100644
--- a/ruby/ql/lib/codeql/ruby/AST.qll
+++ b/ruby/ql/lib/codeql/ruby/AST.qll
@@ -36,6 +36,13 @@ private module Cached {
not s instanceof ModuleBase and
result = getEnclosingMethod(s.getOuterScope())
}
+
+ cached
+ Toplevel getEnclosingToplevel(Scope s) {
+ result = s
+ or
+ result = getEnclosingToplevel(s.getOuterScope())
+ }
}
private import Cached
@@ -66,6 +73,9 @@ class AstNode extends TAstNode {
/** Gets the enclosing method, if any. */
final MethodBase getEnclosingMethod() { result = getEnclosingMethod(scopeOfInclSynth(this)) }
+ /** Gets the enclosing top-level. */
+ final Toplevel getEnclosingToplevel() { result = getEnclosingToplevel(scopeOfInclSynth(this)) }
+
/** Gets a textual representation of this node. */
cached
string toString() { none() }
From c8f7519cee198121ac9e201e2535ba327fd07133 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Mon, 17 Oct 2022 19:54:27 +0200
Subject: [PATCH 071/249] Ruby: add Module.getNamespaceOrTopLevel
---
ruby/ql/lib/codeql/ruby/ast/Module.qll | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/ruby/ql/lib/codeql/ruby/ast/Module.qll b/ruby/ql/lib/codeql/ruby/ast/Module.qll
index 91ffbfb2c91..a511a8cf952 100644
--- a/ruby/ql/lib/codeql/ruby/ast/Module.qll
+++ b/ruby/ql/lib/codeql/ruby/ast/Module.qll
@@ -241,6 +241,14 @@ class ModuleBase extends BodyStmt, Scope, TModuleBase {
* Does not include the `self` variable from any of the methods in the module.
*/
SelfVariable getModuleSelfVariable() { result.getDeclaringScope() = this }
+
+ /** Gets the nearest enclosing `Namespace` or `Toplevel`, possibly this module itself. */
+ Namespace getNamespaceOrToplevel() {
+ result = this
+ or
+ not this instanceof Namespace and
+ result = this.getEnclosingModule().getNamespaceOrToplevel()
+ }
}
/**
From 515b8366d223fe6e61ff7b030a43ec056a148f3c Mon Sep 17 00:00:00 2001
From: Asger F
Date: Tue, 18 Oct 2022 13:32:29 +0200
Subject: [PATCH 072/249] Ruby: add getAnAncestor, getADescendent
---
ruby/ql/lib/codeql/ruby/ast/Module.qll | 14 ++++++++++++++
.../ruby/dataflow/internal/DataFlowPublic.qll | 11 +++++++++++
2 files changed, 25 insertions(+)
diff --git a/ruby/ql/lib/codeql/ruby/ast/Module.qll b/ruby/ql/lib/codeql/ruby/ast/Module.qll
index a511a8cf952..cb940b4a35a 100644
--- a/ruby/ql/lib/codeql/ruby/ast/Module.qll
+++ b/ruby/ql/lib/codeql/ruby/ast/Module.qll
@@ -23,6 +23,20 @@ class Module extends TModule {
/** Gets an `include`d module. */
Module getAnIncludedModule() { result = getAnIncludedModule(this) }
+ /** Gets the super class or an included or prepended module. */
+ Module getADirectAncestor() {
+ result = [this.getSuperClass(), this.getAPrependedModule(), this.getAnIncludedModule()]
+ }
+
+ /** Gets a direct subclass or module including or prepending this one. */
+ Module getADirectDescendent() { this = result.getADirectAncestor() }
+
+ /** Gets a module that is transitively subclassed, included, or prepended by this module. */
+ Module getAnAncestor() { result = this.getADirectAncestor*() }
+
+ /** Gets a module that transitively subclasses, includes, or prepends this module. */
+ Module getADescendent() { result = this.getADirectDescendent*() }
+
/** Holds if this module is a class. */
pragma[noinline]
predicate isClass() {
diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
index f4934500efd..7c2f7abbd75 100644
--- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
+++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
@@ -703,6 +703,17 @@ class ModuleNode instanceof Module {
/** Gets an `include`d module. */
final ModuleNode getAnIncludedModule() { result = super.getAnIncludedModule() }
+ /** Gets the super class or an included or prepended module. */
+ final ModuleNode getADirectAncestor() { result = super.getADirectAncestor() }
+
+ /** Gets a direct subclass or module including or prepending this one. */
+ final ModuleNode getADirectDescendent() { result = super.getADirectDescendent() }
+
+ /** Gets a module that is transitively subclassed, included, or prepended by this module. */
+ final ModuleNode getAnAncestor() { result = super.getAnAncestor() }
+
+ /** Gets a module that transitively subclasses, includes, or prepends this module. */
+ final ModuleNode getADescendent() { result = super.getADescendent() }
/** Holds if this module is a class. */
predicate isClass() { super.isClass() }
From 2546d09fe272d73e3b46af763b17ecd78abf65fa Mon Sep 17 00:00:00 2001
From: Asger F
Date: Tue, 18 Oct 2022 13:42:45 +0200
Subject: [PATCH 073/249] Ruby: add SetterCallNode
---
.../ruby/dataflow/internal/DataFlowPublic.qll | 23 +++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
index 7c2f7abbd75..d7acaf71b66 100644
--- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
+++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
@@ -150,6 +150,29 @@ class CallNode extends LocalSourceNode, ExprNode {
}
}
+/**
+ * A call to a setter method.
+ *
+ * For example,
+ * ```rb
+ * self.foo = 10
+ * a[0] = 10
+ * ```
+ */
+class SetterCallNode extends CallNode {
+ SetterCallNode() { asExpr().getExpr() instanceof SetterMethodCall }
+
+ /**
+ * Gets the name of the method being called without the trailing `=`. For example, in the following
+ * two statements the target name is `value`:
+ * ```rb
+ * foo.value=(1)
+ * foo.value = 1
+ * ```
+ */
+ final string getTargetName() { result = asExpr().getExpr().(SetterMethodCall).getTargetName() }
+}
+
/**
* An expression, viewed as a node in a data flow graph.
*
From 77d1788619e3462b7de952fb0ba5dff8d33bc35e Mon Sep 17 00:00:00 2001
From: Asger F
Date: Thu, 20 Oct 2022 11:01:09 +0200
Subject: [PATCH 074/249] Ruby: add data flow versions of ArrayLiteral,
HashLiteral, Pair
---
.../ruby/dataflow/internal/DataFlowPublic.qll | 66 +++++++++++++++++++
1 file changed, 66 insertions(+)
diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
index d7acaf71b66..fd0e37bb92e 100644
--- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
+++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
@@ -737,6 +737,7 @@ class ModuleNode instanceof Module {
/** Gets a module that transitively subclasses, includes, or prepends this module. */
final ModuleNode getADescendent() { result = super.getADescendent() }
+
/** Holds if this module is a class. */
predicate isClass() { super.isClass() }
@@ -1000,3 +1001,68 @@ class BlockNode extends CallableNode {
/** Gets the underlying AST node for this block. */
Block asBlock() { result = this.asCallableAstNode() }
}
+
+/**
+ * A representation of a pair such as `K => V` or `K: V`.
+ *
+ * Unlike most expressions, pairs do not evaluate to actual objects at runtime and their nodes
+ * cannot generally be expected to have meaningful data flow edges.
+ * This node simply provides convenient access to the key and value as data flow nodes.
+ */
+class PairNode extends ExprNode {
+ PairNode() { getExprNode() instanceof CfgNodes::ExprNodes::PairCfgNode }
+
+ /**
+ * Holds if this pair is of form `key => value` or `key: value`.
+ */
+ predicate hasKeyAndValue(Node key, Node value) {
+ exists(CfgNodes::ExprNodes::PairCfgNode n |
+ getExprNode() = n and
+ key = TExprNode(n.getKey()) and
+ value = TExprNode(n.getValue())
+ )
+ }
+
+ /** Gets the key expression of this pair, such as the `K` in `K => V` or `K: V`. */
+ Node getKey() { this.hasKeyAndValue(result, _) }
+
+ /** Gets the value expression of this pair, such as the `V` in `K => V` or `K: V`. */
+ Node getValue() { this.hasKeyAndValue(_, result) }
+}
+
+/**
+ * A data-flow node that corresponds to a hash literal. Hash literals are desugared
+ * into calls to `Hash.[]`, so this includes both desugared calls as well as
+ * explicit calls.
+ */
+class HashLiteralNode extends LocalSourceNode, ExprNode {
+ HashLiteralNode() { super.getExprNode() instanceof CfgNodes::ExprNodes::HashLiteralCfgNode }
+
+ /** Gets a pair in this hash literal. */
+ PairNode getAKeyValuePair() {
+ result.getExprNode() =
+ super.getExprNode().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair()
+ }
+
+ /** Gets the value associated with the constant `key`, if known. */
+ Node getElementFromKey(ConstantValue key) {
+ exists(ExprNode keyNode |
+ this.getAKeyValuePair().hasKeyAndValue(keyNode, result) and
+ keyNode.getConstantValue() = key
+ )
+ }
+}
+
+/**
+ * A data-flow node corresponding to an array literal. Array literals are desugared
+ * into calls to `Array.[]`, so this includes both desugared calls as well as
+ * explicit calls.
+ */
+class ArrayLiteralNode extends LocalSourceNode, ExprNode {
+ ArrayLiteralNode() { super.getExprNode() instanceof CfgNodes::ExprNodes::ArrayLiteralCfgNode }
+
+ /**
+ * Gets an element of the array.
+ */
+ Node getAnElement() { result = this.(CallNode).getPositionalArgument(_) }
+}
From 046e669c786b78e8bcd7d444a0900e72e3a08a50 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Thu, 20 Oct 2022 14:29:52 +0200
Subject: [PATCH 075/249] Ruby: add getAncestorExpr
---
ruby/ql/lib/codeql/ruby/ast/Module.qll | 26 +++++++++++++++++++
.../ruby/dataflow/internal/DataFlowPublic.qll | 18 +++++++++++++
2 files changed, 44 insertions(+)
diff --git a/ruby/ql/lib/codeql/ruby/ast/Module.qll b/ruby/ql/lib/codeql/ruby/ast/Module.qll
index cb940b4a35a..7d5c2fae540 100644
--- a/ruby/ql/lib/codeql/ruby/ast/Module.qll
+++ b/ruby/ql/lib/codeql/ruby/ast/Module.qll
@@ -3,6 +3,7 @@ private import codeql.ruby.CFG
private import internal.AST
private import internal.Module
private import internal.TreeSitter
+private import internal.Scope
/**
* A representation of a run-time `module` or `class` value.
@@ -263,6 +264,31 @@ class ModuleBase extends BodyStmt, Scope, TModuleBase {
not this instanceof Namespace and
result = this.getEnclosingModule().getNamespaceOrToplevel()
}
+
+ /**
+ * Gets an expression denoting the super class or an included or prepended module.
+ *
+ * For example, `C` is an ancestor expression of `M` in each of the following examples:
+ * ```rb
+ * class M < C
+ * end
+ *
+ * module M
+ * include C
+ * prepend C
+ * end
+ * ```
+ */
+ Expr getAnAncestorExpr() {
+ exists(MethodCall call |
+ call.getReceiver().(SelfVariableAccess).getVariable() = this.getModuleSelfVariable() and
+ call.getMethodName() = ["include", "prepend"] and
+ result = call.getArgument(0) and
+ scopeOfInclSynth(call) = this // only permit calls directly in the module scope, not in a block
+ )
+ or
+ result = this.(ClassDeclaration).getSuperclassExpr()
+ }
}
/**
diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
index fd0e37bb92e..ac7ba249467 100644
--- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
+++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
@@ -738,6 +738,24 @@ class ModuleNode instanceof Module {
/** Gets a module that transitively subclasses, includes, or prepends this module. */
final ModuleNode getADescendent() { result = super.getADescendent() }
+ /**
+ * Gets the expression node denoting the super class or an included or prepended module.
+ *
+ * For example, `C` is an ancestor expression of `M` in each of the following examples:
+ * ```rb
+ * class M < C
+ * end
+ *
+ * module M
+ * include C
+ * prepend C
+ * end
+ * ```
+ */
+ final ExprNode getAnAncestorExpr() {
+ result.asExpr().getExpr() = super.getADeclaration().getAnAncestorExpr()
+ }
+
/** Holds if this module is a class. */
predicate isClass() { super.isClass() }
From 06ec03de747fcecbf1cb878eb356db2b0060a337 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Thu, 20 Oct 2022 11:07:35 +0200
Subject: [PATCH 076/249] Ruby: add convenience-accessors for ConstantValue
---
ruby/ql/lib/codeql/ruby/ast/Constant.qll | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/ruby/ql/lib/codeql/ruby/ast/Constant.qll b/ruby/ql/lib/codeql/ruby/ast/Constant.qll
index 187295d443b..a5a48a04ed4 100644
--- a/ruby/ql/lib/codeql/ruby/ast/Constant.qll
+++ b/ruby/ql/lib/codeql/ruby/ast/Constant.qll
@@ -170,6 +170,24 @@ module ConstantValue {
/** A constant `nil` value. */
class ConstantNilValue extends ConstantValue, TNil { }
+
+ /** Gets the integer constant `x`. */
+ ConstantValue getInt(int x) { result.getInt() = x }
+
+ /** Gets the float constant `x`. */
+ ConstantValue getFloat(float x) { result.getFloat() = x }
+
+ /** Gets the string constant `x`. */
+ ConstantValue getString(string x) { result.getString() = x }
+
+ /** Gets the symbol constant `x`. */
+ ConstantValue getSymbol(string x) { result.getSymbol() = x }
+
+ /** Gets the regexp constant `x`. */
+ ConstantValue getRegExp(string x) { result.getRegExp() = x }
+
+ /** Gets the string, symbol, or regexp constant `x`. */
+ ConstantValue getStringlikeValue(string x) { result.getStringlikeValue() = x }
}
/** An access to a constant. */
From 5369ba1d832e39013ef1d82581c2582239856290 Mon Sep 17 00:00:00 2001
From: Nick Rolfe
Date: Mon, 31 Oct 2022 11:24:30 +0000
Subject: [PATCH 077/249] ruby docs: remove distracting sentence
---
.../codeql-language-guides/analyzing-data-flow-in-ruby.rst | 1 -
1 file changed, 1 deletion(-)
diff --git a/docs/codeql/codeql-language-guides/analyzing-data-flow-in-ruby.rst b/docs/codeql/codeql-language-guides/analyzing-data-flow-in-ruby.rst
index feaa6415486..49a633ba2a7 100644
--- a/docs/codeql/codeql-language-guides/analyzing-data-flow-in-ruby.rst
+++ b/docs/codeql/codeql-language-guides/analyzing-data-flow-in-ruby.rst
@@ -97,7 +97,6 @@ The next section will give some concrete examples, but there is a more abstract
A local source is a data-flow node with no local data flow into it.
As such, it is a local origin of data flow, a place where a new value is created.
This includes parameters (which only receive global data flow) and most expressions (because they are not value-preserving).
-Restricting attention to such local sources gives a much lighter and more performant data-flow graph and in most cases also a more suitable abstraction for the investigation of interest.
The class ``LocalSourceNode`` represents data-flow nodes that are also local sources.
It comes with a useful member predicate ``flowsTo(DataFlow::Node node)``, which holds if there is local data flow from the local source to ``node``.
From b632e21ba0e4dac76929f868e0017d6e19d8b140 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Thu, 20 Oct 2022 14:29:58 +0200
Subject: [PATCH 078/249] Ruby: add ConstRef
---
.../ruby/dataflow/internal/DataFlowPublic.qll | 230 ++++++++++++++++++
1 file changed, 230 insertions(+)
diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
index ac7ba249467..91be512d329 100644
--- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
+++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
@@ -1084,3 +1084,233 @@ class ArrayLiteralNode extends LocalSourceNode, ExprNode {
*/
Node getAnElement() { result = this.(CallNode).getPositionalArgument(_) }
}
+
+/**
+ * A place in which a named constant can be found up during constant lookup.
+ */
+private newtype TConstLookupScope =
+ /** Look up in a qualified constant name `base::`. */
+ MkQualifiedLookup(ConstantAccess base) or
+ /** Look up in the ancestors of `mod`. */
+ MkAncestorLookup(Module mod) or
+ /** Look up in a module syntactically nested in `scope`. */
+ MkNestedLookup(ModuleBase scope) or
+ /** Pseudo-scope for accesses that are known to resolve to `mod`. */
+ MkExactLookup(Module mod)
+
+/**
+ * Gets a `LocalSourceNode` to represent the constant read or written by `access`.
+ */
+private LocalSourceNode getConstantAccessNode(ConstantAccess access) {
+ // Namespaces don't evaluate to the constant being accessed, they return the value of their last statement.
+ // Use the definition of 'self' in the namespace as the representative in this case.
+ if access instanceof Namespace
+ then
+ result.(SsaDefinitionNode).getDefinition().(Ssa::SelfDefinition).getSourceVariable() =
+ access.(Namespace).getModuleSelfVariable()
+ else result.asExpr().getExpr() = access
+}
+
+/**
+ * An access to a constant, such as `C`, `C::D`, or a class or module declaration.
+ *
+ * See `DataFlow::getConst` for usage example.
+ */
+class ConstRef extends LocalSourceNode {
+ private ConstantAccess access;
+
+ ConstRef() { this = getConstantAccessNode(access) }
+
+ /** Gets the underlying constant access AST node. */
+ ConstantAccess asConstantAccess() { result = access }
+
+ /** Gets the underlying module declaration, if any. */
+ Namespace asNamespaceDeclaration() { result = access }
+
+ /** Gets the module defined or re-opened by this constant access, if any. */
+ ModuleNode asModule() { result.getADeclaration() = access }
+
+ /**
+ * Gets the simple name of the constant being referenced, such as
+ * the `B` in `A::B`.
+ */
+ string getName() { result = access.getName() }
+
+ /**
+ * Holds if this might refer to a top-level constant.
+ */
+ predicate isPossiblyGlobal() {
+ exists(Module mod |
+ not exists(mod.getCanonicalEnclosingModule()) and
+ mod.getAnImmediateReference() = access
+ )
+ or
+ not exists(Module mod | mod.getAnImmediateReference() = access) and
+ not exists(access.getScopeExpr())
+ }
+
+ /**
+ * Gets a module for which this constant is the reference to an ancestor module.
+ *
+ * For example, `M` is the ancestry target of `C` in the following examples:
+ * ```rb
+ * class M < C {}
+ *
+ * module M
+ * include C
+ * end
+ *
+ * module M
+ * prepend C
+ * end
+ * ```
+ */
+ private ModuleNode getAncestryTarget() { result.getAnAncestorExpr() = this }
+
+ /**
+ * Gets a module scope in which the value of this constant is part of `Module.nesting`.
+ */
+ private ModuleBase getANestingScope() {
+ result = this.getAncestryTarget().getADeclaration()
+ or
+ result.getEnclosingModule() = this.getANestingScope()
+ }
+
+ /**
+ * Gets the known target module.
+ *
+ * We resolve these differently to prune out infeasible constant lookups.
+ */
+ private Module getExactTarget() { result.getAnImmediateReference() = access }
+
+ /**
+ * Gets a scope in which a constant lookup may access the contents of the module referenced by this constant.
+ */
+ pragma[nomagic]
+ private TConstLookupScope getATargetScope() {
+ result = MkAncestorLookup(this.getAncestryTarget().getADirectDescendent*())
+ or
+ access = any(ConstantAccess ac).getScopeExpr() and
+ result = MkQualifiedLookup(access)
+ or
+ result = MkNestedLookup(this.getANestingScope())
+ or
+ result = MkExactLookup(access.(Namespace).getModule())
+ }
+
+ /**
+ * Gets the scope expression, or the immediately enclosing `Namespace` (skipping over singleton classes).
+ *
+ * Top-levels are not included, since this is only needed for nested constant lookup, and unqualified constants
+ * at the top-level are handled by `DataFlow::getConst`, never `ConstRef.getConst`.
+ */
+ private TConstLookupScope getLookupScope() {
+ result = MkQualifiedLookup(access.getScopeExpr())
+ or
+ not exists(this.getExactTarget()) and
+ not exists(access.getScopeExpr()) and
+ not access.hasGlobalScope() and
+ (
+ result = MkAncestorLookup(access.getEnclosingModule().getNamespaceOrToplevel().getModule())
+ or
+ result = MkNestedLookup(access.getEnclosingModule())
+ )
+ }
+
+ /**
+ * Holds if this can reference a constant named `name` from `scope` using a lookup of `kind`.
+ */
+ pragma[nomagic]
+ private predicate accesses(TConstLookupScope scope, string name) {
+ scope = this.getLookupScope() and
+ name = this.getName()
+ or
+ exists(Module mod |
+ this.getExactTarget() = mod.getCanonicalNestedModule(name) and
+ scope = MkExactLookup(mod)
+ )
+ }
+
+ /**
+ * Gets a constant reference that may resolve to a member of this node.
+ *
+ * For example `DataFlow::getConst("A").getConst("B")` finds the following:
+ * ```rb
+ * A::B # simple reference
+ *
+ * module A
+ * B # in scope
+ * module X
+ * B # in nested scope
+ * end
+ * end
+ *
+ * module X
+ * include A
+ * B # via inclusion
+ * end
+ *
+ * class X < A
+ * B # via subclassing
+ * end
+ * ```
+ */
+ pragma[inline]
+ ConstRef getConst(string name) {
+ exists(TConstLookupScope scope |
+ pragma[only_bind_into](scope) = pragma[only_bind_out](this).getATargetScope() and
+ result.accesses(pragma[only_bind_out](scope), name)
+ )
+ }
+
+ /**
+ * Gets a module that transitively subclasses, includes, or prepends the module referred to by
+ * this constant.
+ *
+ * For example, `DataFlow::getConst("A").getADescendentModule()` finds `B`, `C`, and `E`:
+ * ```rb
+ * class B < A
+ * end
+ *
+ * class C < B
+ * end
+ *
+ * module E
+ * include C
+ * end
+ * ```
+ */
+ ModuleNode getADescendentModule() { MkAncestorLookup(result) = this.getATargetScope() }
+}
+
+/**
+ * Gets a constant reference that may resolve to the top-level constant `name`.
+ *
+ * To get nested constants, call `getConst()` one or more times on the result.
+ *
+ * For example `DataFlow::getConst("A").getConst("B")` finds the following:
+ * ```rb
+ * A::B # simple reference
+ *
+ * module A
+ * B # in scope
+ * module X
+ * B # in nested scope
+ * end
+ * end
+ *
+ * module X
+ * include A
+ * B # via inclusion
+ * end
+ *
+ * class X < A
+ * B # via subclassing
+ * end
+ * ```
+ */
+pragma[nomagic]
+ConstRef getConst(string name) {
+ result.getName() = name and
+ result.isPossiblyGlobal()
+}
From 4ed61c13f89ba4dc2aab0e19c0c5b813c222bb7c Mon Sep 17 00:00:00 2001
From: Asger F
Date: Thu, 27 Oct 2022 09:56:58 +0200
Subject: [PATCH 079/249] Ruby: add some captured-variable flow tests
---
.../dataflow/global/Flow.expected | 33 ++++++++++++++++
.../dataflow/global/captured_variables.rb | 38 +++++++++++++++++++
2 files changed, 71 insertions(+)
create mode 100644 ruby/ql/test/library-tests/dataflow/global/captured_variables.rb
diff --git a/ruby/ql/test/library-tests/dataflow/global/Flow.expected b/ruby/ql/test/library-tests/dataflow/global/Flow.expected
index c7139c2ec5b..ffba59b0f7e 100644
--- a/ruby/ql/test/library-tests/dataflow/global/Flow.expected
+++ b/ruby/ql/test/library-tests/dataflow/global/Flow.expected
@@ -1,5 +1,17 @@
failures
edges
+| captured_variables.rb:1:24:1:24 | x : | captured_variables.rb:2:20:2:20 | x |
+| captured_variables.rb:1:24:1:24 | x : | captured_variables.rb:2:20:2:20 | x |
+| captured_variables.rb:5:20:5:30 | call to source : | captured_variables.rb:1:24:1:24 | x : |
+| captured_variables.rb:5:20:5:30 | call to source : | captured_variables.rb:1:24:1:24 | x : |
+| captured_variables.rb:21:33:21:33 | x : | captured_variables.rb:23:14:23:14 | x |
+| captured_variables.rb:21:33:21:33 | x : | captured_variables.rb:23:14:23:14 | x |
+| captured_variables.rb:27:29:27:39 | call to source : | captured_variables.rb:21:33:21:33 | x : |
+| captured_variables.rb:27:29:27:39 | call to source : | captured_variables.rb:21:33:21:33 | x : |
+| captured_variables.rb:32:31:32:31 | x : | captured_variables.rb:34:14:34:14 | x |
+| captured_variables.rb:32:31:32:31 | x : | captured_variables.rb:34:14:34:14 | x |
+| captured_variables.rb:38:27:38:37 | call to source : | captured_variables.rb:32:31:32:31 | x : |
+| captured_variables.rb:38:27:38:37 | call to source : | captured_variables.rb:32:31:32:31 | x : |
| instance_variables.rb:10:19:10:19 | x : | instance_variables.rb:11:18:11:18 | x : |
| instance_variables.rb:10:19:10:19 | x : | instance_variables.rb:11:18:11:18 | x : |
| instance_variables.rb:11:18:11:18 | x : | instance_variables.rb:11:9:11:14 | [post] self [@field] : |
@@ -152,6 +164,24 @@ edges
| instance_variables.rb:84:6:84:10 | foo13 [@field] : | instance_variables.rb:84:6:84:20 | call to get_field |
| instance_variables.rb:84:6:84:10 | foo13 [@field] : | instance_variables.rb:84:6:84:20 | call to get_field |
nodes
+| captured_variables.rb:1:24:1:24 | x : | semmle.label | x : |
+| captured_variables.rb:1:24:1:24 | x : | semmle.label | x : |
+| captured_variables.rb:2:20:2:20 | x | semmle.label | x |
+| captured_variables.rb:2:20:2:20 | x | semmle.label | x |
+| captured_variables.rb:5:20:5:30 | call to source : | semmle.label | call to source : |
+| captured_variables.rb:5:20:5:30 | call to source : | semmle.label | call to source : |
+| captured_variables.rb:21:33:21:33 | x : | semmle.label | x : |
+| captured_variables.rb:21:33:21:33 | x : | semmle.label | x : |
+| captured_variables.rb:23:14:23:14 | x | semmle.label | x |
+| captured_variables.rb:23:14:23:14 | x | semmle.label | x |
+| captured_variables.rb:27:29:27:39 | call to source : | semmle.label | call to source : |
+| captured_variables.rb:27:29:27:39 | call to source : | semmle.label | call to source : |
+| captured_variables.rb:32:31:32:31 | x : | semmle.label | x : |
+| captured_variables.rb:32:31:32:31 | x : | semmle.label | x : |
+| captured_variables.rb:34:14:34:14 | x | semmle.label | x |
+| captured_variables.rb:34:14:34:14 | x | semmle.label | x |
+| captured_variables.rb:38:27:38:37 | call to source : | semmle.label | call to source : |
+| captured_variables.rb:38:27:38:37 | call to source : | semmle.label | call to source : |
| instance_variables.rb:10:19:10:19 | x : | semmle.label | x : |
| instance_variables.rb:10:19:10:19 | x : | semmle.label | x : |
| instance_variables.rb:11:9:11:14 | [post] self [@field] : | semmle.label | [post] self [@field] : |
@@ -335,6 +365,9 @@ subpaths
| instance_variables.rb:84:6:84:10 | foo13 [@field] : | instance_variables.rb:13:5:15:7 | self in get_field [@field] : | instance_variables.rb:14:9:14:21 | return : | instance_variables.rb:84:6:84:20 | call to get_field |
| instance_variables.rb:84:6:84:10 | foo13 [@field] : | instance_variables.rb:13:5:15:7 | self in get_field [@field] : | instance_variables.rb:14:9:14:21 | return : | instance_variables.rb:84:6:84:20 | call to get_field |
#select
+| captured_variables.rb:2:20:2:20 | x | captured_variables.rb:5:20:5:30 | call to source : | captured_variables.rb:2:20:2:20 | x | $@ | captured_variables.rb:5:20:5:30 | call to source : | call to source : |
+| captured_variables.rb:23:14:23:14 | x | captured_variables.rb:27:29:27:39 | call to source : | captured_variables.rb:23:14:23:14 | x | $@ | captured_variables.rb:27:29:27:39 | call to source : | call to source : |
+| captured_variables.rb:34:14:34:14 | x | captured_variables.rb:38:27:38:37 | call to source : | captured_variables.rb:34:14:34:14 | x | $@ | captured_variables.rb:38:27:38:37 | call to source : | call to source : |
| instance_variables.rb:20:10:20:13 | @foo | instance_variables.rb:19:12:19:21 | call to taint : | instance_variables.rb:20:10:20:13 | @foo | $@ | instance_variables.rb:19:12:19:21 | call to taint : | call to taint : |
| instance_variables.rb:25:6:25:18 | call to get_field | instance_variables.rb:24:15:24:23 | call to taint : | instance_variables.rb:25:6:25:18 | call to get_field | $@ | instance_variables.rb:24:15:24:23 | call to taint : | call to taint : |
| instance_variables.rb:29:6:29:18 | call to inc_field | instance_variables.rb:28:15:28:22 | call to taint : | instance_variables.rb:29:6:29:18 | call to inc_field | $@ | instance_variables.rb:28:15:28:22 | call to taint : | call to taint : |
diff --git a/ruby/ql/test/library-tests/dataflow/global/captured_variables.rb b/ruby/ql/test/library-tests/dataflow/global/captured_variables.rb
new file mode 100644
index 00000000000..88b5be2e7c3
--- /dev/null
+++ b/ruby/ql/test/library-tests/dataflow/global/captured_variables.rb
@@ -0,0 +1,38 @@
+def capture_local_call x
+ fn = -> { sink(x) } # $ hasValueFlow=1.1
+ fn.call
+end
+capture_local_call source(1.1)
+
+def capture_escape_return1 x
+ -> {
+ sink(x) # $ MISSING: hasValueFlow=1.2
+ }
+end
+(capture_escape_return1 source(1.2)).call
+
+def capture_escape_return2 x
+ -> {
+ sink(x) # $ MISSING: hasValueFlow=1.3
+ }
+end
+Something.unknownMethod(capture_escape_return2 source(1.3))
+
+def capture_escape_unknown_call x
+ fn = -> {
+ sink(x) # $ hasValueFlow=1.4
+ }
+ Something.unknownMethod(fn)
+end
+capture_escape_unknown_call source(1.4)
+
+def call_it fn
+ fn.call
+end
+def capture_escape_known_call x
+ fn = -> {
+ sink(x) # $ hasValueFlow=1.5
+ }
+ call_it fn
+end
+capture_escape_known_call source(1.5)
From b29ac5249e497d52c0778018b972dca5f66ad9c0 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Thu, 27 Oct 2022 10:07:36 +0200
Subject: [PATCH 080/249] Ruby: add type-tracking inline test in global flow
test
---
.../global/TypeTrackingInlineTest.expected | 15 +++++++++++++++
.../dataflow/global/TypeTrackingInlineTest.ql | 1 +
2 files changed, 16 insertions(+)
create mode 100644 ruby/ql/test/library-tests/dataflow/global/TypeTrackingInlineTest.expected
create mode 100644 ruby/ql/test/library-tests/dataflow/global/TypeTrackingInlineTest.ql
diff --git a/ruby/ql/test/library-tests/dataflow/global/TypeTrackingInlineTest.expected b/ruby/ql/test/library-tests/dataflow/global/TypeTrackingInlineTest.expected
new file mode 100644
index 00000000000..aa4fa55223a
--- /dev/null
+++ b/ruby/ql/test/library-tests/dataflow/global/TypeTrackingInlineTest.expected
@@ -0,0 +1,15 @@
+| instance_variables.rb:20:16:20:33 | # $ hasValueFlow=7 | Missing result:hasValueFlow=7 |
+| instance_variables.rb:25:21:25:39 | # $ hasValueFlow=42 | Missing result:hasValueFlow=42 |
+| instance_variables.rb:37:22:37:40 | # $ hasValueFlow=21 | Missing result:hasValueFlow=21 |
+| instance_variables.rb:41:18:41:36 | # $ hasValueFlow=22 | Missing result:hasValueFlow=22 |
+| instance_variables.rb:49:22:49:40 | # $ hasValueFlow=24 | Missing result:hasValueFlow=24 |
+| instance_variables.rb:53:22:53:40 | # $ hasValueFlow=22 | Missing result:hasValueFlow=22 |
+| instance_variables.rb:54:22:54:40 | # $ hasValueFlow=24 | Missing result:hasValueFlow=24 |
+| instance_variables.rb:55:22:55:40 | # $ hasValueFlow=25 | Missing result:hasValueFlow=25 |
+| instance_variables.rb:60:22:60:40 | # $ hasValueFlow=26 | Missing result:hasValueFlow=26 |
+| instance_variables.rb:61:22:61:40 | # $ hasValueFlow=26 | Missing result:hasValueFlow=26 |
+| instance_variables.rb:66:22:66:40 | # $ hasValueFlow=27 | Missing result:hasValueFlow=27 |
+| instance_variables.rb:67:23:67:41 | # $ hasValueFlow=27 | Missing result:hasValueFlow=27 |
+| instance_variables.rb:75:23:75:41 | # $ hasValueFlow=28 | Missing result:hasValueFlow=28 |
+| instance_variables.rb:79:23:79:41 | # $ hasValueFlow=28 | Missing result:hasValueFlow=28 |
+| instance_variables.rb:84:23:84:41 | # $ hasValueFlow=28 | Missing result:hasValueFlow=28 |
diff --git a/ruby/ql/test/library-tests/dataflow/global/TypeTrackingInlineTest.ql b/ruby/ql/test/library-tests/dataflow/global/TypeTrackingInlineTest.ql
new file mode 100644
index 00000000000..97df46829d9
--- /dev/null
+++ b/ruby/ql/test/library-tests/dataflow/global/TypeTrackingInlineTest.ql
@@ -0,0 +1 @@
+import TestUtilities.InlineTypeTrackingFlowTest
From 017157820ab1afc85e28f88fb973c88db7278b97 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Mon, 17 Oct 2022 15:23:24 +0200
Subject: [PATCH 081/249] Ruby: make ParameterNode extend LocalSourceNode
---
.../lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll | 8 ++++----
.../lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll
index 89010d75235..fe0899d4763 100644
--- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll
+++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll
@@ -109,7 +109,7 @@ module LocalFlow {
* Holds if `nodeFrom` is a parameter node, and `nodeTo` is a corresponding SSA node.
*/
predicate localFlowSsaParamInput(Node nodeFrom, Node nodeTo) {
- nodeTo = getParameterDefNode(nodeFrom.(ParameterNode).getParameter())
+ nodeTo = getParameterDefNode(nodeFrom.(ParameterNodeImpl).getParameter())
or
nodeTo = getSelfParameterDefNode(nodeFrom.(SelfParameterNode).getMethod())
}
@@ -299,7 +299,7 @@ private module Cached {
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
or
- defaultValueFlow(nodeTo.(ParameterNode).getParameter(), nodeFrom)
+ defaultValueFlow(nodeTo.(ParameterNodeImpl).getParameter(), nodeFrom)
or
LocalFlow::localFlowSsaParamInput(nodeFrom, nodeTo)
or
@@ -316,7 +316,7 @@ private module Cached {
predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) {
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
or
- defaultValueFlow(nodeTo.(ParameterNode).getParameter(), nodeFrom)
+ defaultValueFlow(nodeTo.(ParameterNodeImpl).getParameter(), nodeFrom)
or
LocalFlow::localFlowSsaParamInput(nodeFrom, nodeTo)
or
@@ -366,7 +366,7 @@ private module Cached {
cached
predicate isLocalSourceNode(Node n) {
- n instanceof ParameterNode
+ n instanceof TParameterNode
or
// Expressions that can't be reached from another entry definition or expression
n instanceof ExprNode and
diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
index 91be512d329..8af1d31e303 100644
--- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
+++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
@@ -193,7 +193,7 @@ class ExprNode extends Node, TExprNode {
* The value of a parameter at function entry, viewed as a node in a data
* flow graph.
*/
-class ParameterNode extends Node, TParameterNode instanceof ParameterNodeImpl {
+class ParameterNode extends LocalSourceNode, TParameterNode instanceof ParameterNodeImpl {
/** Gets the parameter corresponding to this node, if any. */
final Parameter getParameter() { result = super.getParameter() }
}
From ff02ba5965cd31e43399a9e8a8a9558534f32dda Mon Sep 17 00:00:00 2001
From: Asger F
Date: Tue, 18 Oct 2022 15:33:41 +0200
Subject: [PATCH 082/249] Ruby: include SSA param input step for flowsTo
---
.../lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll | 7 ++++++-
.../lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll | 6 ++++--
2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll
index fe0899d4763..d8b04bebfab 100644
--- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll
+++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll
@@ -327,7 +327,12 @@ private module Cached {
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(nodeFrom, nodeTo, _)
}
- /** This is the local flow predicate that is used in type tracking. */
+ /**
+ * This is the local flow predicate that is used in type tracking.
+ *
+ * This needs to exclude `localFlowSsaParamInput` due to a performance trick
+ * in type tracking, where such steps are treated as call steps.
+ */
cached
predicate localFlowStepTypeTracker(Node nodeFrom, Node nodeTo) {
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
index 8af1d31e303..16740ce33fe 100644
--- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
+++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
@@ -326,9 +326,11 @@ private module Cached {
source = sink and
source instanceof LocalSourceNode
or
- exists(Node mid |
- hasLocalSource(mid, source) and
+ exists(Node mid | hasLocalSource(mid, source) |
localFlowStepTypeTracker(mid, sink)
+ or
+ // Explicitly include the SSA param input step as type-tracking omits this step.
+ LocalFlow::localFlowSsaParamInput(mid, sink)
)
}
From 0a8f39fe96e89baa167017d290080384f373c113 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Thu, 27 Oct 2022 21:36:15 +0200
Subject: [PATCH 083/249] Ruby: recover some incomplete capture flow
---
ruby/ql/lib/codeql/ruby/dataflow/SSA.qll | 2 +-
.../dataflow/internal/DataFlowPrivate.qll | 16 ++
.../ruby/dataflow/internal/DataFlowPublic.qll | 2 +
.../dataflow/local/DataflowStep.expected | 8 +-
.../dataflow/local/TaintStep.expected | 8 +-
.../test/library-tests/variables/ssa.expected | 204 +++++++++---------
6 files changed, 129 insertions(+), 111 deletions(-)
diff --git a/ruby/ql/lib/codeql/ruby/dataflow/SSA.qll b/ruby/ql/lib/codeql/ruby/dataflow/SSA.qll
index d828420682c..ca956ed89f5 100644
--- a/ruby/ql/lib/codeql/ruby/dataflow/SSA.qll
+++ b/ruby/ql/lib/codeql/ruby/dataflow/SSA.qll
@@ -289,7 +289,7 @@ module Ssa {
)
}
- final override string toString() { result = "" }
+ final override string toString() { result = " " + this.getSourceVariable() }
override Location getLocation() { result = this.getBasicBlock().getLocation() }
}
diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll
index d8b04bebfab..0ccc41e3069 100644
--- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll
+++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll
@@ -114,6 +114,22 @@ module LocalFlow {
nodeTo = getSelfParameterDefNode(nodeFrom.(SelfParameterNode).getMethod())
}
+ /**
+ * Holds if `nodeFrom -> nodeTo` is a step from a parameter to a capture entry node for
+ * that parameter.
+ *
+ * This is intended to recover from flow not currently recognised by ordinary capture flow.
+ */
+ predicate localFlowSsaParamCaptureInput(Node nodeFrom, Node nodeTo) {
+ exists(Ssa::CapturedEntryDefinition def |
+ nodeFrom.asParameter().(NamedParameter).getVariable() = def.getSourceVariable()
+ or
+ nodeFrom.(SelfParameterNode).getSelfVariable() = def.getSourceVariable()
+ |
+ nodeTo.(SsaDefinitionNode).getDefinition() = def
+ )
+ }
+
/**
* Holds if there is a local use-use flow step from `nodeFrom` to `nodeTo`
* involving SSA definition `def`.
diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
index 16740ce33fe..fdd21c5bf5e 100644
--- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
+++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll
@@ -331,6 +331,8 @@ private module Cached {
or
// Explicitly include the SSA param input step as type-tracking omits this step.
LocalFlow::localFlowSsaParamInput(mid, sink)
+ or
+ LocalFlow::localFlowSsaParamCaptureInput(mid, sink)
)
}
diff --git a/ruby/ql/test/library-tests/dataflow/local/DataflowStep.expected b/ruby/ql/test/library-tests/dataflow/local/DataflowStep.expected
index a9387f49fa5..1c06c617f5f 100644
--- a/ruby/ql/test/library-tests/dataflow/local/DataflowStep.expected
+++ b/ruby/ql/test/library-tests/dataflow/local/DataflowStep.expected
@@ -26,7 +26,7 @@
| local_dataflow.rb:9:9:9:15 | call to [] | local_dataflow.rb:9:1:9:15 | ... = ... |
| local_dataflow.rb:9:9:9:15 | call to [] | local_dataflow.rb:9:1:9:15 | ... = ... |
| local_dataflow.rb:10:5:13:3 | ... = ... | local_dataflow.rb:12:5:12:5 | x |
-| local_dataflow.rb:10:5:13:3 | | local_dataflow.rb:11:1:11:2 | self |
+| local_dataflow.rb:10:5:13:3 | self | local_dataflow.rb:11:1:11:2 | self |
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | ... = ... |
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | ... = ... |
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | __synth__0__1 |
@@ -65,7 +65,7 @@
| local_dataflow.rb:45:10:45:10 | 6 | local_dataflow.rb:45:3:45:10 | return |
| local_dataflow.rb:49:1:53:3 | [post] self | local_dataflow.rb:55:1:55:14 | self |
| local_dataflow.rb:49:1:53:3 | self | local_dataflow.rb:55:1:55:14 | self |
-| local_dataflow.rb:49:3:53:3 | | local_dataflow.rb:50:18:50:18 | x |
+| local_dataflow.rb:49:3:53:3 | x | local_dataflow.rb:50:18:50:18 | x |
| local_dataflow.rb:50:8:50:13 | "next" | local_dataflow.rb:50:3:50:13 | next |
| local_dataflow.rb:50:18:50:18 | [post] x | local_dataflow.rb:51:20:51:20 | x |
| local_dataflow.rb:50:18:50:18 | x | local_dataflow.rb:51:20:51:20 | x |
@@ -264,7 +264,7 @@
| local_dataflow.rb:118:3:118:11 | [post] self | local_dataflow.rb:119:3:119:31 | self |
| local_dataflow.rb:118:3:118:11 | call to source | local_dataflow.rb:118:3:118:31 | call to tap |
| local_dataflow.rb:118:3:118:11 | self | local_dataflow.rb:119:3:119:31 | self |
-| local_dataflow.rb:118:17:118:31 | | local_dataflow.rb:118:23:118:29 | self |
+| local_dataflow.rb:118:17:118:31 | self | local_dataflow.rb:118:23:118:29 | self |
| local_dataflow.rb:118:20:118:20 | x | local_dataflow.rb:118:20:118:20 | x |
| local_dataflow.rb:118:20:118:20 | x | local_dataflow.rb:118:28:118:28 | x |
| local_dataflow.rb:119:3:119:31 | [post] self | local_dataflow.rb:119:8:119:16 | self |
@@ -278,4 +278,4 @@
| local_dataflow.rb:123:8:123:16 | call to source | local_dataflow.rb:123:8:123:20 | call to dup |
| local_dataflow.rb:123:8:123:20 | call to dup | local_dataflow.rb:123:8:123:45 | call to tap |
| local_dataflow.rb:123:8:123:45 | call to tap | local_dataflow.rb:123:8:123:49 | call to dup |
-| local_dataflow.rb:123:26:123:45 | | local_dataflow.rb:123:32:123:43 | self |
+| local_dataflow.rb:123:26:123:45 | self | local_dataflow.rb:123:32:123:43 | self |
diff --git a/ruby/ql/test/library-tests/dataflow/local/TaintStep.expected b/ruby/ql/test/library-tests/dataflow/local/TaintStep.expected
index 5043cbdb18f..c8af6b208df 100644
--- a/ruby/ql/test/library-tests/dataflow/local/TaintStep.expected
+++ b/ruby/ql/test/library-tests/dataflow/local/TaintStep.expected
@@ -78,7 +78,7 @@
| local_dataflow.rb:9:9:9:15 | call to [] | local_dataflow.rb:9:1:9:15 | ... = ... |
| local_dataflow.rb:9:9:9:15 | call to [] | local_dataflow.rb:9:1:9:15 | ... = ... |
| local_dataflow.rb:10:5:13:3 | ... = ... | local_dataflow.rb:12:5:12:5 | x |
-| local_dataflow.rb:10:5:13:3 | | local_dataflow.rb:11:1:11:2 | self |
+| local_dataflow.rb:10:5:13:3 | self | local_dataflow.rb:11:1:11:2 | self |
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | ... = ... |
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | ... = ... |
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | __synth__0__1 |
@@ -123,7 +123,7 @@
| local_dataflow.rb:45:10:45:10 | 6 | local_dataflow.rb:45:3:45:10 | return |
| local_dataflow.rb:49:1:53:3 | [post] self | local_dataflow.rb:55:1:55:14 | self |
| local_dataflow.rb:49:1:53:3 | self | local_dataflow.rb:55:1:55:14 | self |
-| local_dataflow.rb:49:3:53:3 | | local_dataflow.rb:50:18:50:18 | x |
+| local_dataflow.rb:49:3:53:3 | x | local_dataflow.rb:50:18:50:18 | x |
| local_dataflow.rb:50:8:50:13 | "next" | local_dataflow.rb:50:3:50:13 | next |
| local_dataflow.rb:50:18:50:18 | [post] x | local_dataflow.rb:51:20:51:20 | x |
| local_dataflow.rb:50:18:50:18 | x | local_dataflow.rb:50:18:50:22 | ... < ... |
@@ -338,7 +338,7 @@
| local_dataflow.rb:118:3:118:11 | [post] self | local_dataflow.rb:119:3:119:31 | self |
| local_dataflow.rb:118:3:118:11 | call to source | local_dataflow.rb:118:3:118:31 | call to tap |
| local_dataflow.rb:118:3:118:11 | self | local_dataflow.rb:119:3:119:31 | self |
-| local_dataflow.rb:118:17:118:31 | | local_dataflow.rb:118:23:118:29 | self |
+| local_dataflow.rb:118:17:118:31 | self | local_dataflow.rb:118:23:118:29 | self |
| local_dataflow.rb:118:20:118:20 | x | local_dataflow.rb:118:20:118:20 | x |
| local_dataflow.rb:118:20:118:20 | x | local_dataflow.rb:118:28:118:28 | x |
| local_dataflow.rb:119:3:119:31 | [post] self | local_dataflow.rb:119:8:119:16 | self |
@@ -352,4 +352,4 @@
| local_dataflow.rb:123:8:123:16 | call to source | local_dataflow.rb:123:8:123:20 | call to dup |
| local_dataflow.rb:123:8:123:20 | call to dup | local_dataflow.rb:123:8:123:45 | call to tap |
| local_dataflow.rb:123:8:123:45 | call to tap | local_dataflow.rb:123:8:123:49 | call to dup |
-| local_dataflow.rb:123:26:123:45 | | local_dataflow.rb:123:32:123:43 | self |
+| local_dataflow.rb:123:26:123:45 | self | local_dataflow.rb:123:32:123:43 | self |
diff --git a/ruby/ql/test/library-tests/variables/ssa.expected b/ruby/ql/test/library-tests/variables/ssa.expected
index 62a3bee1a81..e0553f1ad39 100644
--- a/ruby/ql/test/library-tests/variables/ssa.expected
+++ b/ruby/ql/test/library-tests/variables/ssa.expected
@@ -13,9 +13,9 @@ definition
| instance_variables.rb:15:3:17:5 | self (m) | instance_variables.rb:15:3:17:5 | self |
| instance_variables.rb:20:1:25:3 | self (M) | instance_variables.rb:20:1:25:3 | self |
| instance_variables.rb:22:2:24:4 | self (n) | instance_variables.rb:22:2:24:4 | self |
-| instance_variables.rb:27:6:29:1 | | instance_variables.rb:1:1:44:4 | self |
+| instance_variables.rb:27:6:29:1 | self | instance_variables.rb:1:1:44:4 | self |
| instance_variables.rb:31:1:33:3 | self (bar) | instance_variables.rb:31:1:33:3 | self |
-| instance_variables.rb:32:10:32:21 | | instance_variables.rb:31:1:33:3 | self |
+| instance_variables.rb:32:10:32:21 | self | instance_variables.rb:31:1:33:3 | self |
| instance_variables.rb:35:1:44:4 | self (C) | instance_variables.rb:35:1:44:4 | self |
| instance_variables.rb:37:3:43:5 | self (x) | instance_variables.rb:37:3:43:5 | self |
| instance_variables.rb:38:4:40:6 | self (y) | instance_variables.rb:38:4:40:6 | self |
@@ -32,8 +32,8 @@ definition
| nested_scopes.rb:13:11:13:15 | ... = ... | nested_scopes.rb:13:11:13:11 | a |
| nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:15:23:15:23 | a |
| nested_scopes.rb:17:15:17:19 | ... = ... | nested_scopes.rb:16:29:16:29 | a |
-| nested_scopes.rb:18:23:18:36 | | nested_scopes.rb:12:9:21:11 | self |
-| nested_scopes.rb:18:23:18:36 | | nested_scopes.rb:16:29:16:29 | a |
+| nested_scopes.rb:18:23:18:36 | a | nested_scopes.rb:16:29:16:29 | a |
+| nested_scopes.rb:18:23:18:36 | self | nested_scopes.rb:12:9:21:11 | self |
| nested_scopes.rb:22:9:24:11 | self (show_a2) | nested_scopes.rb:22:9:24:11 | self |
| nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:22:21:22:21 | a |
| nested_scopes.rb:27:7:29:9 | self (show) | nested_scopes.rb:27:7:29:9 | self |
@@ -41,7 +41,7 @@ definition
| nested_scopes.rb:31:11:31:16 | ... = ... | nested_scopes.rb:31:11:31:11 | a |
| nested_scopes.rb:40:1:40:18 | ... = ... | nested_scopes.rb:40:1:40:1 | d |
| parameters.rb:1:1:1:1 | self (parameters.rb) | parameters.rb:1:1:62:1 | self |
-| parameters.rb:1:9:5:3 | | parameters.rb:1:1:62:1 | self |
+| parameters.rb:1:9:5:3 | self | parameters.rb:1:1:62:1 | self |
| parameters.rb:1:14:1:14 | x | parameters.rb:1:14:1:14 | x |
| parameters.rb:2:4:2:8 | ... = ... | parameters.rb:1:18:1:18 | y |
| parameters.rb:7:1:13:3 | self (order_pizza) | parameters.rb:7:1:13:3 | self |
@@ -49,7 +49,7 @@ definition
| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas |
| parameters.rb:15:1:19:3 | self (print_map) | parameters.rb:15:1:19:3 | self |
| parameters.rb:15:17:15:19 | map | parameters.rb:15:17:15:19 | map |
-| parameters.rb:16:12:18:5 | | parameters.rb:15:1:19:3 | self |
+| parameters.rb:16:12:18:5 | self | parameters.rb:15:1:19:3 | self |
| parameters.rb:16:16:16:18 | key | parameters.rb:16:16:16:18 | key |
| parameters.rb:16:21:16:25 | value | parameters.rb:16:21:16:25 | value |
| parameters.rb:21:17:21:21 | block | parameters.rb:21:17:21:21 | block |
@@ -76,8 +76,8 @@ definition
| parameters.rb:49:13:49:13 | a | parameters.rb:49:13:49:13 | a |
| parameters.rb:49:15:49:15 | b | parameters.rb:49:15:49:15 | b |
| parameters.rb:53:1:53:6 | ... = ... | parameters.rb:53:1:53:1 | x |
-| parameters.rb:54:9:57:3 | | parameters.rb:1:1:62:1 | self |
-| parameters.rb:54:9:57:3 | | parameters.rb:53:1:53:1 | x |
+| parameters.rb:54:9:57:3 | self | parameters.rb:1:1:62:1 | self |
+| parameters.rb:54:9:57:3 | x | parameters.rb:53:1:53:1 | x |
| parameters.rb:54:14:54:14 | y | parameters.rb:54:14:54:14 | y |
| parameters.rb:54:19:54:23 | ... = ... | parameters.rb:53:1:53:1 | x |
| parameters.rb:55:4:55:9 | phi | parameters.rb:53:1:53:1 | x |
@@ -86,11 +86,11 @@ definition
| parameters.rb:59:23:59:23 | b | parameters.rb:59:23:59:23 | b |
| parameters.rb:59:25:59:25 | c | parameters.rb:59:25:59:25 | c |
| scopes.rb:1:1:1:15 | self (scopes.rb) | scopes.rb:1:1:49:4 | self |
-| scopes.rb:2:9:6:3 | | scopes.rb:1:1:49:4 | self |
+| scopes.rb:2:9:6:3 | self | scopes.rb:1:1:49:4 | self |
| scopes.rb:4:4:4:8 | ... = ... | scopes.rb:4:4:4:4 | a |
| scopes.rb:7:1:7:5 | ... = ... | scopes.rb:7:1:7:1 | a |
-| scopes.rb:9:9:18:3 | | scopes.rb:1:1:49:4 | self |
-| scopes.rb:9:9:18:3 | | scopes.rb:7:1:7:1 | a |
+| scopes.rb:9:9:18:3 | a | scopes.rb:7:1:7:1 | a |
+| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:49:4 | self |
| scopes.rb:11:4:11:9 | ... = ... | scopes.rb:7:1:7:1 | a |
| scopes.rb:13:4:13:4 | ... = ... | scopes.rb:7:1:7:1 | a |
| scopes.rb:13:7:13:7 | ... = ... | scopes.rb:13:7:13:7 | b |
@@ -120,11 +120,11 @@ definition
| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self |
| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements |
| ssa.rb:26:3:28:5 | ... = ... | ssa.rb:26:7:26:10 | elem |
-| ssa.rb:26:3:28:5 | | ssa.rb:25:1:30:3 | self |
+| ssa.rb:26:3:28:5 | self | ssa.rb:25:1:30:3 | self |
| ssa.rb:26:3:28:5 | __synth__0__1 | ssa.rb:26:3:28:5 | __synth__0__1 |
| ssa.rb:26:3:28:5 | call to each | ssa.rb:26:7:26:10 | elem |
| ssa.rb:32:1:36:3 | self (m3) | ssa.rb:32:1:36:3 | self |
-| ssa.rb:33:16:35:5 | | ssa.rb:32:1:36:3 | self |
+| ssa.rb:33:16:35:5 | self | ssa.rb:32:1:36:3 | self |
| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x |
| ssa.rb:38:1:42:3 | self (m4) | ssa.rb:38:1:42:3 | self |
| ssa.rb:40:3:40:9 | ... = ... | ssa.rb:40:3:40:4 | m3 |
@@ -147,19 +147,19 @@ definition
| ssa.rb:64:8:64:8 | a | ssa.rb:64:8:64:8 | a |
| ssa.rb:65:3:65:15 | ... = ... | ssa.rb:65:3:65:10 | captured |
| ssa.rb:66:3:70:5 | call to times | ssa.rb:65:3:65:10 | captured |
-| ssa.rb:66:11:70:5 | | ssa.rb:64:1:72:3 | self |
-| ssa.rb:66:11:70:5 | | ssa.rb:65:3:65:10 | captured |
+| ssa.rb:66:11:70:5 | captured | ssa.rb:65:3:65:10 | captured |
+| ssa.rb:66:11:70:5 | self | ssa.rb:64:1:72:3 | self |
| ssa.rb:66:15:66:15 | a | ssa.rb:66:15:66:15 | a |
| ssa.rb:69:5:69:17 | ... = ... | ssa.rb:65:3:65:10 | captured |
| ssa.rb:74:1:79:3 | self (m10) | ssa.rb:74:1:79:3 | self |
| ssa.rb:75:3:75:14 | ... = ... | ssa.rb:75:3:75:10 | captured |
-| ssa.rb:76:7:78:5 | | ssa.rb:74:1:79:3 | self |
-| ssa.rb:76:7:78:5 | | ssa.rb:75:3:75:10 | captured |
+| ssa.rb:76:7:78:5 | captured | ssa.rb:75:3:75:10 | captured |
+| ssa.rb:76:7:78:5 | self | ssa.rb:74:1:79:3 | self |
| ssa.rb:81:1:88:3 | self (m11) | ssa.rb:81:1:88:3 | self |
| ssa.rb:82:3:82:14 | ... = ... | ssa.rb:82:3:82:10 | captured |
-| ssa.rb:83:7:87:5 | | ssa.rb:81:1:88:3 | self |
-| ssa.rb:84:10:86:8 | | ssa.rb:81:1:88:3 | self |
-| ssa.rb:84:10:86:8 | | ssa.rb:82:3:82:10 | captured |
+| ssa.rb:83:7:87:5 | self | ssa.rb:81:1:88:3 | self |
+| ssa.rb:84:10:86:8 | captured | ssa.rb:82:3:82:10 | captured |
+| ssa.rb:84:10:86:8 | self | ssa.rb:81:1:88:3 | self |
read
| class_variables.rb:1:1:1:3 | self (class_variables.rb) | class_variables.rb:1:1:29:4 | self | class_variables.rb:3:1:3:5 | self |
| class_variables.rb:5:1:7:3 | self (print) | class_variables.rb:5:1:7:3 | self | class_variables.rb:6:2:6:6 | self |
@@ -179,8 +179,8 @@ read
| instance_variables.rb:15:3:17:5 | self (m) | instance_variables.rb:15:3:17:5 | self | instance_variables.rb:16:5:16:6 | self |
| instance_variables.rb:20:1:25:3 | self (M) | instance_variables.rb:20:1:25:3 | self | instance_variables.rb:21:2:21:3 | self |
| instance_variables.rb:22:2:24:4 | self (n) | instance_variables.rb:22:2:24:4 | self | instance_variables.rb:23:4:23:5 | self |
-| instance_variables.rb:27:6:29:1 | | instance_variables.rb:1:1:44:4 | self | instance_variables.rb:28:3:28:4 | self |
-| instance_variables.rb:32:10:32:21 | | instance_variables.rb:31:1:33:3 | self | instance_variables.rb:32:12:32:13 | self |
+| instance_variables.rb:27:6:29:1 | self | instance_variables.rb:1:1:44:4 | self | instance_variables.rb:28:3:28:4 | self |
+| instance_variables.rb:32:10:32:21 | self | instance_variables.rb:31:1:33:3 | self | instance_variables.rb:32:12:32:13 | self |
| instance_variables.rb:35:1:44:4 | self (C) | instance_variables.rb:35:1:44:4 | self | instance_variables.rb:36:3:36:4 | self |
| instance_variables.rb:37:3:43:5 | self (x) | instance_variables.rb:37:3:43:5 | self | instance_variables.rb:41:4:41:4 | self |
| instance_variables.rb:37:3:43:5 | self (x) | instance_variables.rb:37:3:43:5 | self | instance_variables.rb:42:4:42:7 | self |
@@ -202,8 +202,8 @@ read
| nested_scopes.rb:13:11:13:15 | ... = ... | nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:15:11:15:11 | a |
| nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:16:13:16:13 | a |
| nested_scopes.rb:17:15:17:19 | ... = ... | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:18:15:18:15 | a |
-| nested_scopes.rb:18:23:18:36 | | nested_scopes.rb:12:9:21:11 | self | nested_scopes.rb:18:29:18:34 | self |
-| nested_scopes.rb:18:23:18:36 | | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:18:34:18:34 | a |
+| nested_scopes.rb:18:23:18:36 | a | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:18:34:18:34 | a |
+| nested_scopes.rb:18:23:18:36 | self | nested_scopes.rb:12:9:21:11 | self | nested_scopes.rb:18:29:18:34 | self |
| nested_scopes.rb:22:9:24:11 | self (show_a2) | nested_scopes.rb:22:9:24:11 | self | nested_scopes.rb:23:11:23:16 | self |
| nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:23:16:23:16 | a |
| nested_scopes.rb:27:7:29:9 | self (show) | nested_scopes.rb:27:7:29:9 | self | nested_scopes.rb:28:11:28:16 | self |
@@ -211,8 +211,8 @@ read
| nested_scopes.rb:30:16:30:19 | self (class << ...) | nested_scopes.rb:30:7:33:9 | self | nested_scopes.rb:32:11:32:16 | self |
| nested_scopes.rb:31:11:31:16 | ... = ... | nested_scopes.rb:31:11:31:11 | a | nested_scopes.rb:32:16:32:16 | a |
| nested_scopes.rb:40:1:40:18 | ... = ... | nested_scopes.rb:40:1:40:1 | d | nested_scopes.rb:41:1:41:1 | d |
-| parameters.rb:1:9:5:3 | | parameters.rb:1:1:62:1 | self | parameters.rb:3:4:3:9 | self |
-| parameters.rb:1:9:5:3 | | parameters.rb:1:1:62:1 | self | parameters.rb:4:4:4:9 | self |
+| parameters.rb:1:9:5:3 | self | parameters.rb:1:1:62:1 | self | parameters.rb:3:4:3:9 | self |
+| parameters.rb:1:9:5:3 | self | parameters.rb:1:1:62:1 | self | parameters.rb:4:4:4:9 | self |
| parameters.rb:1:14:1:14 | x | parameters.rb:1:14:1:14 | x | parameters.rb:3:9:3:9 | x |
| parameters.rb:2:4:2:8 | ... = ... | parameters.rb:1:18:1:18 | y | parameters.rb:4:9:4:9 | y |
| parameters.rb:7:1:13:3 | self (order_pizza) | parameters.rb:7:1:13:3 | self | parameters.rb:9:5:9:33 | self |
@@ -222,7 +222,7 @@ read
| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:8:6:8:11 | pizzas |
| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:11:14:11:19 | pizzas |
| parameters.rb:15:17:15:19 | map | parameters.rb:15:17:15:19 | map | parameters.rb:16:3:16:5 | map |
-| parameters.rb:16:12:18:5 | | parameters.rb:15:1:19:3 | self | parameters.rb:17:5:17:28 | self |
+| parameters.rb:16:12:18:5 | self | parameters.rb:15:1:19:3 | self | parameters.rb:17:5:17:28 | self |
| parameters.rb:16:16:16:18 | key | parameters.rb:16:16:16:18 | key | parameters.rb:17:13:17:15 | key |
| parameters.rb:16:21:16:25 | value | parameters.rb:16:21:16:25 | value | parameters.rb:17:22:17:26 | value |
| parameters.rb:21:17:21:21 | block | parameters.rb:21:17:21:21 | block | parameters.rb:22:3:22:7 | block |
@@ -246,8 +246,8 @@ read
| parameters.rb:49:1:51:3 | self (tuples) | parameters.rb:49:1:51:3 | self | parameters.rb:50:3:50:18 | self |
| parameters.rb:49:13:49:13 | a | parameters.rb:49:13:49:13 | a | parameters.rb:50:11:50:11 | a |
| parameters.rb:49:15:49:15 | b | parameters.rb:49:15:49:15 | b | parameters.rb:50:16:50:16 | b |
-| parameters.rb:54:9:57:3 | | parameters.rb:1:1:62:1 | self | parameters.rb:55:4:55:9 | self |
-| parameters.rb:54:9:57:3 | | parameters.rb:1:1:62:1 | self | parameters.rb:56:4:56:9 | self |
+| parameters.rb:54:9:57:3 | self | parameters.rb:1:1:62:1 | self | parameters.rb:55:4:55:9 | self |
+| parameters.rb:54:9:57:3 | self | parameters.rb:1:1:62:1 | self | parameters.rb:56:4:56:9 | self |
| parameters.rb:54:14:54:14 | y | parameters.rb:54:14:54:14 | y | parameters.rb:56:9:56:9 | y |
| parameters.rb:55:4:55:9 | phi | parameters.rb:53:1:53:1 | x | parameters.rb:55:9:55:9 | x |
| parameters.rb:59:1:61:3 | self (tuples_nested) | parameters.rb:59:1:61:3 | self | parameters.rb:60:3:60:23 | self |
@@ -255,19 +255,19 @@ read
| parameters.rb:59:23:59:23 | b | parameters.rb:59:23:59:23 | b | parameters.rb:60:16:60:16 | b |
| parameters.rb:59:25:59:25 | c | parameters.rb:59:25:59:25 | c | parameters.rb:60:21:60:21 | c |
| scopes.rb:1:1:1:15 | self (scopes.rb) | scopes.rb:1:1:49:4 | self | scopes.rb:8:1:8:6 | self |
-| scopes.rb:2:9:6:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:3:4:3:9 | self |
-| scopes.rb:2:9:6:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:3:9:3:9 | self |
-| scopes.rb:2:9:6:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:5:4:5:9 | self |
+| scopes.rb:2:9:6:3 | self | scopes.rb:1:1:49:4 | self | scopes.rb:3:4:3:9 | self |
+| scopes.rb:2:9:6:3 | self | scopes.rb:1:1:49:4 | self | scopes.rb:3:9:3:9 | self |
+| scopes.rb:2:9:6:3 | self | scopes.rb:1:1:49:4 | self | scopes.rb:5:4:5:9 | self |
| scopes.rb:4:4:4:8 | ... = ... | scopes.rb:4:4:4:4 | a | scopes.rb:5:9:5:9 | a |
| scopes.rb:7:1:7:5 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:8:6:8:6 | a |
-| scopes.rb:9:9:18:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:10:4:10:9 | self |
-| scopes.rb:9:9:18:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:12:4:12:9 | self |
-| scopes.rb:9:9:18:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:14:4:14:9 | self |
-| scopes.rb:9:9:18:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:15:4:15:9 | self |
-| scopes.rb:9:9:18:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:16:4:16:9 | self |
-| scopes.rb:9:9:18:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:17:4:17:9 | self |
-| scopes.rb:9:9:18:3 | | scopes.rb:7:1:7:1 | a | scopes.rb:10:9:10:9 | a |
-| scopes.rb:9:9:18:3 | | scopes.rb:7:1:7:1 | a | scopes.rb:11:4:11:4 | a |
+| scopes.rb:9:9:18:3 | a | scopes.rb:7:1:7:1 | a | scopes.rb:10:9:10:9 | a |
+| scopes.rb:9:9:18:3 | a | scopes.rb:7:1:7:1 | a | scopes.rb:11:4:11:4 | a |
+| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:49:4 | self | scopes.rb:10:4:10:9 | self |
+| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:49:4 | self | scopes.rb:12:4:12:9 | self |
+| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:49:4 | self | scopes.rb:14:4:14:9 | self |
+| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:49:4 | self | scopes.rb:15:4:15:9 | self |
+| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:49:4 | self | scopes.rb:16:4:16:9 | self |
+| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:49:4 | self | scopes.rb:17:4:17:9 | self |
| scopes.rb:11:4:11:9 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:12:9:12:9 | a |
| scopes.rb:13:4:13:4 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:14:9:14:9 | a |
| scopes.rb:13:7:13:7 | ... = ... | scopes.rb:13:7:13:7 | b | scopes.rb:15:9:15:9 | b |
@@ -308,10 +308,10 @@ read
| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self | ssa.rb:29:3:29:11 | self |
| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:26:15:26:22 | elements |
| ssa.rb:26:3:28:5 | ... = ... | ssa.rb:26:7:26:10 | elem | ssa.rb:27:10:27:13 | elem |
-| ssa.rb:26:3:28:5 | | ssa.rb:25:1:30:3 | self | ssa.rb:27:5:27:13 | self |
+| ssa.rb:26:3:28:5 | self | ssa.rb:25:1:30:3 | self | ssa.rb:27:5:27:13 | self |
| ssa.rb:26:3:28:5 | __synth__0__1 | ssa.rb:26:3:28:5 | __synth__0__1 | ssa.rb:26:3:28:5 | __synth__0__1 |
| ssa.rb:26:3:28:5 | call to each | ssa.rb:26:7:26:10 | elem | ssa.rb:29:8:29:11 | elem |
-| ssa.rb:33:16:35:5 | | ssa.rb:32:1:36:3 | self | ssa.rb:34:5:34:10 | self |
+| ssa.rb:33:16:35:5 | self | ssa.rb:32:1:36:3 | self | ssa.rb:34:5:34:10 | self |
| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x | ssa.rb:34:10:34:10 | x |
| ssa.rb:38:1:42:3 | self (m4) | ssa.rb:38:1:42:3 | self | ssa.rb:39:3:39:9 | self |
| ssa.rb:38:1:42:3 | self (m4) | ssa.rb:38:1:42:3 | self | ssa.rb:39:8:39:9 | self |
@@ -331,18 +331,18 @@ read
| ssa.rb:64:1:72:3 | self (m9) | ssa.rb:64:1:72:3 | self | ssa.rb:71:3:71:15 | self |
| ssa.rb:64:8:64:8 | a | ssa.rb:64:8:64:8 | a | ssa.rb:66:3:66:3 | a |
| ssa.rb:66:3:70:5 | call to times | ssa.rb:65:3:65:10 | captured | ssa.rb:71:8:71:15 | captured |
-| ssa.rb:66:11:70:5 | | ssa.rb:64:1:72:3 | self | ssa.rb:67:5:67:10 | self |
-| ssa.rb:66:11:70:5 | | ssa.rb:64:1:72:3 | self | ssa.rb:68:5:68:17 | self |
-| ssa.rb:66:11:70:5 | | ssa.rb:65:3:65:10 | captured | ssa.rb:68:10:68:17 | captured |
-| ssa.rb:66:11:70:5 | | ssa.rb:65:3:65:10 | captured | ssa.rb:69:5:69:12 | captured |
+| ssa.rb:66:11:70:5 | captured | ssa.rb:65:3:65:10 | captured | ssa.rb:68:10:68:17 | captured |
+| ssa.rb:66:11:70:5 | captured | ssa.rb:65:3:65:10 | captured | ssa.rb:69:5:69:12 | captured |
+| ssa.rb:66:11:70:5 | self | ssa.rb:64:1:72:3 | self | ssa.rb:67:5:67:10 | self |
+| ssa.rb:66:11:70:5 | self | ssa.rb:64:1:72:3 | self | ssa.rb:68:5:68:17 | self |
| ssa.rb:66:15:66:15 | a | ssa.rb:66:15:66:15 | a | ssa.rb:67:10:67:10 | a |
| ssa.rb:74:1:79:3 | self (m10) | ssa.rb:74:1:79:3 | self | ssa.rb:76:3:78:5 | self |
-| ssa.rb:76:7:78:5 | | ssa.rb:74:1:79:3 | self | ssa.rb:77:6:77:23 | self |
-| ssa.rb:76:7:78:5 | | ssa.rb:75:3:75:10 | captured | ssa.rb:77:15:77:22 | captured |
+| ssa.rb:76:7:78:5 | captured | ssa.rb:75:3:75:10 | captured | ssa.rb:77:15:77:22 | captured |
+| ssa.rb:76:7:78:5 | self | ssa.rb:74:1:79:3 | self | ssa.rb:77:6:77:23 | self |
| ssa.rb:81:1:88:3 | self (m11) | ssa.rb:81:1:88:3 | self | ssa.rb:83:3:87:5 | self |
-| ssa.rb:83:7:87:5 | | ssa.rb:81:1:88:3 | self | ssa.rb:84:6:86:8 | self |
-| ssa.rb:84:10:86:8 | | ssa.rb:81:1:88:3 | self | ssa.rb:85:10:85:22 | self |
-| ssa.rb:84:10:86:8 | | ssa.rb:82:3:82:10 | captured | ssa.rb:85:15:85:22 | captured |
+| ssa.rb:83:7:87:5 | self | ssa.rb:81:1:88:3 | self | ssa.rb:84:6:86:8 | self |
+| ssa.rb:84:10:86:8 | captured | ssa.rb:82:3:82:10 | captured | ssa.rb:85:15:85:22 | captured |
+| ssa.rb:84:10:86:8 | self | ssa.rb:81:1:88:3 | self | ssa.rb:85:10:85:22 | self |
firstRead
| class_variables.rb:1:1:1:3 | self (class_variables.rb) | class_variables.rb:1:1:29:4 | self | class_variables.rb:3:1:3:5 | self |
| class_variables.rb:5:1:7:3 | self (print) | class_variables.rb:5:1:7:3 | self | class_variables.rb:6:2:6:6 | self |
@@ -358,8 +358,8 @@ firstRead
| instance_variables.rb:15:3:17:5 | self (m) | instance_variables.rb:15:3:17:5 | self | instance_variables.rb:16:5:16:6 | self |
| instance_variables.rb:20:1:25:3 | self (M) | instance_variables.rb:20:1:25:3 | self | instance_variables.rb:21:2:21:3 | self |
| instance_variables.rb:22:2:24:4 | self (n) | instance_variables.rb:22:2:24:4 | self | instance_variables.rb:23:4:23:5 | self |
-| instance_variables.rb:27:6:29:1 | | instance_variables.rb:1:1:44:4 | self | instance_variables.rb:28:3:28:4 | self |
-| instance_variables.rb:32:10:32:21 | | instance_variables.rb:31:1:33:3 | self | instance_variables.rb:32:12:32:13 | self |
+| instance_variables.rb:27:6:29:1 | self | instance_variables.rb:1:1:44:4 | self | instance_variables.rb:28:3:28:4 | self |
+| instance_variables.rb:32:10:32:21 | self | instance_variables.rb:31:1:33:3 | self | instance_variables.rb:32:12:32:13 | self |
| instance_variables.rb:35:1:44:4 | self (C) | instance_variables.rb:35:1:44:4 | self | instance_variables.rb:36:3:36:4 | self |
| instance_variables.rb:37:3:43:5 | self (x) | instance_variables.rb:37:3:43:5 | self | instance_variables.rb:41:4:41:4 | self |
| instance_variables.rb:38:4:40:6 | self (y) | instance_variables.rb:38:4:40:6 | self | instance_variables.rb:39:6:39:7 | self |
@@ -376,15 +376,15 @@ firstRead
| nested_scopes.rb:13:11:13:15 | ... = ... | nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:14:16:14:16 | a |
| nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:16:13:16:13 | a |
| nested_scopes.rb:17:15:17:19 | ... = ... | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:18:15:18:15 | a |
-| nested_scopes.rb:18:23:18:36 | | nested_scopes.rb:12:9:21:11 | self | nested_scopes.rb:18:29:18:34 | self |
-| nested_scopes.rb:18:23:18:36 | | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:18:34:18:34 | a |
+| nested_scopes.rb:18:23:18:36 | a | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:18:34:18:34 | a |
+| nested_scopes.rb:18:23:18:36 | self | nested_scopes.rb:12:9:21:11 | self | nested_scopes.rb:18:29:18:34 | self |
| nested_scopes.rb:22:9:24:11 | self (show_a2) | nested_scopes.rb:22:9:24:11 | self | nested_scopes.rb:23:11:23:16 | self |
| nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:23:16:23:16 | a |
| nested_scopes.rb:27:7:29:9 | self (show) | nested_scopes.rb:27:7:29:9 | self | nested_scopes.rb:28:11:28:16 | self |
| nested_scopes.rb:30:16:30:19 | self (class << ...) | nested_scopes.rb:30:7:33:9 | self | nested_scopes.rb:32:11:32:16 | self |
| nested_scopes.rb:31:11:31:16 | ... = ... | nested_scopes.rb:31:11:31:11 | a | nested_scopes.rb:32:16:32:16 | a |
| nested_scopes.rb:40:1:40:18 | ... = ... | nested_scopes.rb:40:1:40:1 | d | nested_scopes.rb:41:1:41:1 | d |
-| parameters.rb:1:9:5:3 | | parameters.rb:1:1:62:1 | self | parameters.rb:3:4:3:9 | self |
+| parameters.rb:1:9:5:3 | self | parameters.rb:1:1:62:1 | self | parameters.rb:3:4:3:9 | self |
| parameters.rb:1:14:1:14 | x | parameters.rb:1:14:1:14 | x | parameters.rb:3:9:3:9 | x |
| parameters.rb:2:4:2:8 | ... = ... | parameters.rb:1:18:1:18 | y | parameters.rb:4:9:4:9 | y |
| parameters.rb:7:1:13:3 | self (order_pizza) | parameters.rb:7:1:13:3 | self | parameters.rb:9:5:9:33 | self |
@@ -393,7 +393,7 @@ firstRead
| parameters.rb:7:17:7:22 | client | parameters.rb:7:17:7:22 | client | parameters.rb:11:41:11:46 | client |
| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:8:6:8:11 | pizzas |
| parameters.rb:15:17:15:19 | map | parameters.rb:15:17:15:19 | map | parameters.rb:16:3:16:5 | map |
-| parameters.rb:16:12:18:5 | | parameters.rb:15:1:19:3 | self | parameters.rb:17:5:17:28 | self |
+| parameters.rb:16:12:18:5 | self | parameters.rb:15:1:19:3 | self | parameters.rb:17:5:17:28 | self |
| parameters.rb:16:16:16:18 | key | parameters.rb:16:16:16:18 | key | parameters.rb:17:13:17:15 | key |
| parameters.rb:16:21:16:25 | value | parameters.rb:16:21:16:25 | value | parameters.rb:17:22:17:26 | value |
| parameters.rb:21:17:21:21 | block | parameters.rb:21:17:21:21 | block | parameters.rb:22:3:22:7 | block |
@@ -416,7 +416,7 @@ firstRead
| parameters.rb:49:1:51:3 | self (tuples) | parameters.rb:49:1:51:3 | self | parameters.rb:50:3:50:18 | self |
| parameters.rb:49:13:49:13 | a | parameters.rb:49:13:49:13 | a | parameters.rb:50:11:50:11 | a |
| parameters.rb:49:15:49:15 | b | parameters.rb:49:15:49:15 | b | parameters.rb:50:16:50:16 | b |
-| parameters.rb:54:9:57:3 | | parameters.rb:1:1:62:1 | self | parameters.rb:55:4:55:9 | self |
+| parameters.rb:54:9:57:3 | self | parameters.rb:1:1:62:1 | self | parameters.rb:55:4:55:9 | self |
| parameters.rb:54:14:54:14 | y | parameters.rb:54:14:54:14 | y | parameters.rb:56:9:56:9 | y |
| parameters.rb:55:4:55:9 | phi | parameters.rb:53:1:53:1 | x | parameters.rb:55:9:55:9 | x |
| parameters.rb:59:1:61:3 | self (tuples_nested) | parameters.rb:59:1:61:3 | self | parameters.rb:60:3:60:23 | self |
@@ -424,11 +424,11 @@ firstRead
| parameters.rb:59:23:59:23 | b | parameters.rb:59:23:59:23 | b | parameters.rb:60:16:60:16 | b |
| parameters.rb:59:25:59:25 | c | parameters.rb:59:25:59:25 | c | parameters.rb:60:21:60:21 | c |
| scopes.rb:1:1:1:15 | self (scopes.rb) | scopes.rb:1:1:49:4 | self | scopes.rb:8:1:8:6 | self |
-| scopes.rb:2:9:6:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:3:4:3:9 | self |
+| scopes.rb:2:9:6:3 | self | scopes.rb:1:1:49:4 | self | scopes.rb:3:4:3:9 | self |
| scopes.rb:4:4:4:8 | ... = ... | scopes.rb:4:4:4:4 | a | scopes.rb:5:9:5:9 | a |
| scopes.rb:7:1:7:5 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:8:6:8:6 | a |
-| scopes.rb:9:9:18:3 | | scopes.rb:1:1:49:4 | self | scopes.rb:10:4:10:9 | self |
-| scopes.rb:9:9:18:3 | | scopes.rb:7:1:7:1 | a | scopes.rb:10:9:10:9 | a |
+| scopes.rb:9:9:18:3 | a | scopes.rb:7:1:7:1 | a | scopes.rb:10:9:10:9 | a |
+| scopes.rb:9:9:18:3 | self | scopes.rb:1:1:49:4 | self | scopes.rb:10:4:10:9 | self |
| scopes.rb:11:4:11:9 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:12:9:12:9 | a |
| scopes.rb:13:4:13:4 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:14:9:14:9 | a |
| scopes.rb:13:7:13:7 | ... = ... | scopes.rb:13:7:13:7 | b | scopes.rb:15:9:15:9 | b |
@@ -451,10 +451,10 @@ firstRead
| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self | ssa.rb:29:3:29:11 | self |
| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:26:15:26:22 | elements |
| ssa.rb:26:3:28:5 | ... = ... | ssa.rb:26:7:26:10 | elem | ssa.rb:27:10:27:13 | elem |
-| ssa.rb:26:3:28:5 | | ssa.rb:25:1:30:3 | self | ssa.rb:27:5:27:13 | self |
+| ssa.rb:26:3:28:5 | self | ssa.rb:25:1:30:3 | self | ssa.rb:27:5:27:13 | self |
| ssa.rb:26:3:28:5 | __synth__0__1 | ssa.rb:26:3:28:5 | __synth__0__1 | ssa.rb:26:3:28:5 | __synth__0__1 |
| ssa.rb:26:3:28:5 | call to each | ssa.rb:26:7:26:10 | elem | ssa.rb:29:8:29:11 | elem |
-| ssa.rb:33:16:35:5 | | ssa.rb:32:1:36:3 | self | ssa.rb:34:5:34:10 | self |
+| ssa.rb:33:16:35:5 | self | ssa.rb:32:1:36:3 | self | ssa.rb:34:5:34:10 | self |
| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x | ssa.rb:34:10:34:10 | x |
| ssa.rb:38:1:42:3 | self (m4) | ssa.rb:38:1:42:3 | self | ssa.rb:39:3:39:9 | self |
| ssa.rb:40:3:40:9 | ... = ... | ssa.rb:40:3:40:4 | m3 | ssa.rb:41:8:41:9 | m3 |
@@ -472,16 +472,16 @@ firstRead
| ssa.rb:64:1:72:3 | self (m9) | ssa.rb:64:1:72:3 | self | ssa.rb:71:3:71:15 | self |
| ssa.rb:64:8:64:8 | a | ssa.rb:64:8:64:8 | a | ssa.rb:66:3:66:3 | a |
| ssa.rb:66:3:70:5 | call to times | ssa.rb:65:3:65:10 | captured | ssa.rb:71:8:71:15 | captured |
-| ssa.rb:66:11:70:5 | | ssa.rb:64:1:72:3 | self | ssa.rb:67:5:67:10 | self |
-| ssa.rb:66:11:70:5 | | ssa.rb:65:3:65:10 | captured | ssa.rb:68:10:68:17 | captured |
+| ssa.rb:66:11:70:5 | captured | ssa.rb:65:3:65:10 | captured | ssa.rb:68:10:68:17 | captured |
+| ssa.rb:66:11:70:5 | self | ssa.rb:64:1:72:3 | self | ssa.rb:67:5:67:10 | self |
| ssa.rb:66:15:66:15 | a | ssa.rb:66:15:66:15 | a | ssa.rb:67:10:67:10 | a |
| ssa.rb:74:1:79:3 | self (m10) | ssa.rb:74:1:79:3 | self | ssa.rb:76:3:78:5 | self |
-| ssa.rb:76:7:78:5 | | ssa.rb:74:1:79:3 | self | ssa.rb:77:6:77:23 | self |
-| ssa.rb:76:7:78:5 | | ssa.rb:75:3:75:10 | captured | ssa.rb:77:15:77:22 | captured |
+| ssa.rb:76:7:78:5 | captured | ssa.rb:75:3:75:10 | captured | ssa.rb:77:15:77:22 | captured |
+| ssa.rb:76:7:78:5 | self | ssa.rb:74:1:79:3 | self | ssa.rb:77:6:77:23 | self |
| ssa.rb:81:1:88:3 | self (m11) | ssa.rb:81:1:88:3 | self | ssa.rb:83:3:87:5 | self |
-| ssa.rb:83:7:87:5 | | ssa.rb:81:1:88:3 | self | ssa.rb:84:6:86:8 | self |
-| ssa.rb:84:10:86:8 | | ssa.rb:81:1:88:3 | self | ssa.rb:85:10:85:22 | self |
-| ssa.rb:84:10:86:8 | | ssa.rb:82:3:82:10 | captured | ssa.rb:85:15:85:22 | captured |
+| ssa.rb:83:7:87:5 | self | ssa.rb:81:1:88:3 | self | ssa.rb:84:6:86:8 | self |
+| ssa.rb:84:10:86:8 | captured | ssa.rb:82:3:82:10 | captured | ssa.rb:85:15:85:22 | captured |
+| ssa.rb:84:10:86:8 | self | ssa.rb:81:1:88:3 | self | ssa.rb:85:10:85:22 | self |
lastRead
| class_variables.rb:1:1:1:3 | self (class_variables.rb) | class_variables.rb:1:1:29:4 | self | class_variables.rb:3:1:3:5 | self |
| class_variables.rb:5:1:7:3 | self (print) | class_variables.rb:5:1:7:3 | self | class_variables.rb:6:2:6:6 | self |
@@ -498,8 +498,8 @@ lastRead
| instance_variables.rb:15:3:17:5 | self (m) | instance_variables.rb:15:3:17:5 | self | instance_variables.rb:16:5:16:6 | self |
| instance_variables.rb:20:1:25:3 | self (M) | instance_variables.rb:20:1:25:3 | self | instance_variables.rb:21:2:21:3 | self |
| instance_variables.rb:22:2:24:4 | self (n) | instance_variables.rb:22:2:24:4 | self | instance_variables.rb:23:4:23:5 | self |
-| instance_variables.rb:27:6:29:1 | | instance_variables.rb:1:1:44:4 | self | instance_variables.rb:28:3:28:4 | self |
-| instance_variables.rb:32:10:32:21 |