JS: Model dependency injection in Nest

This commit is contained in:
Asger F
2025-01-29 13:25:27 +01:00
parent 89ad737b2a
commit 16f7373712
2 changed files with 52 additions and 1 deletions

View File

@@ -4,6 +4,7 @@
import javascript
private import semmle.javascript.security.dataflow.ServerSideUrlRedirectCustomizations
private import semmle.javascript.dataflow.internal.PreCallGraphStep
/**
* Provides classes and predicates for reasoning about [Nest](https://nestjs.com/).
@@ -462,4 +463,54 @@ module NestJS {
result.(DataFlow::FunctionNode).getAParameter() = this
}
}
/**
* A value passed in the `providers` array in:
* ```js
* @Module({ providers: [ ... ] })
* class App { ... }
* ```
*/
private DataFlow::Node providerTuple() {
result =
DataFlow::moduleImport("@nestjs/common")
.getAPropertyRead("Module")
.getACall()
.getOptionArgument(0, "providers")
.getALocalSource()
.(DataFlow::ArrayCreationNode)
.getAnElement()
}
private predicate providerPair(DataFlow::Node interface, DataFlow::Node concreteClass) {
exists(DataFlow::SourceNode tuple |
tuple = providerTuple().getALocalSource() and
interface = tuple.getAPropertyWrite("provide").getRhs() and
concreteClass = tuple.getAPropertyWrite("useClass").getRhs()
)
}
/** Gets the class being referenced at `node` without relying on the call graph. */
private DataFlow::ClassNode getClassFromNode(DataFlow::Node node) {
result.getAstNode() = node.analyze().getAValue().(AbstractClass).getClass()
}
private predicate providerClassPair(
DataFlow::ClassNode interface, DataFlow::ClassNode concreteClass
) {
exists(DataFlow::Node interfaceNode, DataFlow::Node concreteClassNode |
providerPair(interfaceNode, concreteClassNode) and
interface = getClassFromNode(interfaceNode) and
concreteClass = getClassFromNode(concreteClassNode)
)
}
private class DependencyInjectionStep extends PreCallGraphStep {
override predicate classInstanceSource(DataFlow::ClassNode cls, DataFlow::Node node) {
exists(DataFlow::ClassNode interfaceClass |
node.asExpr().(Parameter).getType().(ClassType).getClass() = interfaceClass.getAstNode() and
providerClassPair(interfaceClass, cls)
)
}
}
}

View File

@@ -2,6 +2,6 @@ import { Foo } from "./foo.interface";
export class FooImpl extends Foo {
fooMethod(x: string) {
sink(x); // $ MISSING: hasValueFlow=x
sink(x); // $ hasValueFlow=x
}
}