Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
4684bc9
C#: Add QL doc to `startsSplits()`
hvitved Aug 23, 2018
1dff900
C#: Speed up `throwMayBeUncaught()` by not relying on `definitelyHand…
hvitved Aug 23, 2018
86ab9ad
JS: support `push` and `sort` taint steps for arrays
Aug 30, 2018
c1e6280
JS: generalize change notes for improved array operation taint steps
Aug 30, 2018
e7234f5
C++: Split index calculation from BB membership
jbj Aug 29, 2018
386b89a
C#: Improvements to `cs/useless-upcast`
hvitved Aug 30, 2018
f6e5be0
cherry-pick 35d31aee onto 1.18 branch
nickrolfe Aug 31, 2018
98612b9
C++: Tidy primitive_basic_block_member calculation
jbj Sep 1, 2018
7e3adec
Merge pull request #135 from esben-semmle/js/pick-get-taint-steps
Sep 3, 2018
20bff70
Merge pull request #136 from esben-semmle/js/composed-function-taint
Sep 3, 2018
58e3845
JavaScript: Improve query name and help for `js/incomplete-sanitizati…
Aug 31, 2018
3e18a9b
CPP: Improve the special case for realloc in MemoryMayNotBeFreed.ql.
geoffw0 Aug 24, 2018
8e5c170
CPP: Change note.
geoffw0 Aug 24, 2018
759d986
Merge pull request #117 from esben-semmle/js/push-sort-taint-steps
Sep 3, 2018
18dc1d0
Merge pull request #129 from Semmle/changenote-desig-init
jbj Sep 3, 2018
b34dbfa
C++: Correct change note formatting
jbj Sep 3, 2018
2fd73f2
C++: getEnclosingElement-without-macros changenote
jbj Sep 3, 2018
5d5febf
Merge pull request #137 from jbj/getEnclosingElement-changenote
nickrolfe Sep 3, 2018
ab6dc1d
C++: Add missing override annotations
jbj Sep 3, 2018
88f80e4
C++: Silence two more QL compiler warnings
jbj Sep 3, 2018
4dec7c5
Merge pull request #127 from xiemaisi/js/incomplete-sanitisation-doc-…
semmle-qlci Sep 3, 2018
af3f855
Merge pull request #94 from hvitved/csharp/cfg/minor-fixes
calumgrant Sep 3, 2018
07bacbf
C++: Follow suppressUnusedThis convention
jbj Sep 4, 2018
c4c74cd
C#: Split up `DataFlowInternal.qll`
hvitved Sep 4, 2018
e0ba2b2
C++: Fix name of suppressUnusedType
jbj Sep 4, 2018
81122ca
C#: Add test that reveals bug in BaseSsa implementation
hvitved Sep 4, 2018
7bd53e7
TypeScript: fix alerts in ambient code
asger-semmle Sep 4, 2018
9a7746e
C#: Fix bug in BaseSsa library
hvitved Sep 4, 2018
3cdaed2
Merge pull request #141 from jbj/ql-warnings-1.18
semmle-qlci Sep 4, 2018
f7827b7
TypeScript: update change note to mention TypeScript 3.0 support
asger-semmle Sep 4, 2018
fdc20e8
Merge pull request #153 from asger-semmle/ts-typescript3.0-changenote
Sep 4, 2018
919203a
Address review comment
hvitved Sep 4, 2018
6a4dbfc
Address review comment
hvitved Sep 4, 2018
4e9c52a
TypeScript: add change note
asger-semmle Sep 4, 2018
aacee8f
C++: Reshuffle IR files into a consistent directory structure
dave-bartolomeo Aug 30, 2018
9fd5f26
C++: Remove unnecessary Impl suffix from some files
dave-bartolomeo Aug 30, 2018
97cfbd9
C++: "IR" means "Aliased SSA IR"
dave-bartolomeo Aug 30, 2018
aa4436f
C++: More IR reshuffling
dave-bartolomeo Aug 31, 2018
fce7a5f
C++: Final IR reshuffle
dave-bartolomeo Aug 31, 2018
4086a89
C++: Fix a couple IR-related tests to handle new directory tree
dave-bartolomeo Sep 3, 2018
4a1d04b
C++: Fix deprecated predicate warnings
dave-bartolomeo Sep 4, 2018
124a00b
C#: Update expected output in SSA tests
hvitved Sep 4, 2018
8225daf
Merge pull request #122 from dave-bartolomeo/dave/IRShuffle
semmle-qlci Sep 4, 2018
1bcae97
Merge pull request #114 from geoffw0/samate-realloc
jbj Sep 5, 2018
6c1098d
Merge pull request #120 from hvitved/csharp/query/useless-upcast
semmle-qlci Sep 5, 2018
50b5a3b
Merge pull request #151 from asger-semmle/ts-ambient-toplevel
semmle-qlci Sep 5, 2018
8263b24
Merge pull request #152 from hvitved/csharp/base-ssa
calumgrant Sep 5, 2018
a70526f
Merge pull request #123 from jbj/primitive-bb-number-split
semmle-qlci Sep 5, 2018
cbdbda3
Merge rc/1.18 into next.
adityasharad Sep 5, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions change-notes/1.18/analysis-javascript.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
- [deepmerge](https://github.com/KyleAMathews/deepmerge)
- [defaults-deep](https://github.com/jonschlinkert/defaults-deep)
- [defaults](https://github.com/tmpvar/defaults)
- [dottie](https://github.com/mickhansen/dottie.js)
- [dotty](https://github.com/deoxxa/dotty)
- [ent](https://github.com/substack/node-ent)
- [entities](https://github.com/fb55/node-entities)
- [escape-goat](https://github.com/sindresorhus/escape-goat)
Expand Down
1 change: 1 addition & 0 deletions javascript/ql/src/javascript.qll
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import semmle.javascript.frameworks.Logging
import semmle.javascript.frameworks.HttpFrameworks
import semmle.javascript.frameworks.NoSQL
import semmle.javascript.frameworks.PkgCloud
import semmle.javascript.frameworks.PropertyProjection
import semmle.javascript.frameworks.React
import semmle.javascript.frameworks.ReactNative
import semmle.javascript.frameworks.Request
Expand Down
155 changes: 155 additions & 0 deletions javascript/ql/src/semmle/javascript/frameworks/PropertyProjection.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/**
* Provides classes for modelling property projection functions.
*
* Subclass `PropertyProjection` to refine the behavior of the analysis on existing property projections.
* Subclass `CustomPropertyProjection` to introduce new kinds of property projections.
*/

import javascript

/**
* A property projection call such as `_.get(o, 'a.b')`, which is equivalent to `o.a.b`.
*/
abstract class CustomPropertyProjection extends DataFlow::CallNode {

/**
* Gets the argument for the object to project properties from, such as `o` in `_.get(o, 'a.b')`.
*/
abstract DataFlow::Node getObject();

/**
* Gets an argument that selects the properties to project, such as `'a.b'` in `_.get(o, 'a.b')`.
*/
abstract DataFlow::Node getASelector();

/**
* Holds if this call returns the value of a single projected property, as opposed to an object that can contain multiple projected properties.
*/
abstract predicate isSingletonProjection();

}

/**
* A property projection call such as `_.get(o, 'a.b')`, which is equivalent to `o.a.b`.
*/
class PropertyProjection extends DataFlow::CallNode {

CustomPropertyProjection custom;

PropertyProjection() { this = custom }

/**
* Gets the argument for the object to project properties from, such as `o` in `_.get(o, 'a.b')`.
*/
DataFlow::Node getObject() { result = custom.getObject() }

/**
* Gets an argument that selects the properties to project, such as `'a.b'` in `_.get(o, 'a.b')`.
*/
DataFlow::Node getASelector() { result = custom.getASelector() }

/**
* Holds if this call returns the value of a single projected property, as opposed to an object that can contain multiple projected properties.
*
* Examples:
* - This predicate holds for `_.get({a: 'b'}, 'a')`, which returns `'b'`,
* - This predicate does not hold for `_.pick({a: 'b', c: 'd'}}, 'a')`, which returns `{a: 'b'}`,
*/
predicate isSingletonProjection() { custom.isSingletonProjection() }

}

/**
* A simple model of common property projection functions.
*/
private class SimplePropertyProjection extends CustomPropertyProjection {

int objectIndex;
int selectorIndex;
boolean singleton;

SimplePropertyProjection() {
exists (DataFlow::SourceNode callee |
this = callee.getACall() |
singleton = false and (
(
callee = LodashUnderscore::member("pick") and
objectIndex = 0 and
selectorIndex = [1..getNumArgument()]
)
or
(
callee = LodashUnderscore::member("pickBy") and
objectIndex = 0 and
selectorIndex = 1
)
or
exists (string name |
name = "pick" or
name = "pickAll" or
name = "pickBy" |
callee = DataFlow::moduleMember("ramda", name) and
objectIndex = 1 and
selectorIndex = 0
)
or
(
callee = DataFlow::moduleMember("dotty", "search") and
objectIndex = 0 and
selectorIndex = 1
)
)
or
singleton = true and (
(
callee = LodashUnderscore::member("get") and
objectIndex = 0 and
selectorIndex = 1
)
or
(
callee = DataFlow::moduleMember("ramda", "path") and
objectIndex = 1 and
selectorIndex = 0
)
or
(
callee = DataFlow::moduleMember("dottie", "get") and
objectIndex = 0 and
selectorIndex = 1
)
or
(
callee = DataFlow::moduleMember("dotty", "get") and
objectIndex = 0 and
selectorIndex = 1
)
)
)
}

override DataFlow::Node getObject() { result = getArgument(objectIndex) }

override DataFlow::Node getASelector() { result = getArgument(selectorIndex) }

override predicate isSingletonProjection() { singleton = true }

}

/**
* A taint step for a property projection.
*/
private class PropertyProjectionTaintStep extends TaintTracking::AdditionalTaintStep {

PropertyProjection projection;

PropertyProjectionTaintStep() {
projection = this
}

override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
// reading from a tainted object yields a tainted result
this = succ and
pred = projection.getObject()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
| tst.js:25:10:25:15 | source |
| tst.js:32:10:32:27 | _.pick(tainted, s) |
| tst.js:33:10:33:26 | _.get(tainted, s) |
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import javascript

class ExampleConfiguration extends TaintTracking::Configuration {

ExampleConfiguration() { this = "ExampleConfiguration" }

override predicate isSource(DataFlow::Node source) {
source.asExpr().(CallExpr).getCalleeName() = "SOURCE"
}

override predicate isSink(DataFlow::Node sink) {
exists (CallExpr callExpr |
callExpr.getCalleeName() = "SINK" and
DataFlow::valueNode(callExpr.getArgument(0)) = sink
)
}

}

from ExampleConfiguration cfg, DataFlow::Node source, DataFlow::Node sink
where cfg.hasFlow(source, sink)
select sink
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
| tst.js:6:1:6:17 | _.pick(o, s1, s2) | tst.js:6:8:6:8 | o | tst.js:6:11:6:12 | s1 | false |
| tst.js:6:1:6:17 | _.pick(o, s1, s2) | tst.js:6:8:6:8 | o | tst.js:6:15:6:16 | s2 | false |
| tst.js:7:1:7:14 | _.pickBy(o, s) | tst.js:7:10:7:10 | o | tst.js:7:13:7:13 | s | false |
| tst.js:9:1:9:12 | R.pick(s, o) | tst.js:9:11:9:11 | o | tst.js:9:8:9:8 | s | false |
| tst.js:10:1:10:14 | R.pickBy(s, o) | tst.js:10:13:10:13 | o | tst.js:10:10:10:10 | s | false |
| tst.js:11:1:11:15 | R.pickAll(s, o) | tst.js:11:14:11:14 | o | tst.js:11:11:11:11 | s | false |
| tst.js:13:1:13:11 | _.get(o, s) | tst.js:13:7:13:7 | o | tst.js:13:10:13:10 | s | true |
| tst.js:15:1:15:12 | R.path(s, o) | tst.js:15:11:15:11 | o | tst.js:15:8:15:8 | s | true |
| tst.js:17:1:17:16 | dottie.get(o, s) | tst.js:17:12:17:12 | o | tst.js:17:15:17:15 | s | true |
| tst.js:19:1:19:15 | dotty.get(o, s) | tst.js:19:11:19:11 | o | tst.js:19:14:19:14 | s | true |
| tst.js:20:1:20:18 | dotty.search(o, s) | tst.js:20:14:20:14 | o | tst.js:20:17:20:17 | s | false |
| tst.js:27:10:27:30 | _.pick( ... ted, s) | tst.js:27:17:27:26 | notTainted | tst.js:27:29:27:29 | s | false |
| tst.js:28:10:28:29 | _.get(notTainted, s) | tst.js:28:16:28:25 | notTainted | tst.js:28:28:28:28 | s | true |
| tst.js:32:10:32:27 | _.pick(tainted, s) | tst.js:32:17:32:23 | tainted | tst.js:32:26:32:26 | s | false |
| tst.js:33:10:33:26 | _.get(tainted, s) | tst.js:33:16:33:22 | tainted | tst.js:33:25:33:25 | s | true |
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import javascript

from PropertyProjection p, boolean singleton
where if p.isSingletonProjection() then singleton = true else singleton = false
select p, p.getObject(), p.getASelector(), singleton
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
var _ = require("lodash"),
dotty = require("dotty"),
dottie = require("dottie"),
R = require("ramda");

_.pick(o, s1, s2);
_.pickBy(o, s);

R.pick(s, o);
R.pickBy(s, o);
R.pickAll(s, o);

_.get(o, s);

R.path(s, o);

dottie.get(o, s);

dotty.get(o, s);
dotty.search(o, s);

(function(){
var source = SOURCE();

SINK(source);

SINK(_.pick(notTainted, s));
SINK(_.get(notTainted, s));

var tainted = {};
tainted[x] = source;
SINK(_.pick(tainted, s));
SINK(_.get(tainted, s));
});