Copy the structure of the Javascript query

This commit is contained in:
Owen Mansel-Chan
2025-07-18 16:14:45 +01:00
committed by Owen Mansel-Chan
parent 5c403d374e
commit 34b2e3e2bf
11 changed files with 153 additions and 235 deletions

View File

@@ -33,6 +33,7 @@ import semmle.go.frameworks.AwsLambda
import semmle.go.frameworks.Beego
import semmle.go.frameworks.BeegoOrm
import semmle.go.frameworks.Bun
import semmle.go.frameworks.CryptoLibraries
import semmle.go.frameworks.RsCors
import semmle.go.frameworks.Couchbase
import semmle.go.frameworks.Echo

View File

@@ -6,6 +6,7 @@ extractor: go
library: true
upgrades: upgrades
dependencies:
codeql/concepts: ${workspace}
codeql/dataflow: ${workspace}
codeql/mad: ${workspace}
codeql/threat-models: ${workspace}

View File

@@ -8,6 +8,10 @@ import go
import semmle.go.dataflow.FunctionInputsAndOutputs
import semmle.go.concepts.HTTP
import semmle.go.concepts.GeneratedFile
private import codeql.concepts.ConceptsShared
private import semmle.go.dataflow.internal.DataFlowImplSpecific
private module ConceptsShared = ConceptsMake<Location, GoDataFlow>;
/**
* A data-flow node that executes an operating system command,
@@ -505,3 +509,31 @@ module UnmarshalingFunction {
abstract string getFormat();
}
}
/**
* Provides models for cryptographic things.
*/
module Cryptography {
private import ConceptsShared::Cryptography as SC
/**
* A data-flow node that is an application of a cryptographic algorithm. For example,
* encryption, decryption, signature-validation.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `CryptographicOperation::Range` instead.
*/
class CryptographicOperation extends SC::CryptographicOperation { }
class EncryptionAlgorithm = SC::EncryptionAlgorithm;
class HashingAlgorithm = SC::HashingAlgorithm;
class PasswordHashingAlgorithm = SC::PasswordHashingAlgorithm;
module CryptographicOperation = SC::CryptographicOperation;
class BlockMode = SC::BlockMode;
class CryptographicAlgorithm = SC::CryptographicAlgorithm;
}

View File

