Skip to content

Commit 7c20c4a

Browse files
authored
Merge pull request github#5396 from asgerf/js/shared-taint-step
Approved by erik-krogh, esbena
2 parents 514c9ef + ccc879d commit 7c20c4a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1306
-993
lines changed

docs/codeql/codeql-language-guides/analyzing-data-flow-in-javascript-and-typescript.rst

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -439,23 +439,24 @@ additional taint step from the first argument of ``resolveSymlinks`` to its resu
439439
}
440440
441441
We might even consider adding this as a default taint step to be used by all taint-tracking configurations. In order to do this, we need
442-
to wrap it in a new subclass of ``TaintTracking::AdditionalTaintStep`` like this:
442+
to wrap it in a new subclass of ``TaintTracking::SharedTaintStep`` like this:
443443
444444
.. code-block:: ql
445445
446-
class StepThroughResolveSymlinks extends TaintTracking::AdditionalTaintStep, DataFlow::CallNode {
447-
StepThroughResolveSymlinks() { this = DataFlow::moduleImport("resolve-symlinks").getACall() }
448-
446+
class StepThroughResolveSymlinks extends TaintTracking::SharedTaintStep {
449447
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
450-
pred = this.getArgument(0) and
451-
succ = this
448+
exists(DataFlow::CallNode c |
449+
c = DataFlow::moduleImport("resolve-symlinks").getACall() and
450+
pred = c.getArgument(0) and
451+
succ = c
452+
)
452453
}
453454
}
454455
455456
If we add this definition to the standard library, it will be picked up by all taint-tracking configurations. Obviously, one has to be
456457
careful when adding such new additional taint steps to ensure that they really make sense for `all` configurations.
457458
458-
Analogous to ``TaintTracking::AdditionalTaintStep``, there is also a class ``DataFlow::AdditionalFlowStep`` that can be extended to add
459+
Analogous to ``TaintTracking::SharedTaintStep``, there is also a class ``DataFlow::SharedFlowStep`` that can be extended to add
459460
extra steps to all data-flow configurations, and hence also to all taint-tracking configurations.
460461
461462
Exercises

javascript/ql/src/Security/CWE-094/ImproperCodeSanitization.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ private DataFlow::Node remoteFlow(DataFlow::TypeTracker t) {
2727
exists(DataFlow::TypeTracker t2, DataFlow::Node prev | prev = remoteFlow(t2) |
2828
t2 = t.smallstep(prev, result)
2929
or
30-
any(TaintTracking::AdditionalTaintStep dts).step(prev, result) and
30+
TaintTracking::sharedTaintStep(prev, result) and
3131
t = t2
3232
)
3333
}

javascript/ql/src/experimental/semmle/javascript/security/dataflow/ResourceExhaustion.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ module ResourceExhaustion {
3737
}
3838

3939
predicate isRestrictedAdditionalTaintStep(DataFlow::Node src, DataFlow::Node dst) {
40-
any(TaintTracking::AdditionalTaintStep dts).step(src, dst) and
40+
TaintTracking::sharedTaintStep(src, dst) and
4141
not dst.asExpr() instanceof AddExpr and
4242
not dst.(DataFlow::MethodCallNode).calls(src, "toString")
4343
}

javascript/ql/src/meta/analysis-quality/TaintSteps.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import CallGraphQuality
1313

