Merge branch 'main' into logs

This commit is contained in:
Erik Krogh Kristensen
2021-07-16 11:21:25 +02:00
311 changed files with 9413 additions and 1874 deletions

View File

@@ -617,11 +617,11 @@ module API {
cached
predicate use(TApiNode nd, DataFlow::Node ref) {
exists(string m, Module mod | nd = MkModuleDef(m) and mod = importableModule(m) |
ref.(ModuleVarNode).getModule() = mod
ref = DataFlow::moduleVarNode(mod)
)
or
exists(string m, Module mod | nd = MkModuleExport(m) and mod = importableModule(m) |
ref.(ExportsVarNode).getModule() = mod
ref = DataFlow::exportsVarNode(mod)
or
exists(DataFlow::Node base | use(MkModuleDef(m), base) |
ref = trackUseNode(base).getAPropertyRead("exports")
@@ -742,12 +742,9 @@ module API {
or
// additional backwards step from `require('m')` to `exports` or `module.exports` in m
exists(Import imp | imp.getImportedModuleNode() = trackDefNode(nd, t.continue()) |
result.(ExportsVarNode).getModule() = imp.getImportedModule()
result = DataFlow::exportsVarNode(imp.getImportedModule())
or
exists(ModuleVarNode mod |
mod.getModule() = imp.getImportedModule() and
result = mod.(DataFlow::SourceNode).getAPropertyRead("exports")
)
result = DataFlow::moduleVarNode(imp.getImportedModule()).getAPropertyRead("exports")
)
or
t = defStep(nd, result)
@@ -981,46 +978,3 @@ private module Label {
/** Gets the `promisedError` edge label connecting a promise to its rejected value. */
string promisedError() { result = "promisedError" }
}
private class NodeModuleSourcesNodes extends DataFlow::SourceNode::Range {
Variable v;
NodeModuleSourcesNodes() {
exists(NodeModule m |
this = DataFlow::ssaDefinitionNode(SSA::implicitInit(v)) and
v = [m.getModuleVariable(), m.getExportsVariable()]
)
}
Variable getVariable() { result = v }
}
/**
* A CommonJS/AMD `module` variable.
*/
private class ModuleVarNode extends DataFlow::Node {
Module m;
ModuleVarNode() {
this.(NodeModuleSourcesNodes).getVariable() = m.(NodeModule).getModuleVariable()
or
DataFlow::parameterNode(this, m.(AmdModule).getDefine().getModuleParameter())
}
Module getModule() { result = m }
}
/**
* A CommonJS/AMD `exports` variable.
*/
private class ExportsVarNode extends DataFlow::Node {
Module m;
ExportsVarNode() {
this.(NodeModuleSourcesNodes).getVariable() = m.(NodeModule).getExportsVariable()
or
DataFlow::parameterNode(this, m.(AmdModule).getDefine().getExportsParameter())
}
Module getModule() { result = m }
}

View File

@@ -68,7 +68,7 @@ module ArrayTaintTracking {
succ = call
or
// `e = Array.from(x)`: if `x` is tainted, then so is `e`.
call = DataFlow::globalVarRef("Array").getAPropertyRead("from").getACall() and
call = arrayFromCall() and
pred = call.getAnArgument() and
succ = call
or
@@ -79,6 +79,11 @@ module ArrayTaintTracking {
call.(DataFlow::MethodCallNode).getMethodName() = "concat" and
succ = call and
pred = call.getAnArgument()
or
// find
// `e = arr.find(callback)`
call = arrayFindCall(pred) and
succ = call
}
}
@@ -97,7 +102,7 @@ private module ArrayDataFlow {
DataFlow::Node pred, DataFlow::Node succ, string fromProp, string toProp
) {
exists(DataFlow::CallNode call |
call = DataFlow::globalVarRef("Array").getAMemberCall("from") and
call = arrayFromCall() and
pred = call.getArgument(0) and
succ = call and
fromProp = arrayLikeElement() and
@@ -297,4 +302,108 @@ private module ArrayDataFlow {
)
}
}
/**
* A step modelling that elements from an array `arr` are received by calling `find`.
*/
private class ArrayFindStep extends DataFlow::SharedFlowStep {
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
exists(DataFlow::CallNode call |
call = arrayFindCall(pred) and
succ = call and
prop = arrayElement()
)
}
}
}
private import ArrayLibraries
/**
* Classes and predicates modelling various libraries that work on arrays or array-like structures.
*/
private module ArrayLibraries {
private import DataFlow::PseudoProperties
/**
* Gets a call to `Array.from` or a polyfill implementing the same functionality.
*/
DataFlow::CallNode arrayFromCall() {
result = DataFlow::globalVarRef("Array").getAMemberCall("from")
or
result = DataFlow::moduleImport("array-from").getACall()
}
/**
* Gets a call to `Array.prototype.find` or a polyfill implementing the same functionality.
*/
DataFlow::CallNode arrayFindCall(DataFlow::Node array) {
result.(DataFlow::MethodCallNode).getMethodName() = "find" and
array = result.getReceiver()
or
result = DataFlow::moduleImport(["array.prototype.find", "array-find"]).getACall() and
array = result.getArgument(0)
}
/**
* A taint step through the `arrify` library, or other libraries that (maybe) convert values into arrays.
*/
private class ArrayifyStep extends TaintTracking::SharedTaintStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(API::CallNode call | call = API::moduleImport(["arrify", "array-ify"]).getACall() |
pred = call.getArgument(0) and succ = call
)
}
}
/**
* A call to a library that copies the elements of an array into another array.
* E.g. `array-union` that creates a union of multiple arrays, or `array-uniq` that creates an array with unique elements.
*/
DataFlow::CallNode arrayCopyCall(DataFlow::Node array) {
result = API::moduleImport(["array-union", "array-uniq", "uniq"]).getACall() and
array = result.getAnArgument()
}
/**
* A taint step for a library that copies the elements of an array into another array.
*/
private class ArrayCopyTaint extends TaintTracking::SharedTaintStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::CallNode call |
call = arrayCopyCall(pred) and
succ = call
)
}
}
/**
* A loadStoreStep for a library that copies the elements of an array into another array.
*/
private class ArrayCopyLoadStore extends DataFlow::SharedFlowStep {
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
exists(DataFlow::CallNode call |
call = arrayCopyCall(pred) and
succ = call and
prop = arrayElement()
)
}
}
/**
* A taint step through a call to `Array.prototype.flat` or a polyfill implementing array flattening.
*/
private class ArrayFlatStep extends TaintTracking::SharedTaintStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::CallNode call | succ = call |
call.(DataFlow::MethodCallNode).getMethodName() = "flat" and
pred = call.getReceiver()
or
call =
API::moduleImport(["array-flatten", "arr-flatten", "flatten", "array.prototype.flat"])
.getACall() and
pred = call.getAnArgument()
)
}
}
}