@@ -3,180 +3,31 @@
*/
import go
/**
* Names of cryptographic algorithms, separated into strong and weak variants.
*
* The names are normalized: upper-case, no spaces, dashes or underscores.
*
* The names are inspired by the names used in real world crypto libraries.
*
* The classification into strong and weak are based on OWASP and Wikipedia (2020).
*
* Sources (more links in qhelp file):
* https://en.wikipedia.org/wiki/Strong_cryptography#Cryptographically_strong_algorithms
* https://en.wikipedia.org/wiki/Strong_cryptography#Examples
* https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html
*/
private module AlgorithmNames {
predicate isStrongHashingAlgorithm(string name) {
name =
[
"DSA", "ED25519", "SHA256", "SHA384", "SHA512", "SHA3", "ES256", "ECDSA256", "ES384",
"ECDSA384", "ES512", "ECDSA512", "SHA2", "SHA224"
]
}
predicate isWeakHashingAlgorithm(string name) {
name =
[
"HAVEL128", "MD2", "SHA1", "MD4", "MD5", "PANAMA", "RIPEMD", "RIPEMD128", "RIPEMD256",
"RIPEMD320", "SHA0"
]
}
predicate isStrongEncryptionAlgorithm(string name) {
name = ["AES", "AES128", "AES192", "AES256", "AES512", "RSA", "RABBIT", "BLOWFISH"]
}
predicate isWeakEncryptionAlgorithm(string name) {
name =
[
"DES", "3DES", "ARC5", "RC5", "TRIPLEDES", "TDEA", "TRIPLEDEA", "ARC2", "RC2", "ARC4",
"RC4", "ARCFOUR"
]
}
predicate isStrongPasswordHashingAlgorithm(string name) {
name = ["ARGON2", "PBKDF2", "BCRYPT", "SCRYPT"]
}
predicate isWeakPasswordHashingAlgorithm(string name) { none() }
}
private import AlgorithmNames
/**
* A cryptographic algorithm.
*/
private newtype TCryptographicAlgorithm =
MkHashingAlgorithm(string name, boolean isWeak) {
isStrongHashingAlgorithm(name) and isWeak = false
or
isWeakHashingAlgorithm(name) and isWeak = true
} or
MkEncryptionAlgorithm(string name, boolean isWeak) {
isStrongEncryptionAlgorithm(name) and isWeak = false
or
isWeakEncryptionAlgorithm(name) and isWeak = true
} or
MkPasswordHashingAlgorithm(string name, boolean isWeak) {
isStrongPasswordHashingAlgorithm(name) and isWeak = false
or
isWeakPasswordHashingAlgorithm(name) and isWeak = true
}
/**
* A cryptographic algorithm.
*/
abstract class CryptographicAlgorithm extends TCryptographicAlgorithm {
/** Gets a textual representation of this element. */
string toString() { result = this.getName() }
/**
* Gets the name of this algorithm.
*/
abstract string getName();
/**
* Holds if the name of this algorithm matches `name` modulo case,
* white space, dashes and underscores.
*/
bindingset[name]
predicate matchesName(string name) {
exists(name.regexpReplaceAll("[-_]", "").regexpFind("(?i)\\Q" + this.getName() + "\\E", _, _))
}
/**
* Holds if this algorithm is weak.
*/
abstract predicate isWeak();
}
/**
* A hashing algorithm such as `MD5` or `SHA512`.
*/
class HashingAlgorithm extends MkHashingAlgorithm, CryptographicAlgorithm {
string name;
boolean isWeak;
HashingAlgorithm() { this = MkHashingAlgorithm(name, isWeak) }
override string getName() { result = name }
override predicate isWeak() { isWeak = true }
}
/**
* An encryption algorithm such as `DES` or `AES512`.
*/
class EncryptionAlgorithm extends MkEncryptionAlgorithm, CryptographicAlgorithm {
string name;
boolean isWeak;
EncryptionAlgorithm() { this = MkEncryptionAlgorithm(name, isWeak) }
override string getName() { result = name }
override predicate isWeak() { isWeak = true }
}
/**
* A password hashing algorithm such as `PBKDF2` or `SCRYPT`.
*/
class PasswordHashingAlgorithm extends MkPasswordHashingAlgorithm, CryptographicAlgorithm {
string name;
boolean isWeak;
PasswordHashingAlgorithm() { this = MkPasswordHashingAlgorithm(name, isWeak) }
override string getName() { result = name }
override predicate isWeak() { isWeak = true }
}
/**
* An application of a cryptographic algorithm.
*/
abstract class CryptographicOperation extends DataFlow::Node {
/**
* Gets the input the algorithm is used on, e.g. the plain text input to be encrypted.
*/
abstract Expr getInput();
/**
* Gets the applied algorithm.
*/
abstract CryptographicAlgorithm getAlgorithm();
}
import semmle.go.Concepts::Cryptography
private import codeql.concepts.internal.CryptoAlgorithmNames
/**
* A cryptographic operation from the `crypto/md5` package.
*/
class Md5 extends CryptographicOperation, DataFlow::CallNode {
Md5() { this.getTarget().hasQualifiedName("crypto/md5", ["New", "Sum"]) }
private module CryptoMd5 {
private class Md5 extends CryptographicOperation::Range instanceof DataFlow::CallNode {
Md5() { this.getTarget().hasQualifiedName("crypto/md5", ["New", "Sum"]) }
override Expr getInput() { result = this.getArgument(0).asExpr() }
override DataFlow::Node getInitialization() { result = this }
override CryptographicAlgorithm getAlgorithm() {
result.matchesName(this.getTarget().getPackage().getName())
override CryptographicAlgorithm getAlgorithm() { result.matchesName("MD5") }
override DataFlow::Node getAnInput() { result = super.getArgument(0) }
// not relevant for md5
override BlockMode getBlockMode() { none() }
}
}
/**
* A cryptographic operation from the `crypto/sha1` package.
*/
class Sha1 extends CryptographicOperation, DataFlow::CallNode {
class Sha1 extends CryptographicOperation::Range instanceof DataFlow::CallNode {
Sha1() { this.getTarget().hasQualifiedName("crypto/sha1", ["New", "Sum"]) }
override Expr getInput() { result = this.getArgument(0).asExpr() }
@@ -189,7 +40,7 @@ class Sha1 extends CryptographicOperation, DataFlow::CallNode {
/**
* A cryptographic operation from the `crypto/des` package.
*/
class Des extends CryptographicOperation, DataFlow::CallNode {
class Des extends CryptographicOperation::Range instanceof DataFlow::CallNode {
Des() { this.getTarget().hasQualifiedName("crypto/des", ["NewCipher", "NewTripleDESCipher"]) }
override Expr getInput() { result = this.getArgument(0).asExpr() }
@@ -202,7 +53,7 @@ class Des extends CryptographicOperation, DataFlow::CallNode {
/**
* A cryptographic operation from the `crypto/rc4` package.
*/
class Rc4 extends CryptographicOperation, DataFlow::CallNode {
class Rc4 extends CryptographicOperation::Range instanceof DataFlow::CallNode {
Rc4() { this.getTarget().hasQualifiedName("crypto/rc4", "NewCipher") }
override Expr getInput() { result = this.getArgument(0).asExpr() }

View File

@@ -0,0 +1,58 @@
/**
* Provides default sources, sinks and sanitizers for reasoning about
* sensitive information in weak cryptographic algorithms,
* as well as extension points for adding your own.
*/
import go
private import semmle.go.security.SensitiveActions
/**
* Provides default sources, sinks and sanitizers for reasoning about
* sensitive information in weak cryptographic algorithms,
* as well as extension points for adding your own.
*/
module BrokenCryptoAlgorithm {
/**
* A data flow source for sensitive information in broken or weak cryptographic algorithms.
*/
abstract class Source extends DataFlow::Node { }
/**
* A data flow sink for sensitive information in broken or weak cryptographic algorithms.
*/
abstract class Sink extends DataFlow::Node {
/** Gets the data-flow node where the cryptographic algorithm used in this operation is configured. */
abstract DataFlow::Node getInitialization();
}
/**
* A sanitizer for sensitive information in broken or weak cryptographic algorithms.
*/
abstract class Sanitizer extends DataFlow::Node { }
/**
* A sensitive source.
*/
class SensitiveSource extends Source {
SensitiveSource() { this.asExpr() instanceof SensitiveExpr }
}
/**
* An expression used by a broken or weak cryptographic algorithm.
*/
class WeakCryptographicOperationSink extends Sink {
CryptographicOperation application;
WeakCryptographicOperationSink() {
(
application.getAlgorithm().isWeak()
or
application.getBlockMode().isWeak()
) and
this = application.getAnInput()
}
override DataFlow::Node getInitialization() { result = application.getInitialization() }
}
}

View File

@@ -0,0 +1,41 @@
/**
* Provides a taint tracking configuration for reasoning about
* sensitive information in broken or weak cryptographic algorithms.
*
* Note, for performance reasons: only import this file if
* `BrokenCryptoAlgorithm::Configuration` is needed, otherwise
* `BrokenCryptoAlgorithmCustomizations` should be imported instead.
*/
import go
import BrokenCryptoAlgorithmCustomizations::BrokenCryptoAlgorithm
/**
* A taint tracking configuration for sensitive information in broken or weak cryptographic algorithms.
*
* This configuration identifies flows from `Source`s, which are sources of
* sensitive data, to `Sink`s, which is an abstract class representing all
* the places sensitive data may used in broken or weak cryptographic algorithms. Additional sources or sinks can be
* added either by extending the relevant class, or by subclassing this configuration itself,
* and amending the sources and sinks.
*/
private module BrokenCryptoAlgorithmConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
predicate observeDiffInformedIncrementalMode() { any() }
Location getASelectedSinkLocation(DataFlow::Node sink) {
result = sink.(Sink).getLocation()
or
result = sink.(Sink).getInitialization().getLocation()
}
}
/**
* Taint tracking flow for sensitive information in broken or weak cryptographic algorithms.
*/
module BrokenCryptoAlgorithmFlow = TaintTracking::Global<BrokenCryptoAlgorithmConfig>;

View File

@@ -1,66 +0,0 @@
/**
* Provides default sources, sinks and sanitizers for reasoning about
* sensitive information in weak cryptographic algorithms,
* as well as extension points for adding your own.
*/
import go
private import semmle.go.frameworks.CryptoLibraries
private import semmle.go.security.SensitiveActions
/**
* Provides default sources, sinks and sanitizers for reasoning about
* sensitive information in weak cryptographic algorithms,
* as well as extension points for adding your own.
*/
module WeakCryptoAlgorithm {
/**
* A data flow source for sensitive information in weak cryptographic algorithms.
*/
abstract class Source extends DataFlow::Node { }
/**
* A data flow sink for sensitive information in weak cryptographic algorithms.
*/
abstract class Sink extends DataFlow::Node { }
/**
* A sanitizer for sensitive information in weak cryptographic algorithms.
*/
abstract class Sanitizer extends DataFlow::Node { }
/**
* A sensitive source.
*/
class SensitiveSource extends Source {
SensitiveSource() { this.asExpr() instanceof SensitiveExpr }
}
/**
* An expression used by a weak cryptographic algorithm.
*/
class WeakCryptographicOperationSink extends Sink {
WeakCryptographicOperationSink() {
exists(CryptographicOperation application |
application.getAlgorithm().isWeak() and
this.asExpr() = application.getInput()
)
}
}
private module Config implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
predicate observeDiffInformedIncrementalMode() { any() }
}
/**
* Tracks taint flow from sensitive information to weak cryptographic
* algorithms.
*/
module Flow = TaintTracking::Global<Config>;
}

View File

@@ -12,10 +12,10 @@
*/
import go
import semmle.go.security.WeakCryptoAlgorithmCustomizations
import WeakCryptoAlgorithm::Flow::PathGraph
import semmle.go.security.BrokenCryptoAlgorithmQuery
import BrokenCryptoAlgorithmFlow::PathGraph
from WeakCryptoAlgorithm::Flow::PathNode source, WeakCryptoAlgorithm::Flow::PathNode sink
where WeakCryptoAlgorithm::Flow::flowPath(source, sink)
from BrokenCryptoAlgorithmFlow::PathNode source, BrokenCryptoAlgorithmFlow::PathNode sink
where BrokenCryptoAlgorithmFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "$@ is used in a weak cryptographic algorithm.",
source.getNode(), "Sensitive data"

View File

@@ -1,4 +1,4 @@
query: Security/CWE-327/WeakCryptoAlgorithm.ql
query: Security/CWE-327/BrokenCryptoAlgorithm.ql
postprocess:
- utils/test/PrettyPrintModels.ql
- utils/test/InlineExpectationsTestQuery.ql