From 5e0ce62ea9ae8b72c4809ae83925ec5f14da364a Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Fri, 16 Apr 2021 12:41:14 +0200 Subject: [PATCH] use the class hierarchy from TypeScript in the callgraph --- .../dataflow/internal/CallGraphs.qll | 29 +++++++++++++++++++ .../TaintTracking/BasicTaintTracking.expected | 1 + .../TaintTracking/DataFlowTracking.expected | 1 + .../library-tests/TaintTracking/tsconfig.json | 0 .../test/library-tests/TaintTracking/typed.ts | 25 ++++++++++++++++ .../TypeScript/CallGraph/CallGraph.expected | 1 + 6 files changed, 57 insertions(+) create mode 100644 javascript/ql/test/library-tests/TaintTracking/tsconfig.json create mode 100644 javascript/ql/test/library-tests/TaintTracking/typed.ts diff --git a/javascript/ql/lib/semmle/javascript/dataflow/internal/CallGraphs.qll b/javascript/ql/lib/semmle/javascript/dataflow/internal/CallGraphs.qll index d5a5b29d1dcd5..dabd8b19a19c7 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/internal/CallGraphs.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/internal/CallGraphs.qll @@ -55,6 +55,35 @@ module CallGraph { or imprecision = 0 and result = callgraphStep(function, t) + or + t.start() and + imprecision = 0 and + function = getTypedCallee(result) + } + + /** + * Gets a class that implements (or is) the given `type`. + */ + private ClassDefinition getAnImplementationClass(Type type) { + exists(InterfaceType inter | inter = type | + result.getSuperClassDefinition*().getASuperInterface().getType() = inter + ) + or + exists(ClassType classType | classType = type | + result.getSuperClassDefinition*() = classType.getClass() + ) + } + + /** + * Gets a function that the given `callee` refers to through the TypeScript class hierarchy. + */ + private DataFlow::FunctionNode getTypedCallee(DataFlow::PropRead callee) { + exists(Type baseType, ClassDefinition impl, string name | + callee.getBase().asExpr().getType() = baseType and + impl = getAnImplementationClass(baseType) and + callee.getPropertyName() = name and + impl.getInstanceMethod(name) = result.getFunction() + ) } /** diff --git a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected index 74095771abb5a..9f2262bd48677 100644 --- a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected +++ b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected @@ -232,6 +232,7 @@ typeInferenceMismatch | tst.js:2:13:2:20 | source() | tst.js:48:10:48:22 | new Buffer(x) | | tst.js:2:13:2:20 | source() | tst.js:51:10:51:31 | seriali ... ript(x) | | tst.js:2:13:2:20 | source() | tst.js:54:14:54:19 | unsafe | +| typed.ts:23:18:23:25 | source() | typed.ts:11:14:11:14 | s | | xml.js:5:18:5:25 | source() | xml.js:8:14:8:17 | text | | xml.js:12:17:12:24 | source() | xml.js:13:14:13:19 | result | | xml.js:23:18:23:25 | source() | xml.js:20:14:20:17 | attr | diff --git a/javascript/ql/test/library-tests/TaintTracking/DataFlowTracking.expected b/javascript/ql/test/library-tests/TaintTracking/DataFlowTracking.expected index 77a5cc33a150e..de40e8815f803 100644 --- a/javascript/ql/test/library-tests/TaintTracking/DataFlowTracking.expected +++ b/javascript/ql/test/library-tests/TaintTracking/DataFlowTracking.expected @@ -111,3 +111,4 @@ | thisAssignments.js:7:19:7:26 | source() | thisAssignments.js:8:10:8:20 | this.field2 | | tst.js:2:13:2:20 | source() | tst.js:4:10:4:10 | x | | tst.js:2:13:2:20 | source() | tst.js:54:14:54:19 | unsafe | +| typed.ts:23:18:23:25 | source() | typed.ts:11:14:11:14 | s | diff --git a/javascript/ql/test/library-tests/TaintTracking/tsconfig.json b/javascript/ql/test/library-tests/TaintTracking/tsconfig.json new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/javascript/ql/test/library-tests/TaintTracking/typed.ts b/javascript/ql/test/library-tests/TaintTracking/typed.ts new file mode 100644 index 0000000000000..4c89a1bfbdb4e --- /dev/null +++ b/javascript/ql/test/library-tests/TaintTracking/typed.ts @@ -0,0 +1,25 @@ +declare function source(): any; +declare function sink(taint: any): any; + + +interface Thing { + do(s: string): void; +} + +class ThingImpl implements Thing { + do(s: string): void { + sink(s); + } +} + +class ThingDoer { + thing: Thing; + doThing(s: string): void { + this.thing.do(s); + } +} + +export function run(doer: ThingDoer): void { + doer.doThing(source()); +} + \ No newline at end of file diff --git a/javascript/ql/test/library-tests/TypeScript/CallGraph/CallGraph.expected b/javascript/ql/test/library-tests/TypeScript/CallGraph/CallGraph.expected index 4cfb86ac7c681..f1acb5a5ff7b4 100644 --- a/javascript/ql/test/library-tests/TypeScript/CallGraph/CallGraph.expected +++ b/javascript/ql/test/library-tests/TypeScript/CallGraph/CallGraph.expected @@ -1,4 +1,5 @@ | tst.ts:4:5:4:21 | x.method("Hello") | tst.ts:15:3:17:3 | public ... x);\\n } | +| tst.ts:4:5:4:21 | x.method("Hello") | tst.ts:21:3:23:3 | public ... ");\\n } | | tst.ts:9:17:9:33 | new AngryLogger() | tst.ts:20:34:20:33 | (...arg ... rgs); } | | tst.ts:10:5:10:49 | (newLog ... hello") | tst.ts:15:3:17:3 | public ... x);\\n } | | tst.ts:10:5:10:49 | (newLog ... hello") | tst.ts:21:3:23:3 | public ... ");\\n } |