View File

@@ -178,11 +178,16 @@ private class ExtendCallTaintStep extends TaintTracking::SharedTaintStep {
private import semmle.javascript.dataflow.internal.PreCallGraphStep
/**
* A step for the `clone` package.
* A step through a cloning library, such as `clone` or `fclone`.
*/
private class CloneStep extends PreCallGraphStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::CallNode call | call = DataFlow::moduleImport("clone").getACall() |
exists(DataFlow::CallNode call |
// `camelcase-keys` isn't quite a cloning library. But it's pretty close.
call = DataFlow::moduleImport(["clone", "fclone", "sort-keys", "camelcase-keys"]).getACall()
or
call = DataFlow::moduleMember("json-cycle", ["decycle", "retrocycle"]).getACall()
|
pred = call.getArgument(0) and
succ = call
)

View File

@@ -26,6 +26,10 @@ private class PlainJsonParserCall extends JsonParserCall {
PlainJsonParserCall() {
exists(DataFlow::SourceNode callee | this = callee.getACall() |
callee = DataFlow::globalVarRef("JSON").getAPropertyRead("parse") or
callee =
DataFlow::moduleMember(["json3", "json5", "flatted", "teleport-javascript", "json-cycle"],
"parse") or
callee = API::moduleImport("replicator").getInstance().getMember("decode").getAnImmediateUse() or
callee = DataFlow::moduleImport("parse-json") or
callee = DataFlow::moduleImport("json-parse-better-errors") or
callee = DataFlow::moduleImport("json-safe-parse") or
@@ -74,3 +78,15 @@ private class JsonParserCallWithCallback extends JsonParserCall {
override DataFlow::SourceNode getOutput() { result = getCallback(1).getParameter(1) }
}
/**
* A taint step through the `strip-json-comments` library.
*/
private class StripJsonCommentsStep extends TaintTracking::SharedTaintStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(API::CallNode call | call = API::moduleImport("strip-json-comments").getACall() |
pred = call.getArgument(0) and
succ = call
)
}
}

View File

@@ -11,12 +11,15 @@ class JsonStringifyCall extends DataFlow::CallNode {
JsonStringifyCall() {
exists(DataFlow::SourceNode callee | this = callee.getACall() |
callee = DataFlow::globalVarRef("JSON").getAPropertyRead("stringify") or
callee = DataFlow::moduleMember("json3", "stringify") or
callee =
DataFlow::moduleMember(["json3", "json5", "flatted", "teleport-javascript", "json-cycle"],
"stringify") or
callee = API::moduleImport("replicator").getInstance().getMember("encode").getAnImmediateUse() or
callee =
DataFlow::moduleImport([
"json-stringify-safe", "json-stable-stringify", "stringify-object",
"fast-json-stable-stringify", "fast-safe-stringify", "javascript-stringify",
"js-stringify"
"js-stringify", "safe-stable-stringify", "fast-json-stringify"
]) or
// require("util").inspect() and similar
callee = DataFlow::moduleMember("util", "inspect") or
@@ -34,3 +37,38 @@ class JsonStringifyCall extends DataFlow::CallNode {
*/
DataFlow::SourceNode getOutput() { result = this }
}
/**
* A taint step through the [`json2csv`](https://www.npmjs.com/package/json2csv) library.
*/
class JSON2CSVTaintStep extends TaintTracking::SharedTaintStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(API::CallNode call |
call =
API::moduleImport("json2csv")
.getMember("Parser")
.getInstance()
.getMember("parse")
.getACall()
|
pred = call.getArgument(0) and
succ = call
)
}
}
/**
* A step through the [`prettyjson`](https://www.npmjs.com/package/prettyjson) library.
* This is not quite a `JSON.stringify` call, as it e.g. does not wrap keys in double quotes.
* It's therefore modelled as a taint-step rather than as a `JSON.stringify` call.
*/
class PrettyJSONTaintStep extends TaintTracking::SharedTaintStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(API::CallNode call |
call = API::moduleImport("prettyjson").getMember("render").getACall()
|
pred = call.getArgument(0) and
succ = call
)
}
}

View File

@@ -1183,6 +1183,13 @@ private predicate flowThroughCall(
not cfg.isLabeledBarrier(output, summary.getEndLabel())
)
or
exists(Function f, LocalVariable variable |
reachableFromInput(f, _, input, output, cfg, summary) and
output = DataFlow::capturedVariableNode(variable) and
getCapturedVariableDepth(variable) < getContainerDepth(f) and // Only step outwards
not cfg.isLabeledBarrier(output, summary.getEndLabel())
)
or
exists(Function f, DataFlow::Node invk, DataFlow::Node ret |
DataFlow::exceptionalFunctionReturnNode(ret, f) and
DataFlow::exceptionalInvocationReturnNode(output, invk.asExpr()) and

View File

@@ -347,6 +347,55 @@ module SourceNode {
}
}
private class NodeModuleSourcesNodes extends SourceNode::Range {
Variable v;
NodeModuleSourcesNodes() {
exists(NodeModule m |
this = DataFlow::ssaDefinitionNode(SSA::implicitInit(v)) and
v = [m.getModuleVariable(), m.getExportsVariable()]
)
}
Variable getVariable() { result = v }
}
/**
* A CommonJS/AMD `module` variable.
*/
private class ModuleVarNode extends DataFlow::Node {
Module m;
ModuleVarNode() {
this.(NodeModuleSourcesNodes).getVariable() = m.(NodeModule).getModuleVariable()
or
DataFlow::parameterNode(this, m.(AmdModule).getDefine().getModuleParameter())
}
Module getModule() { result = m }
}
/**
* A CommonJS/AMD `exports` variable.
*/
private class ExportsVarNode extends DataFlow::Node {
Module m;
ExportsVarNode() {
this.(NodeModuleSourcesNodes).getVariable() = m.(NodeModule).getExportsVariable()
or
DataFlow::parameterNode(this, m.(AmdModule).getDefine().getExportsParameter())
}
Module getModule() { result = m }
}
/** Gets the CommonJS/AMD `module` variable for module `m`. */
SourceNode moduleVarNode(Module m) { result.(ModuleVarNode).getModule() = m }
/** Gets the CommonJS/AMD `exports` variable for module `m`. */
SourceNode exportsVarNode(Module m) { result.(ExportsVarNode).getModule() = m }
deprecated class DefaultSourceNode extends SourceNode {
DefaultSourceNode() { this instanceof SourceNode::DefaultRange }
}

View File

@@ -109,13 +109,30 @@ DataFlow::Node getThrowTarget(DataFlow::Node thrower) {
*/
cached
private module CachedSteps {
/** Gets the nesting depth of the given container, starting with the top-level at 0. */
cached
int getContainerDepth(StmtContainer container) {
not exists(container.getEnclosingContainer()) and
result = 0
or
result = 1 + getContainerDepth(container.getEnclosingContainer())
}
/** Gets the nesting depth of the container declaring the given captured variable. */
cached
int getCapturedVariableDepth(LocalVariable v) {
v.isCaptured() and
result = getContainerDepth(v.getDeclaringContainer())
}
/**
* Holds if `f` captures the given `variable` in `cap`.
*/
cached
predicate captures(Function f, LocalVariable variable, SsaVariableCapture cap) {
variable = cap.getSourceVariable() and
f = cap.getContainer()
f = cap.getContainer() and
not f = variable.getDeclaringContainer()
}
/**

View File

@@ -216,20 +216,13 @@ module Angular2 {
}
}
private string getInternalName(string name) {
exists(Identifier id |
result = id.getName() and
name = result.regexpCapture("\\u0275(DomAdapter|getDOM)", 1)
)
}
/** Gets a reference to a `DomAdapter`, which provides acess to raw DOM elements. */
private DataFlow::SourceNode domAdapter() {
// Note: these are internal properties, prefixed with the "latin small letter barred O (U+0275)" character.
// Despite being internal, some codebases do access them.
result.hasUnderlyingType("@angular/common", getInternalName("DomAdapter"))
result.hasUnderlyingType("@angular/common", 629.toUnicode() + "DomAdapter")
or
result = DataFlow::moduleImport("@angular/common").getAMemberCall(getInternalName("getDOM"))
result = DataFlow::moduleImport("@angular/common").getAMemberCall(629.toUnicode() + "getDOM")
}
/** A reference to the DOM location obtained through `DomAdapter.getLocation()`. */

View File

@@ -470,3 +470,16 @@ class Chokidar extends FileNameProducer, FileSystemAccess, API::CallNode {
)
}
}
/**
* A call to the [`mkdirp`](https://www.npmjs.com/package/mkdirp) library.
*/
private class Mkdirp extends FileSystemAccess, API::CallNode {
Mkdirp() {
this = API::moduleImport("mkdirp").getACall()
or
this = API::moduleImport("mkdirp").getMember("sync").getACall()
}
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
}

View File

@@ -383,3 +383,17 @@ private module Pino {
override DataFlow::Node getAMessageComponent() { result = getAnArgument() }
}
}
/**
* A step through the [`ansi-to-html`](https://npmjs.org/package/ansi-to-html) library.
*/
class AnsiToHtmlStep extends TaintTracking::SharedTaintStep {
override predicate stringManipulationStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(API::CallNode call |
call = API::moduleImport("ansi-to-html").getInstance().getMember("toHtml").getACall()
|
pred = call.getArgument(0) and
succ = call
)
}
}

