1313import javascript
1414private import semmle.javascript.dataflow.InferredTypes
1515private import semmle.javascript.dataflow.internal.StepSummary
16+ private import semmle.javascript.security.dataflow.InsecureRandomnessCustomizations
1617
1718/**
1819 * Gets a Buffer/TypedArray containing cryptographically secure random numbers.
@@ -29,7 +30,9 @@ private DataFlow::SourceNode randomBufferSource() {
2930 or
3031 result = DataFlow:: moduleImport ( "secure-random" ) .getACall ( )
3132 or
32- result = DataFlow:: moduleImport ( "secure-random" ) .getAMethodCall ( [ "randomArray" , "randomUint8Array" , "randomBuffer" ] )
33+ result =
34+ DataFlow:: moduleImport ( "secure-random" )
35+ .getAMethodCall ( [ "randomArray" , "randomUint8Array" , "randomBuffer" ] )
3336}
3437
3538/**
@@ -41,48 +44,50 @@ private string prop() { result = DataFlow::PseudoProperties::setElement() }
4144/**
4245 * Gets a reference to a cryptographically secure random number, type tracked using `t`.
4346 */
44- private DataFlow:: SourceNode goodRandom ( DataFlow:: TypeTracker t ) {
47+ private DataFlow:: Node goodRandom ( DataFlow:: TypeTracker t ) {
4548 t .startInProp ( prop ( ) ) and
4649 result = randomBufferSource ( )
4750 or
4851 // Loading a number from a `Buffer`.
4952 exists ( DataFlow:: TypeTracker t2 | t = t2 .append ( LoadStep ( prop ( ) ) ) |
5053 // the random generators return arrays/Buffers of random numbers, we therefore track through an indexed read.
51- exists ( DataFlow:: PropRead read |
54+ exists ( DataFlow:: PropRead read | result = read |
5255 read .getBase ( ) = goodRandom ( t2 ) and
53- exists ( read .getPropertyNameExpr ( ) )
56+ not read .getPropertyNameExpr ( ) instanceof Label
5457 )
5558 or
5659 // reading a number from a Buffer.
57- exists ( DataFlow:: MethodCallNode call |
58- call .getReceiver ( ) . getALocalSource ( ) = goodRandom ( t . continue ( ) ) and
60+ exists ( DataFlow:: MethodCallNode call | result = call |
61+ call .getReceiver ( ) = goodRandom ( t2 ) and
5962 call
6063 .getMethodName ( )
6164 .regexpMatch ( "read(BigInt|BigUInt|Double|Float|Int|UInt)(8|16|32|64)?(BE|LE)?" )
6265 )
6366 )
6467 or
65- exists ( DataFlow:: TypeTracker t2 | result = goodRandom ( t2 ) . track ( t2 , t ) )
68+ exists ( DataFlow:: TypeTracker t2 | t = t2 . smallstep ( goodRandom ( t2 ) , result ) )
6669 or
67- // re-using the collection steps for `Set`.
70+ // re-using the collection steps for `Set`.
6871 exists ( DataFlow:: TypeTracker t2 |
6972 result = CollectionsTypeTracking:: collectionStep ( goodRandom ( t2 ) , t , t2 )
7073 )
74+ or
75+ InsecureRandomness:: isAdditionalTaintStep ( goodRandom ( t .continue ( ) ) , result )
7176}
7277
7378/**
7479 * Gets a reference to a cryptographically random number.
7580 */
76- DataFlow:: SourceNode goodRandom ( ) { result = goodRandom ( DataFlow:: TypeTracker:: end ( ) ) }
81+ DataFlow:: Node goodRandom ( ) { result = goodRandom ( DataFlow:: TypeTracker:: end ( ) ) }
7782
7883/**
7984 * Gets a node that that produces a biased result from otherwise cryptographically secure random numbers.
8085 */
8186DataFlow:: Node badCrypto ( string description ) {
8287 // addition and multiplication - always bad when both the lhs and rhs are random.
8388 exists ( BinaryExpr binop | result .asExpr ( ) = binop |
84- goodRandom ( ) .flowsToExpr ( binop .getLeftOperand ( ) ) and
85- goodRandom ( ) .flowsToExpr ( binop .getRightOperand ( ) ) and
89+ goodRandom ( ) .asExpr ( ) = binop .getLeftOperand ( ) and
90+ goodRandom ( ) .asExpr ( ) = binop .getRightOperand ( ) and
8691 (
8792 binop .getOperator ( ) = "+" and description = "addition"
8893 or
@@ -92,22 +97,21 @@ DataFlow::Node badCrypto(string description) {
9297 or
9398 // division - always bad
9499 exists ( DivExpr div | result .asExpr ( ) = div |
95- goodRandom ( ) .flowsToExpr ( div .getLeftOperand ( ) ) and
100+ goodRandom ( ) .asExpr ( ) = div .getLeftOperand ( ) and
96101 description = "division"
97102 )
98103 or
99104 // modulo - only bad if not by a power of 2 - and the result is not checked for bias
100- exists ( ModExpr mod , DataFlow:: SourceNode random |
101- result .asExpr ( ) = mod and mod .getOperator ( ) = "%"
102- |
105+ exists ( ModExpr mod , DataFlow:: Node random | result .asExpr ( ) = mod and mod .getOperator ( ) = "%" |
103106 description = "modulo" and
104107 goodRandom ( ) = random and
105- random .flowsToExpr ( mod .getLeftOperand ( ) ) and
106- // division by a power of 2 is OK. E.g. if `x` is uniformly random is in the range [0..255] then `x % 32` is uniformly random in the range [0..31].
108+ random .asExpr ( ) = mod .getLeftOperand ( ) and
109+ // division by a power of 2 is OK. E.g. if `x` is uniformly random is in the range [0..255] then `x % 32` is uniformly random in the range [0..31].
107110 not mod .getRightOperand ( ) .getIntValue ( ) = [ 2 , 4 , 8 , 16 , 32 , 64 , 128 ] and
108111 // not exists a comparison that checks if the result is potentially biased.
109112 not exists ( BinaryExpr comparison | comparison .getOperator ( ) = [ ">" , "<" , "<=" , ">=" ] |
110- AccessPath:: getAnAliasedSourceNode ( random ) .flowsToExpr ( comparison .getAnOperand ( ) )
113+ AccessPath:: getAnAliasedSourceNode ( random .getALocalSource ( ) )
114+ .flowsToExpr ( comparison .getAnOperand ( ) )
111115 or
112116 exists ( DataFlow:: PropRead otherRead |
113117 otherRead = random .( DataFlow:: PropRead ) .getBase ( ) .getALocalSource ( ) .getAPropertyRead ( ) and
@@ -121,7 +125,7 @@ DataFlow::Node badCrypto(string description) {
121125 exists ( DataFlow:: CallNode number , StringOps:: ConcatenationRoot root | result = number |
122126 number = DataFlow:: globalVarRef ( [ "Number" , "parseInt" , "parseFloat" ] ) .getACall ( ) and
123127 root = number .getArgument ( 0 ) and
124- goodRandom ( ) . flowsTo ( root .getALeaf ( ) ) and
128+ goodRandom ( ) = root .getALeaf ( ) and
125129 exists ( root .getALeaf ( ) .getStringValue ( ) ) and
126130 description = "string concatenation"
127131 )
0 commit comments