Files
codeql/rust/ql/lib/codeql/rust/security/HardcodedCryptographicValueExtensions.qll
2026-07-02 11:26:26 +01:00

189 lines
5.9 KiB
Plaintext

/**
* Provides classes and predicates for reasoning about hard-coded cryptographic value
* vulnerabilities.
*/
import rust
private import codeql.rust.dataflow.DataFlow
private import codeql.rust.dataflow.FlowBarrier
private import codeql.rust.dataflow.FlowSource
private import codeql.rust.dataflow.FlowSink
private import codeql.rust.Concepts
private import codeql.rust.security.SensitiveData
private import codeql.rust.dataflow.internal.Node as Node
/**
* A kind of cryptographic value.
*/
class CryptographicValueKind extends string {
CryptographicValueKind() { this = ["password", "key", "iv", "nonce", "salt"] }
/**
* Gets a description of this value kind for user-facing messages.
*/
string getDescription() {
this = "password" and result = "a password"
or
this = "key" and result = "a key"
or
this = "iv" and result = "an initialization vector"
or
this = "nonce" and result = "a nonce"
or
this = "salt" and result = "a salt"
}
}
/**
* Provides default sources, sinks and barriers for detecting hard-coded cryptographic
* value vulnerabilities, as well as extension points for adding your own.
*/
module HardcodedCryptographicValue {
/**
* A data flow source for hard-coded cryptographic value vulnerabilities.
*/
abstract class Source extends DataFlow::Node { }
/**
* A data flow sink for hard-coded cryptographic value vulnerabilities.
*/
abstract class Sink extends QuerySink::Range {
override string getSinkType() { result = "HardcodedCryptographicValue" }
/**
* Gets the kind of credential this sink is interpreted as.
*/
abstract CryptographicValueKind getKind();
}
/**
* A barrier for hard-coded cryptographic value vulnerabilities.
*/
abstract class Barrier extends DataFlow::Node { }
/**
* Holds if `e` is a literal or a combination of literals that is constant.
*/
private predicate isConstant(Expr e) {
e instanceof LiteralExpr // e.g. `0`
or
forex(Expr elem | elem = e.(ArrayListExpr).getExpr(_) | isConstant(elem)) // e.g. `[0, 0, 0, 0]`
or
isConstant(e.(ArrayRepeatExpr).getRepeatOperand()) // e.g. `[0; 10]`
or
// e.g. `const MY_CONST: u64 = ...`
// the constant initializer / body is the preferred source location for flow paths, when available.
e = any(Const c).getBody()
or
// e.g. `u64::MAX`
// when the constant initializer is not available as a source location (case above), use the access instead.
e instanceof ConstAccess and
not exists(e.(ConstAccess).getConst().getBody())
or
// e.g. `1 << 4`
isConstant(e.(BinaryExpr).getLhs()) and
isConstant(e.(BinaryExpr).getRhs())
}
/**
* A constant, considered as a flow source.
*/
private class ConstantSource extends Source {
ConstantSource() { isConstant(this.asExpr()) }
}
/**
* An externally modeled source for constant values.
*/
private class ModeledSource extends Source {
ModeledSource() { sourceNode(this, "constant-source") }
}
/**
* An externally modeled sink for hard-coded cryptographic value vulnerabilities.
*/
private class ModelsAsDataSinks extends Sink {
CryptographicValueKind kind;
ModelsAsDataSinks() { sinkNode(this, "credentials-" + kind) }
override CryptographicValueKind getKind() { result = kind }
}
/**
* A heuristic sink for hard-coded cryptographic value vulnerabilities.
*/
private class HeuristicSinks extends Sink {
CryptographicValueKind kind;
HeuristicSinks() {
// any argument going to a parameter whose name matches a credential name
exists(Call c, Function f, int argIndex, string argName |
c.getPositionalArgument(argIndex) = this.asExpr() and
c.getStaticTarget() = f and
f.getParam(argIndex).getPat().(IdentPat).getName().getText() = argName and
(
argName = "password" and kind = "password"
or
argName = "iv" and kind = "iv"
or
argName = "nonce" and kind = "nonce"
or
argName = "salt" and kind = "salt"
//
// note: matching "key" results in too many false positives
) and
// don't duplicate modeled sinks
not exists(ModelsAsDataSinks s | s.(Node::FlowSummaryNode).getSinkElement().getCall() = c)
)
}
override CryptographicValueKind getKind() { result = kind }
}
/**
* An externally modeled barrier for hard-coded cryptographic value vulnerabilities.
*
* Note that a barrier will block flow to all hard-coded cryptographic value
* sinks, regardless of the `kind` that is specified. For example a barrier of
* kind `credentials-key` will block flow to a sink of kind `credentials-iv`.
*/
private class ModelsAsDataBarrier extends Barrier {
ModelsAsDataBarrier() {
exists(CryptographicValueKind kind | barrierNode(this, "credentials-" + kind))
}
}
/**
* A call to `getrandom` that is a barrier.
*/
private class GetRandomBarrier extends Barrier {
GetRandomBarrier() {
exists(Call call |
call.getStaticTarget().getCanonicalPath() = ["getrandom::fill", "getrandom::getrandom"] and
this.asExpr().getParentNode*() = call.getPositionalArgument(0)
)
}
}
/**
* An arithmetic or bitwise operation that acts as a barrier.
*
* This prevents false positives where a hard-coded value is combined with
* non-constant data through operations like `+`, `^`, or `+=` (including string concatenation).
*/
private class ArithmeticOperationBarrier extends Barrier {
ArithmeticOperationBarrier() {
// binary operations (e.g. `a + b`, `a ^ b`)
this.asExpr() = any(BinaryArithmeticOperation a).getAnOperand()
or
this.asExpr() = any(BinaryBitwiseOperation a).getAnOperand()
or
// compound assignments (e.g. `a += b`, `a ^= b`)
this.asExpr() = any(AssignArithmeticOperation a).getAnOperand()
or
this.asExpr() = any(AssignBitwiseOperation a).getAnOperand()
}
}
}