View File

@@ -103,3 +103,39 @@ private class LibraryFormatter extends PrintfStyleCall {
override predicate returnsFormatted() { returns = true }
}
/**
* A taint step through a case changing function.
*/
private class CaseChangingStep extends TaintTracking::SharedTaintStep {
override predicate stringManipulationStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::SourceNode callee, DataFlow::CallNode call |
callee = DataFlow::moduleMember("change-case", _) or
callee = DataFlow::moduleMember("camel-case", "camelCase") or
callee = DataFlow::moduleMember("pascal-case", "pascalCase") or
callee = DataFlow::moduleMember("snake-case", "snakeCase") or
callee = DataFlow::moduleImport("kebab-case") or
callee = DataFlow::moduleMember("kebab-case", "reverse") or
callee = DataFlow::moduleMember("param-case", "paramCase") or
callee = DataFlow::moduleMember("path-case", "pathCase") or
callee = DataFlow::moduleMember("sentence-case", "sentenceCase") or
callee = DataFlow::moduleMember("title-case", "titleCase") or
callee = DataFlow::moduleMember("upper-case", ["upperCase", "localeUpperCase"]) or
callee = DataFlow::moduleMember("lower-case", ["lowerCase", "localeLowerCase"]) or
callee = DataFlow::moduleMember("no-case", "noCase") or
callee = DataFlow::moduleMember("constant-case", "constantCase") or
callee = DataFlow::moduleMember("dot-case", "dotCase") or
callee = DataFlow::moduleMember("upper-case-first", "upperCaseFirst") or
callee = DataFlow::moduleMember("lower-case-first", "lowerCaseFirst") or
callee = DataFlow::moduleMember("header-case", "headerCase") or
callee = DataFlow::moduleMember("capital-case", "capitalCase") or
callee = DataFlow::moduleMember("swap-case", "swapCase") or
callee = DataFlow::moduleMember("sponge-case", "spongeCase") or
callee = DataFlow::moduleImport(["titleize", "camelcase", "decamelize"])
|
call = callee.getACall() and
pred = call.getArgument(0) and
succ = call
)
}
}

