mirror of
https://github.com/github/codeql.git
synced 2026-04-30 19:26:02 +02:00
Merge pull request #1290 from esben-semmle/js/semver-lib
JS: add SemVer library
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
+ semmlecode-javascript-queries/Security/CWE-346/CorsMisconfigurationForCredentials.ql: /Security/CWE/CWE-346
|
||||
+ semmlecode-javascript-queries/Security/CWE-352/MissingCsrfMiddleware.ql: /Security/CWE/CWE-352
|
||||
+ semmlecode-javascript-queries/Security/CWE-400/RemotePropertyInjection.ql: /Security/CWE/CWE-400
|
||||
+ semmlecode-javascript-queries/Security/CWE-400/PrototypePollution.ql: /Security/CWE/CWE-400
|
||||
+ semmlecode-javascript-queries/Security/CWE-502/UnsafeDeserialization.ql: /Security/CWE/CWE-502
|
||||
+ semmlecode-javascript-queries/Security/CWE-506/HardcodedDataInterpretedAsCode.ql: /Security/CWE/CWE-506
|
||||
+ semmlecode-javascript-queries/Security/CWE-601/ClientSideUrlRedirect.ql: /Security/CWE/CWE-601
|
||||
|
||||
@@ -14,7 +14,15 @@
|
||||
import javascript
|
||||
import semmle.javascript.security.dataflow.PrototypePollution::PrototypePollution
|
||||
import DataFlow::PathGraph
|
||||
import semmle.javascript.dependencies.Dependencies
|
||||
|
||||
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Prototype pollution caused by merging a user-controlled value from $@.", source, "here"
|
||||
from
|
||||
Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, Dependency dependency,
|
||||
string dependencyId
|
||||
where
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
dependency = sink.getNode().(Sink).getDependency() and
|
||||
dependency.info(dependencyId, _)
|
||||
select sink.getNode(), source, sink,
|
||||
"Prototype pollution caused by merging a user-controlled value from $@ using a vulnerable version of $@.",
|
||||
source, "here", dependency, dependencyId
|
||||
|
||||
@@ -263,6 +263,13 @@ private class AmdDependencyImport extends Import {
|
||||
not exists(super.getImportedModule()) and
|
||||
result = resolveByAbsolutePath()
|
||||
}
|
||||
|
||||
override DataFlow::Node getImportedModuleNode() {
|
||||
exists(Parameter param |
|
||||
any(AmdModuleDefinition def).dependencyParameter(this, param) and
|
||||
result = DataFlow::parameterNode(param)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,6 +43,22 @@ class ImportDeclaration extends Stmt, Import, @importdeclaration {
|
||||
|
||||
/** Gets an import specifier of this import declaration. */
|
||||
ImportSpecifier getASpecifier() { result = getSpecifier(_) }
|
||||
|
||||
override DataFlow::Node getImportedModuleNode() {
|
||||
// `import * as http from 'http'` or `import http from `http`'
|
||||
exists(ImportSpecifier is |
|
||||
is = getASpecifier() and
|
||||
result = DataFlow::ssaDefinitionNode(SSA::definition(is))
|
||||
|
|
||||
is instanceof ImportNamespaceSpecifier and
|
||||
count(getASpecifier()) = 1
|
||||
or
|
||||
is.getImportedName() = "default"
|
||||
)
|
||||
or
|
||||
// `import { createServer } from 'http'`
|
||||
result = DataFlow::destructuredModuleImportNode(this)
|
||||
}
|
||||
}
|
||||
|
||||
/** A literal path expression appearing in an `import` declaration. */
|
||||
|
||||
@@ -1562,6 +1562,8 @@ class DynamicImportExpr extends @dynamicimport, Expr, Import {
|
||||
override PathExpr getImportedPath() { result = getSource() }
|
||||
|
||||
override Module getEnclosingModule() { result = getTopLevel() }
|
||||
|
||||
override DataFlow::Node getImportedModuleNode() { result = DataFlow::valueNode(this) }
|
||||
}
|
||||
|
||||
/** A literal path expression appearing in a dynamic import. */
|
||||
|
||||
@@ -160,6 +160,11 @@ abstract class Import extends ASTNode {
|
||||
result = resolveFromTypeRoot()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the data flow node that the default import of this import is available at.
|
||||
*/
|
||||
abstract DataFlow::Node getImportedModuleNode();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -234,6 +234,8 @@ class Require extends CallExpr, Import {
|
||||
priority - (prioritiesPerCandidate() * r + numberOfExtensions() + 1))
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getImportedModuleNode() { result = DataFlow::valueNode(this) }
|
||||
}
|
||||
|
||||
/** An argument to `require` or `require.resolve`, considered as a path expression. */
|
||||
|
||||
@@ -212,6 +212,8 @@ class ExternalModuleReference extends Expr, Import, @externalmodulereference {
|
||||
override ControlFlowNode getFirstControlFlowNode() {
|
||||
result = getExpression().getFirstControlFlowNode()
|
||||
}
|
||||
|
||||
override DataFlow::Node getImportedModuleNode() { result = DataFlow::valueNode(this) }
|
||||
}
|
||||
|
||||
/** A literal path expression appearing in an external module reference. */
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.dependencies.Dependencies
|
||||
|
||||
/** A data flow node corresponding to an expression. */
|
||||
class ExprNode extends DataFlow::ValueNode {
|
||||
@@ -461,37 +462,9 @@ module ModuleImportNode {
|
||||
string path;
|
||||
|
||||
DefaultRange() {
|
||||
// `require("http")`
|
||||
exists(Require req | req.getImportedPath().getValue() = path |
|
||||
this = DataFlow::valueNode(req)
|
||||
)
|
||||
or
|
||||
// `import http = require("http")`
|
||||
exists(ExternalModuleReference req | req.getImportedPath().getValue() = path |
|
||||
this = DataFlow::valueNode(req)
|
||||
)
|
||||
or
|
||||
// `import * as http from 'http'` or `import http from `http`'
|
||||
exists(ImportDeclaration id, ImportSpecifier is |
|
||||
id.getImportedPath().getValue() = path and
|
||||
is = id.getASpecifier() and
|
||||
this = DataFlow::ssaDefinitionNode(SSA::definition(is))
|
||||
|
|
||||
is instanceof ImportNamespaceSpecifier and
|
||||
count(id.getASpecifier()) = 1
|
||||
or
|
||||
is.getImportedName() = "default"
|
||||
)
|
||||
or
|
||||
// `import { createServer } from 'http'`
|
||||
exists(ImportDeclaration id |
|
||||
this = DataFlow::destructuredModuleImportNode(id) and
|
||||
id.getImportedPath().getValue() = path
|
||||
)
|
||||
or
|
||||
// declared AMD dependency
|
||||
exists(AmdModuleDefinition amd |
|
||||
this = DataFlow::parameterNode(amd.getDependencyParameter(path))
|
||||
exists(Import i |
|
||||
this = i.getImportedModuleNode() and
|
||||
i.getImportedPath().getValue() = path
|
||||
)
|
||||
or
|
||||
// AMD require
|
||||
@@ -515,6 +488,15 @@ module ModuleImportNode {
|
||||
*/
|
||||
ModuleImportNode moduleImport(string path) { result.getPath() = path }
|
||||
|
||||
/**
|
||||
* Gets a (default) import of the given dependency `dep`, such as
|
||||
* `require("lodash")` in a context where a package.json file includes
|
||||
* `"lodash"` as a dependency.
|
||||
*/
|
||||
ModuleImportNode dependencyModuleImport(Dependency dep) {
|
||||
result = dep.getAUse("import").(Import).getImportedModuleNode()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data flow node that either imports `m` from the module with
|
||||
* the given `path`, or accesses `m` as a member on a default or
|
||||
|
||||
@@ -23,7 +23,7 @@ abstract class Dependency extends Locatable {
|
||||
/**
|
||||
* A use of this dependency, which is of the given `kind`.
|
||||
*
|
||||
* Currently, the only supported kind is `"import"`.
|
||||
* Currently, the only supported kinds are `"import"` and `"use"`.
|
||||
*/
|
||||
abstract Locatable getAUse(string kind);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ abstract class FrameworkLibrary extends string {
|
||||
* a file or script containing the code for a particular
|
||||
* version of a framework.
|
||||
*/
|
||||
abstract class FrameworkLibraryInstance extends Script {
|
||||
abstract class FrameworkLibraryInstance extends TopLevel {
|
||||
/**
|
||||
* Holds if this is an instance of version `v` of framework library `fl`.
|
||||
*/
|
||||
@@ -184,6 +184,7 @@ class FrameworkLibraryInstanceWithMarkerComment extends FrameworkLibraryInstance
|
||||
* Holds if comment `c` in toplevel `tl` matches the marker comment of library
|
||||
* `fl` at `version`.
|
||||
*/
|
||||
cached
|
||||
private predicate matchMarkerComment(
|
||||
Comment c, TopLevel tl, FrameworkLibraryWithMarkerComment fl, string version
|
||||
) {
|
||||
|
||||
74
javascript/ql/src/semmle/javascript/dependencies/SemVer.qll
Normal file
74
javascript/ql/src/semmle/javascript/dependencies/SemVer.qll
Normal file
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Provides classes for working SemVer (Semantic Versioning).
|
||||
*/
|
||||
|
||||
import semmle.javascript.dependencies.Dependencies
|
||||
|
||||
/**
|
||||
* A SemVer-formatted version string in a dependency.
|
||||
*
|
||||
* Pre-release information and build metadata is not yet supported.
|
||||
*/
|
||||
class DependencySemVer extends string {
|
||||
Dependency dep;
|
||||
|
||||
string normalized;
|
||||
|
||||
DependencySemVer() {
|
||||
dep.info(_, this) and
|
||||
normalized = normalizeSemver(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this version may be before `last`.
|
||||
*/
|
||||
bindingset[last]
|
||||
predicate maybeBefore(string last) { normalized < normalizeSemver(last) }
|
||||
|
||||
/**
|
||||
* Holds if this version may be after `first`.
|
||||
*/
|
||||
bindingset[first]
|
||||
predicate maybeAfter(string first) { normalizeSemver(first) < normalized }
|
||||
|
||||
/**
|
||||
* Holds if this version may be between `first` (inclusive) and `last` (exclusive).
|
||||
*/
|
||||
bindingset[first, last]
|
||||
predicate maybeBetween(string first, string last) {
|
||||
normalizeSemver(first) <= normalized and
|
||||
normalized < normalizeSemver(last)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this version is equivalent to `other`.
|
||||
*/
|
||||
bindingset[other]
|
||||
predicate is(string other) { normalized = normalizeSemver(other) }
|
||||
|
||||
/**
|
||||
* Gets the dependency that uses this string.
|
||||
*/
|
||||
Dependency getDependency() { result = dep }
|
||||
}
|
||||
|
||||
bindingset[str]
|
||||
private string leftPad(string str) { result = ("000" + str).suffix(str.length()) }
|
||||
|
||||
/**
|
||||
* Normalizes a SemVer string such that the lexicographical ordering
|
||||
* of two normalized strings is consistent with the SemVer ordering.
|
||||
*
|
||||
* Pre-release information and build metadata is not yet supported.
|
||||
*/
|
||||
bindingset[orig]
|
||||
private string normalizeSemver(string orig) {
|
||||
exists(string pattern, string major, string minor, string patch |
|
||||
pattern = "(\\d+)\\.(\\d+)\\.(\\d+)" and
|
||||
major = orig.regexpCapture(pattern, 1) and
|
||||
minor = orig.regexpCapture(pattern, 2) and
|
||||
patch = orig.regexpCapture(pattern, 3)
|
||||
|
|
||||
result = leftPad(major) + "." + leftPad(minor) + "." + leftPad(patch)
|
||||
)
|
||||
}
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.security.TaintedObject
|
||||
import semmle.javascript.dependencies.Dependencies
|
||||
import semmle.javascript.dependencies.SemVer
|
||||
|
||||
module PrototypePollution {
|
||||
/**
|
||||
@@ -47,6 +49,11 @@ module PrototypePollution {
|
||||
* Gets the type of data that can taint this sink.
|
||||
*/
|
||||
abstract DataFlow::FlowLabel getAFlowLabel();
|
||||
|
||||
/**
|
||||
* Gets the dependency that defines this sink.
|
||||
*/
|
||||
abstract Dependency getDependency();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,30 +110,15 @@ module PrototypePollution {
|
||||
|
||||
override DataFlow::FlowLabel getAFlowLabel() { result = TaintedObject::label() }
|
||||
}
|
||||
|
||||
string getModuleName(ExtendCall call) {
|
||||
call = DataFlow::moduleImport(result).getACall() or
|
||||
call = DataFlow::moduleMember(result, _).getACall()
|
||||
}
|
||||
|
||||
class DeepExtendSink extends Sink {
|
||||
ExtendCall call;
|
||||
|
||||
Dependency dependency;
|
||||
|
||||
DeepExtendSink() {
|
||||
this = call.getASourceOperand() and
|
||||
call.isDeep() and
|
||||
exists(string moduleName | moduleName = getModuleName(call) |
|
||||
moduleName = "lodash" + any(string s) or
|
||||
moduleName = "just-extend" or
|
||||
moduleName = "extend" or
|
||||
moduleName = "extend2" or
|
||||
moduleName = "node.extend" or
|
||||
moduleName = "merge" or
|
||||
moduleName = "smart-extend" or
|
||||
moduleName = "js-extend" or
|
||||
moduleName = "deep" or
|
||||
moduleName = "defaults-deep"
|
||||
)
|
||||
isVulnerableDeepExtendCall(call, dependency)
|
||||
}
|
||||
|
||||
override DataFlow::FlowLabel getAFlowLabel() {
|
||||
@@ -134,5 +126,57 @@ module PrototypePollution {
|
||||
or
|
||||
result = TaintedObjectWrapper::label()
|
||||
}
|
||||
|
||||
override Dependency getDependency() { result = dependency }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `call` is vulnerable to prototype pollution because the callee is defined by `dep`.
|
||||
*/
|
||||
predicate isVulnerableDeepExtendCall(ExtendCall call, Dependency dep) {
|
||||
call.isDeep() and
|
||||
(
|
||||
call = DataFlow::dependencyModuleImport(dep).getAMemberCall(_) or
|
||||
call = DataFlow::dependencyModuleImport(dep).getACall()
|
||||
) and
|
||||
exists(DependencySemVer version, string id | dep.info(id, version) |
|
||||
id = "assign-deep" and
|
||||
version.maybeBefore("0.4.7")
|
||||
or
|
||||
id = "deep"
|
||||
or
|
||||
id = "deep-extend" and
|
||||
version.maybeBefore("0.5.1")
|
||||
or
|
||||
id = "defaults-deep" and
|
||||
version.maybeBefore("0.2.4")
|
||||
or
|
||||
id = "extend" and
|
||||
(version.maybeBefore("2.0.2") or version.maybeBetween("3.0.0", "3.0.2"))
|
||||
or
|
||||
id = "extend2"
|
||||
or
|
||||
id = "js-extend"
|
||||
or
|
||||
id = "just-extend" and
|
||||
version.maybeBefore("4.0.1")
|
||||
or
|
||||
id = "lodash" + any(string s) and
|
||||
version.maybeBefore("4.17.11")
|
||||
or
|
||||
id = "merge" and
|
||||
version.maybeBefore("1.2.1")
|
||||
or
|
||||
id = "merge-deep" and
|
||||
version.maybeBefore("3.0.1")
|
||||
or
|
||||
id = "merge-options" and
|
||||
version.maybeBefore("1.0.1")
|
||||
or
|
||||
id = "node.extend" and
|
||||
(version.maybeBefore("1.1.7") or version.maybeBetween("2.0.0", "2.0.1"))
|
||||
or
|
||||
id = "smart-extend"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
| src/dir-with-complex-package-json/tst.js:6:7:6:7 | x |
|
||||
| src/dir-with-complex-package-json/tst.js:8:10:8:10 | x |
|
||||
| src/dir-with-package-json/tst.js:7:7:7:7 | x |
|
||||
| src/dir-with-package-json/tst.js:13:10:13:10 | x |
|
||||
| src/dir-with-package-lock-json/tst.js:10:7:10:7 | x |
|
||||
| src/dir-with-package-lock-json/tst.js:15:7:15:7 | x |
|
||||
| src/dir-without-package-json/tst.js:10:7:10:7 | x |
|
||||
| src/dir-without-package-json/tst.js:15:7:15:7 | x |
|
||||
@@ -0,0 +1,44 @@
|
||||
import javascript
|
||||
import semmle.javascript.dependencies.Dependencies
|
||||
import semmle.javascript.dependencies.SemVer
|
||||
|
||||
class SampleVersionSink extends DataFlow::Node {
|
||||
SampleVersionSink() {
|
||||
exists(
|
||||
string dependencyName, Dependency dep, DependencySemVer vDep, string vFrom, string vTo,
|
||||
string functionName, int argNumber
|
||||
|
|
||||
dependencyName = "a" and
|
||||
vFrom = "0.1.0" and
|
||||
vTo = "1.1.0" and
|
||||
functionName = "m1" and
|
||||
argNumber = 0
|
||||
or
|
||||
dependencyName = "b" and
|
||||
vFrom = "1.1.0" and
|
||||
vTo = "2.1.0" and
|
||||
functionName = "m1" and
|
||||
argNumber = 0
|
||||
or
|
||||
dependencyName = "c" and
|
||||
vFrom = "0.1.0" and
|
||||
vTo = "1.1.0" and
|
||||
functionName = "m1" and
|
||||
argNumber = 1
|
||||
or
|
||||
dependencyName = "d" and
|
||||
vFrom = "0.1.0" and
|
||||
vTo = "1.0.0" and
|
||||
functionName = "m1" and
|
||||
argNumber = 0
|
||||
|
|
||||
this = DataFlow::dependencyModuleImport(dep)
|
||||
.getAMemberCall(functionName)
|
||||
.getArgument(argNumber) and
|
||||
dep.info(dependencyName, vDep) and
|
||||
vDep.maybeBetween(vFrom, vTo)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
select any(SampleVersionSink s)
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"a": "^1.0.0",
|
||||
"c": ">=1.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
const a = require('a'),
|
||||
c = require('c');
|
||||
|
||||
|
||||
(function() {
|
||||
a.m1(x); // flagged
|
||||
|
||||
c.m1(0, x); // flagged
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"a": "1.0.0",
|
||||
"b": "1.0.0",
|
||||
"c": "1.0.0",
|
||||
"d": "1.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
const a = require('a'),
|
||||
b = require('b'),
|
||||
c = require('c'),
|
||||
d = require('d');
|
||||
|
||||
(function() {
|
||||
a.m1(x); // flagged
|
||||
a.m2(x); // not flagged: other method
|
||||
|
||||
b.m1(x); // not flagged: early version
|
||||
|
||||
c.m1(x); // not flagged: other argument
|
||||
c.m1(0, x); // flagged
|
||||
|
||||
d.m1(x); // not flagged: late version
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"a": "1.0.0",
|
||||
"b": "1.0.0",
|
||||
"c": "1.0.0",
|
||||
"d": "1.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
const a = require('a'),
|
||||
b = require('b'),
|
||||
c = require('c'),
|
||||
d = require('d');
|
||||
|
||||
(function() {
|
||||
a.m1(x); // flagged
|
||||
a.m2(x); // not flagged: other method
|
||||
|
||||
b.m1(x); // not flagged: early version
|
||||
|
||||
c.m1(x); // not flagged: other argument
|
||||
c.m1(0, x); // flagged
|
||||
|
||||
d.m1(x); // not flagged: late version
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
const a = require('a'),
|
||||
b = require('b'),
|
||||
c = require('c'),
|
||||
d = require('d');
|
||||
|
||||
(function() {
|
||||
a.m1(x); // not flagged: not a dependency
|
||||
a.m2(x); // not flagged: not a dependency
|
||||
|
||||
b.m1(x); // flagged
|
||||
|
||||
c.m1(x); // not flagged: not a dependency
|
||||
c.m1(0, x); // not flagged: not a dependency
|
||||
|
||||
d.m1(x); // flagged
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"b": "1.2.0",
|
||||
"d": "0.2.0"
|
||||
}
|
||||
}
|
||||
7
javascript/ql/test/query-tests/NodeJS/UnusedDependency/src/package-lock.json
generated
Normal file
7
javascript/ql/test/query-tests/NodeJS/UnusedDependency/src/package-lock.json
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "test-package",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"package-lock.json-dependency": "=1.2.3"
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
nodes
|
||||
| PrototypePollution.js:7:17:7:29 | req.query.foo |
|
||||
| PrototypePollution.js:10:17:12:5 | {\\n ... K\\n } |
|
||||
| PrototypePollution.js:11:16:11:30 | req.query.value |
|
||||
| PrototypePollution.js:15:14:15:28 | req.query.value |
|
||||
| PrototypePollution.js:17:17:19:5 | {\\n ... K\\n } |
|
||||
| PrototypePollution.js:18:16:18:25 | opts.thing |
|
||||
| src-vulnerable-lodash/tst.js:7:17:7:29 | req.query.foo |
|
||||
| src-vulnerable-lodash/tst.js:10:17:12:5 | {\\n ... K\\n } |
|
||||
| src-vulnerable-lodash/tst.js:11:16:11:30 | req.query.value |
|
||||
| src-vulnerable-lodash/tst.js:15:14:15:28 | req.query.value |
|
||||
| src-vulnerable-lodash/tst.js:17:17:19:5 | {\\n ... K\\n } |
|
||||
| src-vulnerable-lodash/tst.js:18:16:18:25 | opts.thing |
|
||||
edges
|
||||
| PrototypePollution.js:11:16:11:30 | req.query.value | PrototypePollution.js:10:17:12:5 | {\\n ... K\\n } |
|
||||
| PrototypePollution.js:15:14:15:28 | req.query.value | PrototypePollution.js:18:16:18:25 | opts.thing |
|
||||
| PrototypePollution.js:18:16:18:25 | opts.thing | PrototypePollution.js:17:17:19:5 | {\\n ... K\\n } |
|
||||
| src-vulnerable-lodash/tst.js:11:16:11:30 | req.query.value | src-vulnerable-lodash/tst.js:10:17:12:5 | {\\n ... K\\n } |
|
||||
| src-vulnerable-lodash/tst.js:15:14:15:28 | req.query.value | src-vulnerable-lodash/tst.js:18:16:18:25 | opts.thing |
|
||||
| src-vulnerable-lodash/tst.js:18:16:18:25 | opts.thing | src-vulnerable-lodash/tst.js:17:17:19:5 | {\\n ... K\\n } |
|
||||
#select
|
||||
| PrototypePollution.js:7:17:7:29 | req.query.foo | PrototypePollution.js:7:17:7:29 | req.query.foo | PrototypePollution.js:7:17:7:29 | req.query.foo | Prototype pollution caused by merging a user-controlled value from $@. | PrototypePollution.js:7:17:7:29 | req.query.foo | here |
|
||||
| PrototypePollution.js:10:17:12:5 | {\\n ... K\\n } | PrototypePollution.js:11:16:11:30 | req.query.value | PrototypePollution.js:10:17:12:5 | {\\n ... K\\n } | Prototype pollution caused by merging a user-controlled value from $@. | PrototypePollution.js:11:16:11:30 | req.query.value | here |
|
||||
| PrototypePollution.js:17:17:19:5 | {\\n ... K\\n } | PrototypePollution.js:15:14:15:28 | req.query.value | PrototypePollution.js:17:17:19:5 | {\\n ... K\\n } | Prototype pollution caused by merging a user-controlled value from $@. | PrototypePollution.js:15:14:15:28 | req.query.value | here |
|
||||
| src-vulnerable-lodash/tst.js:7:17:7:29 | req.query.foo | src-vulnerable-lodash/tst.js:7:17:7:29 | req.query.foo | src-vulnerable-lodash/tst.js:7:17:7:29 | req.query.foo | Prototype pollution caused by merging a user-controlled value from $@ using a vulnerable version of $@. | src-vulnerable-lodash/tst.js:7:17:7:29 | req.query.foo | here | src-vulnerable-lodash/package.json:3:19:3:26 | "4.17.4" | lodash |
|
||||
| src-vulnerable-lodash/tst.js:10:17:12:5 | {\\n ... K\\n } | src-vulnerable-lodash/tst.js:11:16:11:30 | req.query.value | src-vulnerable-lodash/tst.js:10:17:12:5 | {\\n ... K\\n } | Prototype pollution caused by merging a user-controlled value from $@ using a vulnerable version of $@. | src-vulnerable-lodash/tst.js:11:16:11:30 | req.query.value | here | src-vulnerable-lodash/package.json:3:19:3:26 | "4.17.4" | lodash |
|
||||
| src-vulnerable-lodash/tst.js:17:17:19:5 | {\\n ... K\\n } | src-vulnerable-lodash/tst.js:15:14:15:28 | req.query.value | src-vulnerable-lodash/tst.js:17:17:19:5 | {\\n ... K\\n } | Prototype pollution caused by merging a user-controlled value from $@ using a vulnerable version of $@. | src-vulnerable-lodash/tst.js:15:14:15:28 | req.query.value | here | src-vulnerable-lodash/package.json:3:19:3:26 | "4.17.4" | lodash |
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"lodash": "4.17.12"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
let express = require('express');
|
||||
let _ = require('lodash');
|
||||
|
||||
let app = express();
|
||||
|
||||
app.get('/hello', function(req, res) {
|
||||
_.merge({}, req.query.foo); // OK
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"lodash": "4.17.4"
|
||||
}
|
||||
}
|
||||
@@ -6,11 +6,11 @@ let app = express();
|
||||
app.get('/hello', function(req, res) {
|
||||
_.merge({}, req.query.foo); // NOT OK
|
||||
_.merge({}, req.query); // NOT OK - but not flagged
|
||||
|
||||
|
||||
_.merge({}, {
|
||||
value: req.query.value // NOT OK
|
||||
});
|
||||
|
||||
|
||||
let opts = {
|
||||
thing: req.query.value // wrapped and unwrapped value
|
||||
};
|
||||
@@ -18,4 +18,3 @@ app.get('/hello', function(req, res) {
|
||||
value: opts.thing // NOT OK
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user