JS: UriLibraryStep

This commit is contained in:
Asger Feldthaus
2020-03-28 10:40:03 +00:00
parent dbb8aaeb0b
commit 4746670ac6
4 changed files with 143 additions and 151 deletions

View File

@@ -217,13 +217,23 @@ module TaintTracking {
* Note: For performance reasons, all subclasses of this class should be part
* of the standard library. Override `Configuration::isAdditionalTaintStep`
* for analysis-specific taint steps.
*
* This class has multiple kinds of `step` predicates; these all have the same
* effect on taint-tracking configurations. However, the categorization of steps
* allows some data-flow configurations to opt in to specific kinds of taint steps.
*/
class SharedTaintStep extends Unit {
/**
* Holds if `pred` → `succ` should be considered a taint-propagating
* data flow edge.
*/
abstract predicate step(DataFlow::Node pred, DataFlow::Node succ);
predicate step(DataFlow::Node pred, DataFlow::Node succ) { none() }
/**
* Holds if `pred` → `succ` should be considered a taint-propagating
* data flow edge, in the URI category.
*/
predicate uriStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
}
/**
@@ -233,6 +243,12 @@ module TaintTracking {
any(SharedTaintStep step).step(pred, succ)
or
any(AdditionalTaintStep step).step(pred, succ)
or
uriStep(pred, succ)
}
predicate uriStep(DataFlow::Node pred, DataFlow::Node succ) {
any(SharedTaintStep step).uriStep(pred, succ)
}
/**

View File

@@ -5,9 +5,11 @@
import javascript
/**
* DEPRECATED. Use `TaintTracking::SharedTaintStep` or `TaintTracking::uriStep` instead.
*
* A taint propagating data flow edge arising from an operation in a URI library.
*/
abstract class UriLibraryStep extends DataFlow::ValueNode, TaintTracking::AdditionalTaintStep { }
deprecated abstract class UriLibraryStep extends DataFlow::ValueNode, TaintTracking::AdditionalTaintStep { }
/**
* Provides classes for working with [urijs](http://medialize.github.io/URI.js/) code.
@@ -56,26 +58,22 @@ module urijs {
/**
* A taint step in the urijs library.
*/
private class Step extends UriLibraryStep {
DataFlow::Node src;
Step() {
private class Step extends TaintTracking::SharedTaintStep {
override predicate uriStep(DataFlow::Node pred, DataFlow::Node succ) {
// flow through "constructors" (`new` is optional)
exists(DataFlow::InvokeNode invk | invk = this and invk = invocation() |
src = invk.getAnArgument()
exists(DataFlow::InvokeNode invk | invk = succ and invk = invocation() |
pred = invk.getAnArgument()
)
or
// flow through chained calls
exists(DataFlow::MethodCallNode mc | mc = this and this = chainCall() |
src = mc.getReceiver() or
src = mc.getAnArgument()
exists(DataFlow::MethodCallNode mc | mc = succ and succ = chainCall() |
pred = mc.getReceiver() or
pred = mc.getAnArgument()
)
or
// flow through getter calls
exists(DataFlow::MethodCallNode mc | mc = this and this = getter() | src = mc.getReceiver())
exists(DataFlow::MethodCallNode mc | mc = succ and succ = getter() | pred = mc.getReceiver())
}
override predicate step(DataFlow::Node pred, DataFlow::Node succ) { pred = src and succ = this }
}
}
@@ -93,22 +91,19 @@ module uridashjs {
/**
* A taint step in the urijs library.
*/
private class Step extends UriLibraryStep, DataFlow::CallNode {
DataFlow::Node src;
Step() {
exists(string name |
private class Step extends TaintTracking::SharedTaintStep {
override predicate uriStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(string name, DataFlow::CallNode call |
name = "parse" or
name = "serialize" or
name = "resolve" or
name = "normalize"
|
this = uridashjsMember(name).getACall() and
src = getAnArgument()
call = uridashjsMember(name).getACall() and
pred = call.getAnArgument() and
succ = call
)
}
override predicate step(DataFlow::Node pred, DataFlow::Node succ) { pred = src and succ = this }
}
}
@@ -126,22 +121,19 @@ module punycode {
/**
* A taint step in the punycode library.
*/
private class Step extends UriLibraryStep, DataFlow::CallNode {
DataFlow::Node src;
Step() {
exists(string name |
private class Step extends TaintTracking::SharedTaintStep {
override predicate uriStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(string name, DataFlow::CallNode call |
name = "decode" or
name = "encode" or
name = "toUnicode" or
name = "toASCII"
|
this = punycodeMember(name).getACall() and
src = getAnArgument()
call = punycodeMember(name).getACall() and
pred = call.getAnArgument() and
succ = call
)
}
override predicate step(DataFlow::Node pred, DataFlow::Node succ) { pred = src and succ = this }
}
}
@@ -162,24 +154,23 @@ module urlParse {
/**
* A taint step in the url-parse library.
*/
private class Step extends UriLibraryStep, DataFlow::CallNode {
DataFlow::Node src;
Step() {
// parse(src)
this = call() and
src = getAnArgument()
or
exists(DataFlow::MethodCallNode mc | this = mc and mc = call().getAMethodCall("set") |
// src = parse(...); src.set(x, y)
src = mc.getReceiver()
private class Step extends TaintTracking::SharedTaintStep {
override predicate uriStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::CallNode call | succ = call |
// parse(pred)
call = call() and
pred = call.getAnArgument()
or
// parse(x).set(y, src)
src = mc.getArgument(1)
call = call().getAMethodCall("set") and
(
// pred = parse(...); pred.set(x, y)
pred = call.getReceiver()
or
// parse(x).set(y, pred)
pred = call.getArgument(1)
)
)
}
override predicate step(DataFlow::Node pred, DataFlow::Node succ) { pred = src and succ = this }
}
}
@@ -197,20 +188,17 @@ module querystringify {
/**
* A taint step in the querystringify library.
*/
private class Step extends UriLibraryStep, DataFlow::CallNode {
DataFlow::Node src;
Step() {
exists(string name |
private class Step extends TaintTracking::SharedTaintStep {
override predicate uriStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(string name, DataFlow::CallNode call |
name = "parse" or
name = "stringify"
|
this = querystringifyMember(name).getACall() and
src = getAnArgument()
call = querystringifyMember(name).getACall() and
pred = call.getAnArgument() and
succ = call
)
}
override predicate step(DataFlow::Node pred, DataFlow::Node succ) { pred = src and succ = this }
}
}
@@ -228,22 +216,19 @@ module querydashstring {
/**
* A taint step in the query-string library.
*/
private class Step extends UriLibraryStep, DataFlow::CallNode {
DataFlow::Node src;
Step() {
exists(string name |
private class Step extends TaintTracking::SharedTaintStep {
override predicate uriStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(string name, DataFlow::CallNode call |
name = "parse" or
name = "extract" or
name = "parseUrl" or
name = "stringify"
|
this = querydashstringMember(name).getACall() and
src = getAnArgument()
call = querydashstringMember(name).getACall() and
pred = call.getAnArgument() and
succ = call
)
}
override predicate step(DataFlow::Node pred, DataFlow::Node succ) { pred = src and succ = this }
}
}
@@ -259,21 +244,18 @@ module url {
/**
* A taint step in the url library.
*/
private class Step extends UriLibraryStep, DataFlow::CallNode {
DataFlow::Node src;
Step() {
exists(string name |
private class Step extends TaintTracking::SharedTaintStep {
override predicate uriStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(string name, DataFlow::CallNode call |
name = "parse" or
name = "format" or
name = "resolve"
|
this = urlMember(name).getACall() and
src = getAnArgument()
call = urlMember(name).getACall() and
pred = call.getAnArgument() and
succ = call
)
}
override predicate step(DataFlow::Node pred, DataFlow::Node succ) { pred = src and succ = this }
}
}
@@ -291,22 +273,19 @@ module querystring {
/**
* A taint step in the querystring library.
*/
private class Step extends UriLibraryStep, DataFlow::CallNode {
DataFlow::Node src;
Step() {
exists(string name |
private class Step extends TaintTracking::SharedTaintStep {
override predicate uriStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(string name, DataFlow::CallNode call |
name = "escape" or
name = "unescape" or
name = "parse" or
name = "stringify"
|
this = querystringMember(name).getACall() and
src = getAnArgument()
call = querystringMember(name).getACall() and
pred = call.getAnArgument() and
succ = call
)
}
override predicate step(DataFlow::Node pred, DataFlow::Node succ) { pred = src and succ = this }
}
}
@@ -317,56 +296,51 @@ private module ClosureLibraryUri {
/**
* Taint step from an argument of a `goog.Uri` call to the return value.
*/
private class ArgumentStep extends UriLibraryStep, DataFlow::InvokeNode {
int arg;
ArgumentStep() {
// goog.Uri constructor
this = Closure::moduleImport("goog.Uri").getAnInstantiation() and arg = 0
or
// static methods on goog.Uri
exists(string name | this = Closure::moduleImport("goog.Uri." + name).getACall() |
name = "parse" and arg = 0
private class ArgumentStep extends TaintTracking::SharedTaintStep {
override predicate uriStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::InvokeNode invoke, int arg | pred = invoke.getArgument(arg) and succ = invoke |
// goog.Uri constructor
invoke = Closure::moduleImport("goog.Uri").getAnInstantiation() and arg = 0
or
name = "create" and
(arg = 0 or arg = 2 or arg = 4)
// static methods on goog.Uri
exists(string name | invoke = Closure::moduleImport("goog.Uri." + name).getACall() |
name = "parse" and arg = 0
or
name = "create" and
(arg = 0 or arg = 2 or arg = 4)
or
name = "resolve" and
(arg = 0 or arg = 1)
)
or
name = "resolve" and
(arg = 0 or arg = 1)
// static methods in goog.uri.utils
arg = 0 and
exists(string name | invoke = Closure::moduleImport("goog.uri.utils." + name).getACall() |
name = "appendParam" or // preserve taint from the original URI, but not from the appended param
name = "appendParams" or
name = "appendParamsFromMap" or
name = "appendPath" or
name = "getParamValue" or
name = "getParamValues" or
name = "getPath" or
name = "getPathAndAfter" or
name = "getQueryData" or
name = "parseQueryData" or
name = "removeFragment" or
name = "removeParam" or
name = "setParam" or
name = "setParamsFromMap" or
name = "setPath" or
name = "split"
)
or
// static methods in goog.string
arg = 0 and
exists(string name | invoke = Closure::moduleImport("goog.string." + name).getACall() |
name = "urlDecode" or
name = "urlEncode"
)
)
or
// static methods in goog.uri.utils
arg = 0 and
exists(string name | this = Closure::moduleImport("goog.uri.utils." + name).getACall() |
name = "appendParam" or // preserve taint from the original URI, but not from the appended param
name = "appendParams" or
name = "appendParamsFromMap" or
name = "appendPath" or
name = "getParamValue" or
name = "getParamValues" or
name = "getPath" or
name = "getPathAndAfter" or
name = "getQueryData" or
name = "parseQueryData" or
name = "removeFragment" or
name = "removeParam" or
name = "setParam" or
name = "setParamsFromMap" or
name = "setPath" or
name = "split"
)
or
// static methods in goog.string
arg = 0 and
exists(string name | this = Closure::moduleImport("goog.string." + name).getACall() |
name = "urlDecode" or
name = "urlEncode"
)
}
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
pred = getArgument(arg) and
succ = this
}
}
@@ -375,7 +349,7 @@ private module ClosureLibraryUri {
*
* Setters mutate the URI object and return the same instance.
*/
private class SetterCall extends DataFlow::MethodCallNode, UriLibraryStep {
private class SetterCall extends DataFlow::MethodCallNode {
DataFlow::NewNode uri;
string name;
@@ -393,12 +367,19 @@ private module ClosureLibraryUri {
DataFlow::NewNode getUri() { result = uri }
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
pred = getReceiver() and succ = this
or
(name = "setDomain" or name = "setPath" or name = "setScheme") and
pred = getArgument(0) and
succ = uri
string getName() { result = name }
}
/** A taint step derived from a setter call on a `goog.Uri` object. */
private class SetterCallStep extends TaintTracking::SharedTaintStep {
override predicate uriStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(SetterCall call |
pred = call.getReceiver() and succ = call
or
(call.getName() = "setDomain" or call.getName() = "setPath" or call.getName() = "setScheme") and
pred = call.getArgument(0) and
succ = call.getUri()
)
}
}
@@ -409,25 +390,20 @@ private module ClosureLibraryUri {
/**
* A taint step in the path module.
*/
private class Step extends UriLibraryStep, DataFlow::CallNode {
DataFlow::Node src;
Step() {
exists(DataFlow::SourceNode ref |
private class Step extends TaintTracking::SharedTaintStep {
override predicate uriStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::SourceNode ref, DataFlow::CallNode call |
ref = NodeJSLib::Path::moduleMember("parse") or
// a ponyfill: https://www.npmjs.com/package/path-parse
ref = DataFlow::moduleImport("path-parse") or
ref = DataFlow::moduleMember("path-parse", "posix") or
ref = DataFlow::moduleMember("path-parse", "win32")
|
this = ref.getACall() and
src = getAnArgument()
call = ref.getACall() and
pred = call.getAnArgument() and
succ = call
)
}
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
pred = src and succ = this
}
}
}
}

View File

@@ -649,7 +649,7 @@ module TaintedPath {
srclabel instanceof Label::PosixPath and
dstlabel instanceof Label::PosixPath and
(
any(UriLibraryStep step).step(src, dst)
TaintTracking::uriStep(src, dst)
or
exists(DataFlow::CallNode decode |
decode.getCalleeName() = "decodeURIComponent" or decode.getCalleeName() = "decodeURI"