Skip to content

Commit 0247a61

Browse files
committed
JS: Replace another use of Type
1 parent 83f785c commit 0247a61

File tree

3 files changed

+29
-12
lines changed

3 files changed

+29
-12
lines changed

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

Lines changed: 5 additions & 12 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.NameResolution
89
private import semmle.javascript.internal.TypeResolution
910

1011
/**
@@ -317,14 +318,6 @@ module NestJS {
317318
}
318319
}
319320

320-
private predicate isStringType(Type type) {
321-
type instanceof StringType
322-
or
323-
type instanceof AnyType
324-
or
325-
isStringType(type.(PromiseType).getElementType().unfold())
326-
}
327-
328321
/**
329322
* A return value from a route handler, seen as an argument to `res.send()`.
330323
*
@@ -343,10 +336,10 @@ module NestJS {
343336
ReturnValueAsResponseSend() {
344337
handler.isReturnValueReflected() and
345338
this = handler.getAReturn() and
346-
// Only returned strings are sinks
347-
not exists(Type type |
348-
type = this.asExpr().getType() and
349-
not isStringType(type.unfold())
339+
// Only returned strings are sinks. If we can find a type for the return value, it must be string-like.
340+
not exists(NameResolution::Node type |
341+
TypeResolution::valueHasType(this.asExpr(), type) and
342+
not TypeResolution::hasUnderlyingStringOrAnyType(type)
350343
)
351344
}
352345

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,4 +360,25 @@ module TypeResolution {
360360
* Holds if the given value has a type that implied it is a Promise object. Does not hold for unions unless all parts of the union are promises.
361361
*/
362362
predicate valueHasPromiseType = ValueHasProperty<isPromiseType/1>::valueHasProperty/1;
363+
364+
/**
365+
* Holds if `type` contains `string` or `any`, possibly wrapped in a promise.
366+
*/
367+
predicate hasUnderlyingStringOrAnyType(Node type) {
368+
type.(TypeAnnotation).isStringy()
369+
or
370+
type.(TypeAnnotation).isAny()
371+
or
372+
type instanceof StringLiteralTypeExpr
373+
or
374+
type instanceof TemplateLiteralTypeExpr
375+
or
376+
exists(Node mid | hasUnderlyingStringOrAnyType(mid) |
377+
TypeFlow::step(mid, type)
378+
or
379+
UnderlyingTypes::underlyingTypeStep(mid, type)
380+
or
381+
type = unwrapPromiseType(mid)
382+
)
383+
}
363384
}

javascript/ql/test/library-tests/frameworks/Nest/test.expected

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ responseSendArgument
7171
| local/customPipe.ts:37:16:37:31 | '' + unsanitized |
7272
| local/customPipe.ts:42:16:42:31 | '' + unsanitized |
7373
| local/customPipe.ts:48:16:48:31 | '' + unsanitized |
74+
| local/routes.ts:7:12:7:16 | 'foo' |
75+
| local/routes.ts:12:12:12:16 | 'foo' |
76+
| local/routes.ts:17:12:17:16 | 'foo' |
7477
| local/routes.ts:32:31:32:31 | x |
7578
| local/routes.ts:33:31:33:38 | queryObj |
7679
| local/routes.ts:34:31:34:34 | name |

0 commit comments

Comments
 (0)