mirror of
https://github.com/github/codeql.git
synced 2026-02-12 05:01:06 +01:00
445 lines
13 KiB
Plaintext
445 lines
13 KiB
Plaintext
/**
|
|
* Provides classes for modeling URI libraries.
|
|
*/
|
|
|
|
import javascript
|
|
|
|
/**
|
|
* Provides classes for working with [urijs](http://medialize.github.io/URI.js/) code.
|
|
*/
|
|
module Urijs {
|
|
/**
|
|
* Gets a data flow source node for the urijs library.
|
|
*/
|
|
DataFlow::SourceNode urijs() {
|
|
result = DataFlow::globalVarRef("URI") or
|
|
result = DataFlow::moduleImport("urijs") or
|
|
result = DataFlow::moduleImport("URIjs")
|
|
}
|
|
|
|
/**
|
|
* Gets a data flow source node for an invocation of the urijs function.
|
|
*/
|
|
private DataFlow::InvokeNode invocation() { result = urijs().getAnInvocation() }
|
|
|
|
/**
|
|
* Gets a data flow source node for a urijs instance.
|
|
*/
|
|
private DataFlow::InvokeNode instance() {
|
|
result = invocation() or
|
|
result = chainCall()
|
|
}
|
|
|
|
/**
|
|
* Gets a data flow source node for a chainable method call on a urijs instance.
|
|
*/
|
|
private DataFlow::MethodCallNode chainCall() {
|
|
result = instance().getAMethodCall(_) and
|
|
// the API has the convention that calls with arguments are chainable
|
|
not result.getNumArgument() = 0
|
|
}
|
|
|
|
/**
|
|
* Gets a data flow source node for a method call on an `instance` that returns a string value.
|
|
*/
|
|
private DataFlow::MethodCallNode getter() {
|
|
result = instance().getAMethodCall(_) and
|
|
// the API has the convention that calls with zero arguments are string-valued getters
|
|
result.getNumArgument() = 0
|
|
}
|
|
|
|
/**
|
|
* A taint step in the urijs library.
|
|
*/
|
|
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 = succ and invk = invocation() |
|
|
pred = invk.getAnArgument()
|
|
)
|
|
or
|
|
// flow through chained calls
|
|
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 = succ and succ = getter() | pred = mc.getReceiver())
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Provides classes for working with [uri-js](https://github.com/garycourt/uri-js) code.
|
|
*/
|
|
module Uridashjs {
|
|
/**
|
|
* Gets a data flow source node for member `name` of the uridashjs library.
|
|
*/
|
|
DataFlow::SourceNode uridashjsMember(string name) {
|
|
result = DataFlow::moduleMember("uri-js", name)
|
|
}
|
|
|
|
/**
|
|
* A taint step in the urijs library.
|
|
*/
|
|
private class Step extends TaintTracking::SharedTaintStep {
|
|
override predicate uriStep(DataFlow::Node pred, DataFlow::Node succ) {
|
|
exists(DataFlow::CallNode call |
|
|
call = uridashjsMember(["parse", "serialize", "resolve", "normalize"]).getACall() and
|
|
pred = call.getAnArgument() and
|
|
succ = call
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Provides classes for working with [punycode](https://github.com/bestiejs/punycode.js) code.
|
|
*/
|
|
module Punycode {
|
|
/**
|
|
* Gets a data flow source node for member `name` of the punycode library.
|
|
*/
|
|
DataFlow::SourceNode punycodeMember(string name) {
|
|
result = DataFlow::moduleMember("punycode", name)
|
|
}
|
|
|
|
/**
|
|
* A taint step in the punycode library.
|
|
*/
|
|
private class Step extends TaintTracking::SharedTaintStep {
|
|
override predicate uriStep(DataFlow::Node pred, DataFlow::Node succ) {
|
|
exists(DataFlow::CallNode call |
|
|
call = punycodeMember(["decode", "encode", "toUnicode", "toASCII"]).getACall() and
|
|
pred = call.getAnArgument() and
|
|
succ = call
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Provides classes for working with [url-parse](https://github.com/unshiftio/url-parse) code.
|
|
*/
|
|
module UrlParse {
|
|
/**
|
|
* Gets a data flow source node for the url-parse library.
|
|
*/
|
|
DataFlow::SourceNode urlParse() { result = DataFlow::moduleImport("url-parse") }
|
|
|
|
/**
|
|
* Gets a data flow source node for a call of the url-parse function.
|
|
*/
|
|
private DataFlow::InvokeNode call() { result = urlParse().getACall() }
|
|
|
|
/**
|
|
* A taint step in the url-parse library.
|
|
*/
|
|
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
|
|
call = call().getAMethodCall("set") and
|
|
(
|
|
// pred = parse(...); pred.set(x, y)
|
|
pred = call.getReceiver()
|
|
or
|
|
// parse(x).set(y, pred)
|
|
pred = call.getArgument(1)
|
|
)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Provides classes for working with [querystringify](https://github.com/unshiftio/querystringify) code.
|
|
*/
|
|
module Querystringify {
|
|
/**
|
|
* Gets a data flow source node for member `name` of the querystringify library.
|
|
*/
|
|
DataFlow::SourceNode querystringifyMember(string name) {
|
|
result = querystringify().getMember(name).asSource()
|
|
}
|
|
|
|
/** Gets an API node referring to the `querystringify` module. */
|
|
private API::Node querystringify() {
|
|
result = [API::moduleImport("querystringify"), API::moduleImport("url-parse").getMember("qs")]
|
|
}
|
|
|
|
/**
|
|
* A taint step in the querystringify library.
|
|
*/
|
|
private class Step extends TaintTracking::SharedTaintStep {
|
|
override predicate uriStep(DataFlow::Node pred, DataFlow::Node succ) {
|
|
exists(DataFlow::CallNode call |
|
|
call = querystringify().getMember(["parse", "stringify"]).getACall() and
|
|
pred = call.getAnArgument() and
|
|
succ = call
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Provides classes for working with [query-string](https://github.com/sindresorhus/query-string) code.
|
|
*/
|
|
module Querydashstring {
|
|
/**
|
|
* Gets a data flow source node for member `name` of the query-string library.
|
|
*/
|
|
DataFlow::SourceNode querydashstringMember(string name) {
|
|
result = DataFlow::moduleMember("query-string", name)
|
|
}
|
|
|
|
/**
|
|
* A taint step in the query-string library.
|
|
*/
|
|
private class Step extends TaintTracking::SharedTaintStep {
|
|
override predicate uriStep(DataFlow::Node pred, DataFlow::Node succ) {
|
|
exists(DataFlow::CallNode call |
|
|
call = querydashstringMember(["parse", "extract", "parseUrl", "stringify"]).getACall() and
|
|
pred = call.getAnArgument() and
|
|
succ = call
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Provides classes for working with [url](https://nodejs.org/api/url.html) code.
|
|
*/
|
|
module Url {
|
|
/**
|
|
* Gets a data flow source node for member `name` of the url library.
|
|
*/
|
|
DataFlow::SourceNode urlMember(string name) { result = DataFlow::moduleMember("url", name) }
|
|
|
|
/**
|
|
* A taint step in the url library.
|
|
*/
|
|
private class Step extends TaintTracking::SharedTaintStep {
|
|
override predicate uriStep(DataFlow::Node pred, DataFlow::Node succ) {
|
|
exists(DataFlow::CallNode call |
|
|
call = urlMember(["parse", "format", "resolve"]).getACall() and
|
|
pred = call.getAnArgument() and
|
|
succ = call
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Provides classes for working with [querystring](https://nodejs.org/api/querystring.html) code.
|
|
*/
|
|
module Querystring {
|
|
/**
|
|
* Gets a data flow source node for member `name` of the querystring library.
|
|
*/
|
|
DataFlow::SourceNode querystringMember(string name) {
|
|
result = DataFlow::moduleMember("querystring", name)
|
|
}
|
|
|
|
/**
|
|
* A taint step in the querystring library.
|
|
*/
|
|
private class Step extends TaintTracking::SharedTaintStep {
|
|
override predicate uriStep(DataFlow::Node pred, DataFlow::Node succ) {
|
|
exists(DataFlow::CallNode call |
|
|
call = querystringMember(["escape", "unescape", "parse", "stringify"]).getACall() and
|
|
pred = call.getAnArgument() and
|
|
succ = call
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A taint step through a call to [qs](https://npmjs.com/package/qs)
|
|
*/
|
|
private class QsStep extends TaintTracking::SharedTaintStep {
|
|
override predicate uriStep(DataFlow::Node pred, DataFlow::Node succ) {
|
|
exists(API::CallNode call |
|
|
call = API::moduleImport("qs").getMember(["parse", "stringify"]).getACall()
|
|
|
|
|
pred = call.getArgument(0) and
|
|
succ = call
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A taint step through a call to [normalize-url](https://npmjs.com/package/normalize-url)
|
|
*/
|
|
private class NormalizeUrlStep extends TaintTracking::SharedTaintStep {
|
|
override predicate uriStep(DataFlow::Node pred, DataFlow::Node succ) {
|
|
exists(API::CallNode call | call = API::moduleImport("normalize-url").getACall() |
|
|
pred = call.getArgument(0) and
|
|
succ = call
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A taint step through a call to [parseqs](https://npmjs.com/package/parseqs).
|
|
*/
|
|
private class ParseQsStep extends TaintTracking::SharedTaintStep {
|
|
override predicate uriStep(DataFlow::Node pred, DataFlow::Node succ) {
|
|
exists(API::CallNode call |
|
|
call = API::moduleImport("parseqs").getMember(["encode", "decode"]).getACall() and
|
|
pred = call.getArgument(0) and
|
|
succ = call
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Provides steps for the `goog.Uri` class in the closure library.
|
|
*/
|
|
private module ClosureLibraryUri {
|
|
/**
|
|
* Taint step from an argument of a `goog.Uri` call to the return value.
|
|
*/
|
|
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
|
|
// 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
|
|
// static methods in goog.uri.utils
|
|
arg = 0 and
|
|
exists(string name | invoke = Closure::moduleImport("goog.uri.utils." + name).getACall() |
|
|
name =
|
|
[
|
|
"appendParam", // preserve taint from the original URI, but not from the appended param
|
|
"appendParams", //
|
|
"appendParamsFromMap", //
|
|
"appendPath", //
|
|
"getParamValue", //
|
|
"getParamValues", //
|
|
"getPath", //
|
|
"getPathAndAfter", //
|
|
"getQueryData", //
|
|
"parseQueryData", //
|
|
"removeFragment", //
|
|
"removeParam", //
|
|
"setParam", //
|
|
"setParamsFromMap", //
|
|
"setPath", //
|
|
"split", //
|
|
]
|
|
)
|
|
or
|
|
// static methods in goog.string
|
|
arg = 0 and
|
|
exists(string name | invoke = Closure::moduleImport("goog.string." + name).getACall() |
|
|
name = ["urlDecode", "urlEncode"]
|
|
)
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Taint steps through chainable setter calls.
|
|
*
|
|
* Setters mutate the URI object and return the same instance.
|
|
*/
|
|
private class SetterCall extends DataFlow::MethodCallNode {
|
|
DataFlow::NewNode uri;
|
|
string name;
|
|
|
|
SetterCall() {
|
|
exists(DataFlow::SourceNode base |
|
|
base = Closure::moduleImport("goog.Uri").getAnInstantiation() and
|
|
uri = base
|
|
or
|
|
base.(SetterCall).getUri() = uri
|
|
|
|
|
this = base.getAMethodCall(name) and
|
|
name.matches("set%")
|
|
)
|
|
}
|
|
|
|
DataFlow::NewNode getUri() { result = 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()
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Provides classes for working with [path](https://nodejs.org/api/path.html) code.
|
|
*/
|
|
module Path {
|
|
/**
|
|
* A taint step in the path module.
|
|
*/
|
|
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")
|
|
|
|
|
call = ref.getACall() and
|
|
pred = call.getAnArgument() and
|
|
succ = call
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
overlay[local?]
|
|
private class QueryStringStringification extends DataFlow::SummarizedCallable {
|
|
QueryStringStringification() { this = "query-string stringification" }
|
|
|
|
overlay[global]
|
|
override DataFlow::InvokeNode getACall() {
|
|
result =
|
|
API::moduleImport(["querystring", "query-string", "querystringify", "qs"])
|
|
.getMember("stringify")
|
|
.getACall() or
|
|
result = API::moduleImport("url-parse").getMember("qs").getMember("stringify").getACall() or
|
|
result = API::moduleImport("parseqs").getMember("encode").getACall()
|
|
}
|
|
|
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
|
preservesValue = false and
|
|
input = ["Argument[0]", "Argument[0].AnyMemberDeep"] and
|
|
output = "ReturnValue"
|
|
}
|
|
}
|