Skip to content

Commit 16f7373

Browse files
committed
JS: Model dependency injection in Nest
1 parent 89ad737 commit 16f7373

File tree

2 files changed

+52
-1
lines changed
  • javascript/ql

2 files changed

+52
-1
lines changed

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

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import javascript
66
private import semmle.javascript.security.dataflow.ServerSideUrlRedirectCustomizations
7+
private import semmle.javascript.dataflow.internal.PreCallGraphStep
78

89
/**
910
* Provides classes and predicates for reasoning about [Nest](https://nestjs.com/).
@@ -462,4 +463,54 @@ module NestJS {
462463
result.(DataFlow::FunctionNode).getAParameter() = this
463464
}
464465
}
466+
467+
/**
468+
* A value passed in the `providers` array in:
469+
* ```js
470+
* @Module({ providers: [ ... ] })
471+
* class App { ... }
472+
* ```
473+
*/
474+
private DataFlow::Node providerTuple() {
475+
result =
476+
DataFlow::moduleImport("@nestjs/common")
477+
.getAPropertyRead("Module")
478+
.getACall()
479+
.getOptionArgument(0, "providers")
480+
.getALocalSource()
481+
.(DataFlow::ArrayCreationNode)
482+
.getAnElement()
483+
}
484+
485+
private predicate providerPair(DataFlow::Node interface, DataFlow::Node concreteClass) {
486+
exists(DataFlow::SourceNode tuple |
487+
tuple = providerTuple().getALocalSource() and
488+
interface = tuple.getAPropertyWrite("provide").getRhs() and
489+
concreteClass = tuple.getAPropertyWrite("useClass").getRhs()
490+
)
491+
}
492+
493+
/** Gets the class being referenced at `node` without relying on the call graph. */
494+
private DataFlow::ClassNode getClassFromNode(DataFlow::Node node) {
495+
result.getAstNode() = node.analyze().getAValue().(AbstractClass).getClass()
496+
}
497+
498+
private predicate providerClassPair(
499+
DataFlow::ClassNode interface, DataFlow::ClassNode concreteClass
500+
) {
501+
exists(DataFlow::Node interfaceNode, DataFlow::Node concreteClassNode |
502+
providerPair(interfaceNode, concreteClassNode) and
503+
interface = getClassFromNode(interfaceNode) and
504+
concreteClass = getClassFromNode(concreteClassNode)
505+
)
506+
}
507+
508+
private class DependencyInjectionStep extends PreCallGraphStep {
509+
override predicate classInstanceSource(DataFlow::ClassNode cls, DataFlow::Node node) {
510+
exists(DataFlow::ClassNode interfaceClass |
511+
node.asExpr().(Parameter).getType().(ClassType).getClass() = interfaceClass.getAstNode() and
512+
providerClassPair(interfaceClass, cls)
513+
)
514+
}
515+
}
465516
}

javascript/ql/test/library-tests/frameworks/Nest/global/foo.impl.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ import { Foo } from "./foo.interface";
22

33
export class FooImpl extends Foo {
44
fooMethod(x: string) {
5-
sink(x); // $ MISSING: hasValueFlow=x
5+
sink(x); // $ hasValueFlow=x
66
}
77
}

0 commit comments

Comments
 (0)