View File

@@ -96,13 +96,8 @@ module uridashjs {
*/
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"
|
call = uridashjsMember(name).getACall() and
exists(DataFlow::CallNode call |
call = uridashjsMember(["parse", "serialize", "resolve", "normalize"]).getACall() and
pred = call.getAnArgument() and
succ = call
)
@@ -126,13 +121,8 @@ module punycode {
*/
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"
|
call = punycodeMember(name).getACall() and
exists(DataFlow::CallNode call |
call = punycodeMember(["decode", "encode", "toUnicode", "toASCII"]).getACall() and
pred = call.getAnArgument() and
succ = call
)
@@ -193,11 +183,8 @@ module querystringify {
*/
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"
|
call = querystringifyMember(name).getACall() and
exists(DataFlow::CallNode call |
call = querystringifyMember(["parse", "stringify"]).getACall() and
pred = call.getAnArgument() and
succ = call
)
@@ -221,13 +208,8 @@ module querydashstring {
*/
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"
|
call = querydashstringMember(name).getACall() and
exists(DataFlow::CallNode call |
call = querydashstringMember(["parse", "extract", "parseUrl", "stringify"]).getACall() and
pred = call.getAnArgument() and
succ = call
)
@@ -249,12 +231,8 @@ module url {
*/
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"
|
call = urlMember(name).getACall() and
exists(DataFlow::CallNode call |
call = urlMember(["parse", "format", "resolve"]).getACall() and
pred = call.getAnArgument() and
succ = call
)
@@ -278,13 +256,8 @@ module querystring {
*/
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"
|
call = querystringMember(name).getACall() and
exists(DataFlow::CallNode call |
call = querystringMember(["escape", "unescape", "parse", "stringify"]).getACall() and
pred = call.getAnArgument() and
succ = call
)
@@ -292,6 +265,45 @@ module querystring {
}
}
/**
* 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.
*/

View File

@@ -169,13 +169,19 @@ module CodeInjection {
}
/**
* The first argument to `Module.prototype._compile` from the Node.js built-in module `module`,
* considered as a code-injection sink.
* The first argument to `Module.prototype._compile`, considered as a code-injection sink.
*/
class ModuleCompileSink extends Sink {
ModuleCompileSink() {
// `require('module').prototype._compile`
this =
API::moduleImport("module").getInstance().getMember("_compile").getACall().getArgument(0)
or
// `module.constructor.prototype._compile`
exists(DataFlow::SourceNode moduleConstructor |
moduleConstructor = DataFlow::moduleVarNode(_).getAPropertyRead("constructor") and
this = moduleConstructor.getAnInstantiation().getAMethodCall("_compile").getArgument(0)
)
}
}

View File

@@ -682,6 +682,20 @@ module TaintedPath {
}
}
/**
* The `cwd` option for the `read-pkg` library.
*/
private class ReadPkgCwdSink extends TaintedPath::Sink {
ReadPkgCwdSink() {
this =
API::moduleImport("read-pkg")
.getMember(["readPackageAsync", "readPackageSync"])
.getParameter(0)
.getMember("cwd")
.getARhs()
}
}
/**
* Holds if there is a step `src -> dst` mapping `srclabel` to `dstlabel` relevant for path traversal vulnerabilities.
*/
@@ -798,6 +812,12 @@ module TaintedPath {
srclabel instanceof Label::SplitPath and
dstlabel.(Label::PosixPath).canContainDotDotSlash()
)
or
exists(API::CallNode call | call = API::moduleImport("slash").getACall() |
src = call.getArgument(0) and
dst = call and
srclabel = dstlabel
)
}
/**

View File

@@ -318,6 +318,20 @@ module DomBasedXss {
}
}
/**
* A React tooltip where the `data-html` attribute is set to `true`.
*/
class TooltipSink extends Sink {
TooltipSink() {
exists(JSXElement el |
el.getAttributeByName("data-html").getStringValue() = "true" or
el.getAttributeByName("data-html").getValue().mayHaveBooleanValue(true)
|
this = el.getAttributeByName("data-tip").getValue().flow()
)
}
}
/**
* The HTML body of an email, viewed as an XSS sink.
*/