Skip to content

Commit 4ec3f64

Browse files
committed
JS: Add tracking of sanitizing primitive types
1 parent 8b7f176 commit 4ec3f64

File tree

2 files changed

+84
-14
lines changed

2 files changed

+84
-14
lines changed

javascript/ql/lib/semmle/javascript/frameworks/Nest.qll

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import javascript
66
private import semmle.javascript.security.dataflow.ServerSideUrlRedirectCustomizations
77
private import semmle.javascript.dataflow.internal.PreCallGraphStep
8+
private import semmle.javascript.internal.TypeResolution
89

910
/**
1011
* Provides classes and predicates for reasoning about [Nest](https://nestjs.com/).
@@ -133,7 +134,9 @@ module NestJS {
133134
hasSanitizingPipe(this, false)
134135
or
135136
hasSanitizingPipe(this, true) and
136-
isSanitizingType(this.getParameter().getType().unfold())
137+
// Note: we could consider types with class-validator decorators to be sanitized here, but instead we consider the root
138+
// object to be tainted, but omit taint steps for the individual properties names that have sanitizing decorators. See ClassValidator.qll.
139+
TypeResolution::isSanitizingPrimitiveType(this.getParameter().getTypeAnnotation())
137140
}
138141
}
139142

@@ -209,19 +212,6 @@ module NestJS {
209212
dependsOnType = true
210213
}
211214

212-
/**
213-
* Holds if a parameter of type `t` is considered sanitized, provided it has been checked by `ValidationPipe`
214-
* (which relies on metadata emitted by the TypeScript compiler).
215-
*/
216-
private predicate isSanitizingType(Type t) {
217-
t instanceof NumberType
218-
or
219-
t instanceof BooleanType
220-
//
221-
// Note: we could consider types with class-validator decorators to be sanitized here, but instead we consider the root
222-
// object to be tainted, but omit taint steps for the individual properties names that have sanitizing decorators. See ClassValidator.qll.
223-
}
224-
225215
/**
226216
* A user-defined pipe class, for example:
227217
* ```js

javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,4 +263,84 @@ module TypeResolution {
263263
typeMember(host, contents.getAReadContent(), type)
264264
)
265265
}
266+
267+
signature predicate nodeSig(Node node);
268+
269+
/**
270+
* Tracks types that have a certain property, in the sense that:
271+
* - an intersection type has the property if any member has the property
272+
* - a union type has the property if all its members have the property
273+
*/
274+
module TrackMustProp<nodeSig/1 directlyHasProperty> {
275+
predicate hasProperty(Node node) {
276+
directlyHasProperty(node)
277+
or
278+
exists(Node mid |
279+
hasProperty(mid) and
280+
TypeFlow::step(mid, node)
281+
)
282+
or
283+
unionHasProp(node)
284+
or
285+
hasProperty(node.(IntersectionTypeExpr).getAnElementType())
286+
or
287+
exists(ConditionalTypeExpr cond |
288+
node = cond and
289+
hasProperty(cond.getTrueType()) and
290+
hasProperty(cond.getFalseType())
291+
)
292+
}
293+
294+
private predicate unionHasProp(UnionTypeExpr node, int n) {
295+
hasProperty(node.getElementType(0)) and n = 1
296+
or
297+
unionHasProp(node, n - 1) and
298+
hasProperty(node.getElementType(n - 1))
299+
}
300+
301+
private predicate unionHasProp(UnionTypeExpr node) {
302+
unionHasProp(node, node.getNumElementType())
303+
}
304+
}
305+
306+
private predicate isSanitizingPrimitiveTypeBase(Node node) {
307+
node.(TypeExpr).isNumbery()
308+
or
309+
node.(TypeExpr).isBooleany()
310+
or
311+
node.(TypeExpr).isNull()
312+
or
313+
node.(TypeExpr).isUndefined()
314+
or
315+
node.(TypeExpr).isVoid()
316+
or
317+
node.(TypeExpr).isNever()
318+
or
319+
node instanceof LiteralTypeExpr
320+
or
321+
node = any(EnumMember m).getIdentifier() // enum members are constant
322+
or
323+
node instanceof EnumDeclaration // enums are unions of constants
324+
}
325+
326+
/**
327+
* Holds if `node` refers to a type that is considered untaintable (if actually enforced at runtime).
328+
*
329+
* Specifically, the types `number`, `boolean`, `null`, `undefined`, `void`, `never`, as well as literal types (`"foo"`)
330+
* and enums and enum members have this property.
331+
*/
332+
predicate isSanitizingPrimitiveType =
333+
TrackMustProp<isSanitizingPrimitiveTypeBase/1>::hasProperty/1;
334+
335+
/**
336+
* Holds if `value` has a type that is considered untaintable (if actually enforced at runtime).
337+
*
338+
* See `isSanitizingPrimitiveType`.
339+
*/
340+
predicate valueHasSanitizingPrimitiveType(Node value) {
341+
exists(Node type |
342+
valueHasType(value, type) and
343+
isSanitizingPrimitiveType(type)
344+
)
345+
}
266346
}

0 commit comments

Comments
 (0)