1414
predicate relevantStep(DataFlow::Node pred, DataFlow::Node succ) {
1515
(
16-
any(TaintTracking::AdditionalTaintStep dts).step(pred, succ)
16+
TaintTracking::sharedTaintStep(pred, succ)
1717
or
1818
any(DataFlow::AdditionalFlowStep cfg).step(pred, succ)
1919
or

javascript/ql/src/semmle/javascript/Arrays.qll

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,9 @@ module ArrayTaintTracking {
99
/**
1010
* A taint propagating data flow edge caused by the builtin array functions.
1111
*/
12-
private class ArrayFunctionTaintStep extends TaintTracking::AdditionalTaintStep,
13-
DataFlow::CallNode {
14-
ArrayFunctionTaintStep() { arrayFunctionTaintStep(_, _, this) }
15-
16-
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
17-
arrayFunctionTaintStep(pred, succ, this)
12+
private class ArrayFunctionTaintStep extends TaintTracking::SharedTaintStep {
13+
override predicate arrayStep(DataFlow::Node pred, DataFlow::Node succ) {
14+
arrayFunctionTaintStep(pred, succ, _)
1815
}
1916
}
2017

javascript/ql/src/semmle/javascript/Base64.qll

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,12 @@ module Base64 {
6767
* Note that we currently do not model base64 encoding as a taint-propagating data flow edge
6868
* to avoid false positives.
6969
*/
70-
private class Base64DecodingStep extends TaintTracking::AdditionalTaintStep {
71-
Decode dec;
72-
73-
Base64DecodingStep() { this = dec }
74-
70+
private class Base64DecodingStep extends TaintTracking::SharedTaintStep {
7571
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
76-
pred = dec.getInput() and
77-
succ = dec.getOutput()
72+
exists(Decode dec |
73+
pred = dec.getInput() and
74+
succ = dec.getOutput()
75+
)
7876
}
7977
}
8078
}

javascript/ql/src/semmle/javascript/Extend.qll

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -165,14 +165,12 @@ private class FunctionalExtendCallShallow extends ExtendCall {
165165
* A taint propagating data flow edge from the objects flowing into an extend call to its return value
166166
* and to the source of the destination object.
167167
*/
168-
private class ExtendCallTaintStep extends TaintTracking::AdditionalTaintStep {
169-
ExtendCall extend;
170-
171-
ExtendCallTaintStep() { this = extend }
172-
168+
private class ExtendCallTaintStep extends TaintTracking::SharedTaintStep {
173169
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
174-
pred = extend.getASourceOperand() and succ = extend.getDestinationOperand().getALocalSource()
175-
or
176-
pred = extend.getAnOperand() and succ = extend
170+
exists(ExtendCall extend |
171+
pred = extend.getASourceOperand() and succ = extend.getDestinationOperand().getALocalSource()
172+
or
173+
pred = extend.getAnOperand() and succ = extend
174+
)
177175
}
178176
}

javascript/ql/src/semmle/javascript/Promises.qll

Lines changed: 65 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -398,74 +398,65 @@ module PromiseFlow {
398398
}
399399

400400
/**
401-
* Holds if taint propagates from `pred` to `succ` through promises.
401+
* DEPRECATED. Use `TaintTracking::promiseStep` instead.
402402
*/
403-
predicate promiseTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
404-
// from `x` to `new Promise((res, rej) => res(x))`
405-
pred = succ.(PromiseDefinition).getResolveParameter().getACall().getArgument(0)
406-
or
407-
// from `x` to `Promise.resolve(x)`
408-
pred = succ.(PromiseCreationCall).getValue() and
409-
not succ instanceof PromiseAllCreation
410-
or
411-
// from `arr` to `Promise.all(arr)`
412-
pred = succ.(PromiseAllCreation).getArrayNode()
413-
or
414-
exists(DataFlow::MethodCallNode thn | thn.getMethodName() = "then" |
415-
// from `p` to `x` in `p.then(x => ...)`
416-
pred = thn.getReceiver() and
417-
succ = thn.getCallback(0).getParameter(0)
403+
deprecated predicate promiseTaintStep = TaintTracking::promiseStep/2;
404+
405+
private class PromiseTaintStep extends TaintTracking::SharedTaintStep {
406+
override predicate promiseStep(DataFlow::Node pred, DataFlow::Node succ) {
407+
// from `x` to `new Promise((res, rej) => res(x))`
408+
pred = succ.(PromiseDefinition).getResolveParameter().getACall().getArgument(0)
418409
or
419-
// from `v` to `p.then(x => return v)`
420-
pred = thn.getCallback([0 .. 1]).getAReturn() and
421-
succ = thn
422-
)
423-
or
424-
exists(DataFlow::MethodCallNode catch | catch.getMethodName() = "catch" |
425-
// from `p` to `p.catch(..)`
426-
pred = catch.getReceiver() and
427-
succ = catch
410+
// from `x` to `Promise.resolve(x)`
411+
pred = succ.(PromiseCreationCall).getValue() and
412+
not succ instanceof PromiseAllCreation
428413
or
429-
// from `v` to `p.catch(x => return v)`
430-
pred = catch.getCallback(0).getAReturn() and
431-
succ = catch
432-
)
433-
or
434-
// from `p` to `p.finally(..)`
435-
exists(DataFlow::MethodCallNode finally | finally.getMethodName() = "finally" |
436-
pred = finally.getReceiver() and
437-
succ = finally
438-
)
439-
or
440-
// from `x` to `await x`
441-
exists(AwaitExpr await |
442-
pred.getEnclosingExpr() = await.getOperand() and
443-
succ.getEnclosingExpr() = await
444-
)
445-
or
446-
exists(DataFlow::CallNode mapSeries |
447-
mapSeries = DataFlow::moduleMember("bluebird", "mapSeries").getACall()
448-
|
449-
// from `xs` to `x` in `require("bluebird").mapSeries(xs, (x) => {...})`.
450-
pred = mapSeries.getArgument(0) and
451-
succ = mapSeries.getABoundCallbackParameter(1, 0)
414+
// from `arr` to `Promise.all(arr)`
415+
pred = succ.(PromiseAllCreation).getArrayNode()
452416
or
453-
// from `y` to `require("bluebird").mapSeries(x, x => y)`.
454-
pred = mapSeries.getCallback(1).getAReturn() and
455-
succ = mapSeries
456-
)
457-
}
458-
459-
/**
460-
* An additional taint step that involves promises.
461-
*/
462-
private class PromiseTaintStep extends TaintTracking::AdditionalTaintStep {
463-
DataFlow::Node source;
464-
465-
PromiseTaintStep() { promiseTaintStep(source, this) }
466-
467-
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
468-
pred = source and succ = this
417+
exists(DataFlow::MethodCallNode thn | thn.getMethodName() = "then" |
418+
// from `p` to `x` in `p.then(x => ...)`
419+
pred = thn.getReceiver() and
420+
succ = thn.getCallback(0).getParameter(0)
421+
or
422+
// from `v` to `p.then(x => return v)`
423+
pred = thn.getCallback([0 .. 1]).getAReturn() and
424+
succ = thn
425+
)
426+
or
427+
exists(DataFlow::MethodCallNode catch | catch.getMethodName() = "catch" |
428+
// from `p` to `p.catch(..)`
429+
pred = catch.getReceiver() and
430+
succ = catch
431+
or
432+
// from `v` to `p.catch(x => return v)`
433+
pred = catch.getCallback(0).getAReturn() and
434+
succ = catch
435+
)
436+
or
437+
// from `p` to `p.finally(..)`
438+
exists(DataFlow::MethodCallNode finally | finally.getMethodName() = "finally" |
439+
pred = finally.getReceiver() and
440+
succ = finally
441+
)
442+
or
443+
// from `x` to `await x`
444+
exists(AwaitExpr await |
445+
pred.getEnclosingExpr() = await.getOperand() and
446+
succ.getEnclosingExpr() = await
447+
)
448+
or
449+
exists(DataFlow::CallNode mapSeries |
450+
mapSeries = DataFlow::moduleMember("bluebird", "mapSeries").getACall()
451+
|
452+
// from `xs` to `x` in `require("bluebird").mapSeries(xs, (x) => {...})`.
453+
pred = mapSeries.getArgument(0) and
454+
succ = mapSeries.getABoundCallbackParameter(1, 0)
455+
or
456+
// from `y` to `require("bluebird").mapSeries(x, x => y)`.
457+
pred = mapSeries.getCallback(1).getAReturn() and
458+
succ = mapSeries
459+
)
469460
}
470461
}
471462

@@ -500,14 +491,13 @@ private module AsyncReturnSteps {
500491
/**
501492
* A data-flow step for ordinary return from an async function in a taint configuration.
502493
*/
503-
private class AsyncTaintReturn extends TaintTracking::AdditionalTaintStep, DataFlow::FunctionNode {
504-
Function f;
505-
506-
AsyncTaintReturn() { this.getFunction() = f and f.isAsync() }
507-
494+
private class AsyncTaintReturn extends TaintTracking::SharedTaintStep {
508495
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
509-
returnExpr(f, pred, _) and
510-
succ.(DataFlow::FunctionReturnNode).getFunction() = f
496+
exists(Function f |
497+
f.isAsync() and
498+
returnExpr(f, pred, _) and
499+
succ.(DataFlow::FunctionReturnNode).getFunction() = f
500+
)
511501
}
512502
}
513503
}
@@ -616,14 +606,12 @@ private module ClosurePromise {
616606
/**
617607
* Taint steps through closure promise methods.
618608
*/
619-
private class ClosurePromiseTaintStep extends TaintTracking::AdditionalTaintStep {
620-
DataFlow::Node pred;
621-
622-
ClosurePromiseTaintStep() {
609+
private class ClosurePromiseTaintStep extends TaintTracking::SharedTaintStep {
610+
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
623611
// static methods in goog.Promise
624612
exists(DataFlow::CallNode call, string name |
625613
call = Closure::moduleImport("goog.Promise." + name).getACall() and
626-
this = call and
614+
succ = call and
627615
pred = call.getAnArgument()
628616
|
629617
name = "all" or
@@ -635,12 +623,10 @@ private module ClosurePromise {
635623
// promise created through goog.promise.withResolver()
636624
exists(DataFlow::CallNode resolver |
637625
resolver = Closure::moduleImport("goog.Promise.withResolver").getACall() and
638-
this = resolver.getAPropertyRead("promise") and
626+
succ = resolver.getAPropertyRead("promise") and
639627
pred = resolver.getAMethodCall("resolve").getArgument(0)
640628
)
641629
}
642-
643-
override predicate step(DataFlow::Node src, DataFlow::Node dst) { src = pred and dst = this }
644630
}
645631
}
646632

javascript/ql/src/semmle/javascript/Regexp.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -904,7 +904,7 @@ private DataFlow::Node regExpSource(DataFlow::Node re, DataFlow::TypeBackTracker
904904
exists(DataFlow::TypeBackTracker t2, DataFlow::Node succ | succ = regExpSource(re, t2) |
905905
t2 = t.smallstep(result, succ)
906906
or
907-
any(TaintTracking::AdditionalTaintStep dts).step(result, succ) and
907+
TaintTracking::sharedTaintStep(result, succ) and
908908
t = t2
909909
)
910910
}

javascript/ql/src/semmle/javascript/dataflow/Configuration.qll

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ private import javascript
7272
private import internal.FlowSteps
7373
private import internal.AccessPaths
7474
private import internal.CallGraphs
75+
private import internal.Unit
7576
private import semmle.javascript.internal.CachedStages
7677

7778
/**
@@ -609,6 +610,57 @@ abstract class AdditionalFlowStep extends DataFlow::Node {
609610
}
610611
}
611612

613+
/**
614+
* A data flow edge that should be added to all data flow configurations in
615+
* addition to standard data flow edges.
616+
*
617+
* This class is a singleton, and thus subclasses do not need to specify a characteristic predicate.
618+
*
619+
* Note: For performance reasons, all subclasses of this class should be part
620+
* of the standard library. Override `Configuration::isAdditionalFlowStep`
621+
* for analysis-specific flow steps.
622+
*/
623+
class SharedFlowStep extends Unit {
624+
/**
625+
* Holds if `pred` → `succ` should be considered a data flow edge.
626+
*/
627+
predicate step(DataFlow::Node pred, DataFlow::Node succ) { none() }
628+
629+
/**
630+
* Holds if `pred` → `succ` should be considered a data flow edge
631+
* transforming values with label `predlbl` to have label `succlbl`.
632+
*/
633+
predicate step(
634+
DataFlow::Node pred, DataFlow::Node succ, DataFlow::FlowLabel predlbl,
635+
DataFlow::FlowLabel succlbl
636+
) {
637+
none()
638+
}
639+
}
640+
641+
/**
642+
* Contributes subclasses of `SharedFlowStep` to `AdditionalFlowStep`.
643+
*
644+
* This is a placeholder until we migrate to the `SharedFlowStep` class and deprecate `AdditionalFlowStep`.
645+
*/
646+
private class SharedStepAsAdditionalFlowStep extends AdditionalFlowStep {
647+
SharedStepAsAdditionalFlowStep() {
648+
any(SharedFlowStep st).step(_, this) or
649+
any(SharedFlowStep st).step(_, this, _, _)
650+
}
651+
652+
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
653+
any(SharedFlowStep st).step(pred, succ) and succ = this
654+
}
655+
656+
override predicate step(
657+
DataFlow::Node pred, DataFlow::Node succ, DataFlow::FlowLabel predlbl,
658+
DataFlow::FlowLabel succlbl
659+
) {
660+
any(SharedFlowStep st).step(pred, succ, predlbl, succlbl) and succ = this
661+
}
662+
}
663+
612664
/**
613665
* A collection of pseudo-properties that are used in multiple files.
614666
*

0 commit comments

Comments
 (0)