mirror of
https://github.com/github/codeql.git
synced 2026-01-15 15:34:49 +01:00
174 lines
5.1 KiB
Plaintext
174 lines
5.1 KiB
Plaintext
/** Provides classes and methods shared by randomness-related queries. */
|
|
|
|
import java
|
|
import semmle.code.java.dataflow.DefUse
|
|
import semmle.code.java.dataflow.DataFlow6
|
|
import RandomDataSource
|
|
|
|
/**
|
|
* The `java.security.SecureRandom` class.
|
|
*/
|
|
class SecureRandomNumberGenerator extends RefType {
|
|
SecureRandomNumberGenerator() { this.hasQualifiedName("java.security", "SecureRandom") }
|
|
}
|
|
|
|
/**
|
|
* A method access calling a method declared on `java.security.SecureRandom`
|
|
* that returns random data or writes random data to an argument.
|
|
*/
|
|
class GetRandomData extends StdlibRandomSource {
|
|
GetRandomData() { this.getQualifier().getType() instanceof SecureRandomNumberGenerator }
|
|
}
|
|
|
|
private predicate isSeeded(RValue use) {
|
|
isSeeding(_, use)
|
|
or
|
|
exists(GetRandomData da, RValue seeduse |
|
|
da.getQualifier() = seeduse and
|
|
useUsePair(seeduse, use)
|
|
)
|
|
}
|
|
|
|
private class PredictableSeedFlowConfiguration extends DataFlow6::Configuration {
|
|
PredictableSeedFlowConfiguration() { this = "Random::PredictableSeedFlowConfiguration" }
|
|
|
|
override predicate isSource(DataFlow6::Node source) {
|
|
source.asExpr() instanceof PredictableSeedExpr
|
|
}
|
|
|
|
override predicate isSink(DataFlow6::Node sink) { isSeeding(sink.asExpr(), _) }
|
|
|
|
override predicate isAdditionalFlowStep(DataFlow6::Node node1, DataFlow6::Node node2) {
|
|
predictableCalcStep(node1.asExpr(), node2.asExpr())
|
|
}
|
|
}
|
|
|
|
private predicate predictableCalcStep(Expr e1, Expr e2) {
|
|
e2.(BinaryExpr).hasOperands(e1, any(PredictableSeedExpr p))
|
|
or
|
|
exists(AssignOp a | a = e2 | e1 = a.getDest() and a.getRhs() instanceof PredictableSeedExpr)
|
|
or
|
|
exists(ConstructorCall cc, TypeNumber t | cc = e2 |
|
|
cc.getArgument(0) = e1 and
|
|
t.hasSubtype*(cc.getConstructedType())
|
|
)
|
|
or
|
|
exists(Method m, MethodAccess ma |
|
|
ma = e2 and
|
|
e1 = ma.getQualifier() and
|
|
m = ma.getMethod() and
|
|
exists(TypeNumber t | hasDescendant(t, m.getDeclaringType())) and
|
|
(
|
|
m.getName().matches("to%String") or
|
|
m.getName() = "toByteArray" or
|
|
m.getName().matches("%Value")
|
|
)
|
|
)
|
|
or
|
|
exists(Method m, MethodAccess ma |
|
|
ma = e2 and
|
|
e1 = ma.getArgument(0) and
|
|
m = ma.getMethod() and
|
|
exists(TypeNumber t | hasDescendant(t, m.getDeclaringType())) and
|
|
(
|
|
m.getName().matches("parse%") or
|
|
m.getName().matches("valueOf%") or
|
|
m.getName().matches("to%String")
|
|
)
|
|
)
|
|
}
|
|
|
|
private predicate safelySeeded(RValue use) {
|
|
exists(Expr arg |
|
|
isSeeding(arg, use) and
|
|
not exists(PredictableSeedFlowConfiguration conf | conf.hasFlowToExpr(arg))
|
|
)
|
|
or
|
|
exists(GetRandomData da, RValue seeduse |
|
|
da.getQualifier() = seeduse and useUsePair(seeduse, use)
|
|
|
|
|
not exists(RValue prior | useUsePair(prior, seeduse) | isSeeded(prior))
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if predictable seed `source` is used to initialise a random-number generator
|
|
* used at `use`.
|
|
*/
|
|
predicate unsafelySeeded(RValue use, PredictableSeedExpr source) {
|
|
isSeedingSource(_, use, source) and
|
|
not safelySeeded(use)
|
|
}
|
|
|
|
private predicate isSeeding(Expr arg, RValue use) {
|
|
exists(Expr e, VariableAssign def |
|
|
def.getSource() = e and
|
|
isSeedingConstruction(e, arg)
|
|
|
|
|
defUsePair(def, use) or
|
|
def.getDestVar().(Field).getAnAccess() = use
|
|
)
|
|
or
|
|
exists(Expr e, RValue seeduse |
|
|
e.(MethodAccess).getQualifier() = seeduse and
|
|
isRandomSeeding(e, arg) and
|
|
useUsePair(seeduse, use)
|
|
)
|
|
}
|
|
|
|
private predicate isSeedingSource(Expr arg, RValue use, Expr source) {
|
|
isSeeding(arg, use) and
|
|
exists(PredictableSeedFlowConfiguration conf |
|
|
conf.hasFlow(DataFlow6::exprNode(source), DataFlow6::exprNode(arg))
|
|
)
|
|
}
|
|
|
|
private predicate isRandomSeeding(MethodAccess m, Expr arg) {
|
|
exists(Method def | m.getMethod() = def |
|
|
def.getDeclaringType() instanceof SecureRandomNumberGenerator and
|
|
def.getName() = "setSeed" and
|
|
arg = m.getArgument(0)
|
|
)
|
|
}
|
|
|
|
private predicate isSeedingConstruction(ClassInstanceExpr c, Expr arg) {
|
|
c.getConstructedType() instanceof SecureRandomNumberGenerator and
|
|
c.getNumArgument() = 1 and
|
|
c.getArgument(0) = arg
|
|
}
|
|
|
|
/**
|
|
* A constant, call to a `ReturnsPredictableExpr` method, or an array initialiser
|
|
* consisting entirely of the same.
|
|
*/
|
|
class PredictableSeedExpr extends Expr {
|
|
PredictableSeedExpr() {
|
|
this.(MethodAccess).getCallee() instanceof ReturnsPredictableExpr
|
|
or
|
|
this instanceof CompileTimeConstantExpr
|
|
or
|
|
this.(ArrayCreationExpr).getInit() instanceof PredictableSeedExpr
|
|
or
|
|
exists(ArrayInit init | init = this |
|
|
forall(Expr e | e = init.getAnInit() | e instanceof PredictableSeedExpr)
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A method whose return value is predictable (not necessarily constant).
|
|
*
|
|
* Extend this class in order that all randomness-related queries should consider the result
|
|
* of a particular method predictable when noting bad RNG seeding and related issues.
|
|
*/
|
|
abstract class ReturnsPredictableExpr extends Method { }
|
|
|
|
private class ReturnsSystemTime extends ReturnsPredictableExpr {
|
|
ReturnsSystemTime() {
|
|
this.getDeclaringType().hasQualifiedName("java.lang", "System") and
|
|
this.hasName("currentTimeMillis")
|
|
or
|
|
this.getDeclaringType().hasQualifiedName("java.lang", "System") and this.hasName("nanoTime")
|
|
}
|
|
}
|