mirror of
https://github.com/github/codeql.git
synced 2026-04-26 01:05:15 +02:00
Merge branch 'main' into js/shared-dataflow
This commit is contained in:
1
javascript/ql/integration-tests/legacy
Normal file
1
javascript/ql/integration-tests/legacy
Normal file
@@ -0,0 +1 @@
|
||||
These tests are still run with the legacy test runner
|
||||
@@ -1,3 +1,25 @@
|
||||
## 1.0.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.0.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* CodeQL package management is now generally available, and all GitHub-produced CodeQL packages have had their version numbers increased to 1.0.0.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Additional heuristics for a new sensitive data classification for private information (e.g. credit card numbers) have been added to the shared `SensitiveDataHeuristics.qll` library. This may result in additional results for queries that use sensitive data such as `js/clear-text-storage-sensitive-data` and `js/clear-text-logging`.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fixed a bug where very large TypeScript files would cause database creation to crash. Large files over 10MB were already excluded from analysis, but the file size check was not applied to TypeScript files.
|
||||
|
||||
## 0.9.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.9.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Enabled type-tracking to follow content through array methods
|
||||
* Improved modeling of `Array.prototype.splice` for when it is called with more than two arguments
|
||||
3
javascript/ql/lib/change-notes/released/0.9.1.md
Normal file
3
javascript/ql/lib/change-notes/released/0.9.1.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.9.1
|
||||
|
||||
No user-facing changes.
|
||||
13
javascript/ql/lib/change-notes/released/1.0.0.md
Normal file
13
javascript/ql/lib/change-notes/released/1.0.0.md
Normal file
@@ -0,0 +1,13 @@
|
||||
## 1.0.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* CodeQL package management is now generally available, and all GitHub-produced CodeQL packages have had their version numbers increased to 1.0.0.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Additional heuristics for a new sensitive data classification for private information (e.g. credit card numbers) have been added to the shared `SensitiveDataHeuristics.qll` library. This may result in additional results for queries that use sensitive data such as `js/clear-text-storage-sensitive-data` and `js/clear-text-logging`.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fixed a bug where very large TypeScript files would cause database creation to crash. Large files over 10MB were already excluded from analysis, but the file size check was not applied to TypeScript files.
|
||||
3
javascript/ql/lib/change-notes/released/1.0.1.md
Normal file
3
javascript/ql/lib/change-notes/released/1.0.1.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 1.0.1
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.9.0
|
||||
lastReleaseVersion: 1.0.1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/javascript-all
|
||||
version: 0.9.1-dev
|
||||
version: 1.0.2-dev
|
||||
groups: javascript
|
||||
dbscheme: semmlecode.javascript.dbscheme
|
||||
extractor: javascript
|
||||
|
||||
@@ -77,8 +77,12 @@ module ArrayTaintTracking {
|
||||
succ = call.getReceiver().getALocalSource() and
|
||||
call.getCalleeName() = ["push", "unshift"]
|
||||
or
|
||||
// `array.splice(i, del, e)`: if `e` is tainted, then so is `array`.
|
||||
pred = call.getArgument(2) and
|
||||
// `array.splice(i, del, e1, e2, ...)`: if any item is tainted, then so is `array`.
|
||||
pred = call.getArgument(any(int i | i >= 2)) and
|
||||
succ.(DataFlow::SourceNode).getAMethodCall("splice") = call
|
||||
or
|
||||
// `array.splice(i, del, ...e)`: if `e` is tainted, then so is `array`.
|
||||
pred = call.getASpreadArgument() and
|
||||
succ.(DataFlow::SourceNode).getAMethodCall("splice") = call
|
||||
or
|
||||
// `e = array.pop()`, `e = array.shift()`, or similar: if `array` is tainted, then so is `e`.
|
||||
@@ -115,9 +119,9 @@ private module ArrayDataFlow {
|
||||
* A step modeling the creation of an Array using the `Array.from(x)` method.
|
||||
* The step copies the elements of the argument (set, array, or iterator elements) into the resulting array.
|
||||
*/
|
||||
private class ArrayFrom extends DataFlow::LegacyFlowStep {
|
||||
private class ArrayFrom extends LegacyPreCallGraphStep {
|
||||
override predicate loadStoreStep(
|
||||
DataFlow::Node pred, DataFlow::Node succ, string fromProp, string toProp
|
||||
DataFlow::Node pred, DataFlow::SourceNode succ, string fromProp, string toProp
|
||||
) {
|
||||
exists(DataFlow::CallNode call |
|
||||
call = arrayFromCall() and
|
||||
@@ -135,9 +139,9 @@ private module ArrayDataFlow {
|
||||
*
|
||||
* Such a step can occur both with the `push` and `unshift` methods, or when creating a new array.
|
||||
*/
|
||||
private class ArrayCopySpread extends DataFlow::LegacyFlowStep {
|
||||
private class ArrayCopySpread extends LegacyPreCallGraphStep {
|
||||
override predicate loadStoreStep(
|
||||
DataFlow::Node pred, DataFlow::Node succ, string fromProp, string toProp
|
||||
DataFlow::Node pred, DataFlow::SourceNode succ, string fromProp, string toProp
|
||||
) {
|
||||
fromProp = arrayLikeElement() and
|
||||
toProp = arrayElement() and
|
||||
@@ -156,7 +160,7 @@ private module ArrayDataFlow {
|
||||
/**
|
||||
* A step for storing an element on an array using `arr.push(e)` or `arr.unshift(e)`.
|
||||
*/
|
||||
private class ArrayAppendStep extends DataFlow::LegacyFlowStep {
|
||||
private class ArrayAppendStep extends LegacyPreCallGraphStep {
|
||||
override predicate storeStep(DataFlow::Node element, DataFlow::SourceNode obj, string prop) {
|
||||
prop = arrayElement() and
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
@@ -187,7 +191,7 @@ private module ArrayDataFlow {
|
||||
* A step for reading/writing an element from an array inside a for-loop.
|
||||
* E.g. a read from `foo[i]` to `bar` in `for(var i = 0; i < arr.length; i++) {bar = foo[i]}`.
|
||||
*/
|
||||
private class ArrayIndexingStep extends DataFlow::LegacyFlowStep {
|
||||
private class ArrayIndexingStep extends LegacyPreCallGraphStep {
|
||||
override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) {
|
||||
exists(ArrayIndexingAccess access |
|
||||
prop = arrayElement() and
|
||||
@@ -209,7 +213,7 @@ private module ArrayDataFlow {
|
||||
* A step for retrieving an element from an array using `.pop()`, `.shift()`, or `.at()`.
|
||||
* E.g. `array.pop()`.
|
||||
*/
|
||||
private class ArrayPopStep extends DataFlow::LegacyFlowStep {
|
||||
private class ArrayPopStep extends LegacyPreCallGraphStep {
|
||||
override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) {
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
call.getMethodName() = ["pop", "shift", "at"] and
|
||||
@@ -274,25 +278,38 @@ private module ArrayDataFlow {
|
||||
|
||||
/**
|
||||
* A step modeling that `splice` can insert elements into an array.
|
||||
* For example in `array.splice(i, del, e)`: if `e` is tainted, then so is `array
|
||||
* For example in `array.splice(i, del, e1, e2, ...)`: if any item is tainted, then so is `array`
|
||||
*/
|
||||
private class ArraySpliceStep extends DataFlow::LegacyFlowStep {
|
||||
private class ArraySpliceStep extends LegacyPreCallGraphStep {
|
||||
override predicate storeStep(DataFlow::Node element, DataFlow::SourceNode obj, string prop) {
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
call.getMethodName() = "splice" and
|
||||
prop = arrayElement() and
|
||||
element = call.getArgument(2) and
|
||||
element = call.getArgument(any(int i | i >= 2)) and
|
||||
call = obj.getAMethodCall()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate loadStoreStep(
|
||||
DataFlow::Node pred, DataFlow::SourceNode succ, string fromProp, string toProp
|
||||
) {
|
||||
fromProp = arrayLikeElement() and
|
||||
toProp = arrayElement() and
|
||||
// `array.splice(i, del, ...arr)` variant
|
||||
exists(DataFlow::MethodCallNode mcn |
|
||||
mcn.getMethodName() = "splice" and
|
||||
pred = mcn.getASpreadArgument() and
|
||||
succ = mcn.getReceiver().getALocalSource()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A step for modeling `concat`.
|
||||
* For example in `e = arr1.concat(arr2, arr3)`: if any of the `arr` is tainted, then so is `e`.
|
||||
*/
|
||||
private class ArrayConcatStep extends DataFlow::LegacyFlowStep {
|
||||
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
private class ArrayConcatStep extends LegacyPreCallGraphStep {
|
||||
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
call.getMethodName() = "concat" and
|
||||
prop = arrayElement() and
|
||||
@@ -305,8 +322,8 @@ private module ArrayDataFlow {
|
||||
/**
|
||||
* A step for modeling that elements from an array `arr` also appear in the result from calling `slice`/`splice`/`filter`.
|
||||
*/
|
||||
private class ArraySliceStep extends DataFlow::LegacyFlowStep {
|
||||
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
private class ArraySliceStep extends LegacyPreCallGraphStep {
|
||||
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
call.getMethodName() = ["slice", "splice", "filter"] and
|
||||
prop = arrayElement() and
|
||||
@@ -319,7 +336,7 @@ private module ArrayDataFlow {
|
||||
/**
|
||||
* A step modeling that elements from an array `arr` are received by calling `find`.
|
||||
*/
|
||||
private class ArrayFindStep extends DataFlow::LegacyFlowStep {
|
||||
private class ArrayFindStep extends LegacyPreCallGraphStep {
|
||||
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
exists(DataFlow::CallNode call |
|
||||
call = arrayFindCall(pred) and
|
||||
@@ -382,7 +399,7 @@ private module ArrayLibraries {
|
||||
* 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
|
||||
result = DataFlow::moduleImport(["array-union", "array-uniq", "uniq"]).getACall() and
|
||||
array = result.getAnArgument()
|
||||
}
|
||||
|
||||
@@ -401,8 +418,8 @@ private module ArrayLibraries {
|
||||
/**
|
||||
* A loadStoreStep for a library that copies the elements of an array into another array.
|
||||
*/
|
||||
private class ArrayCopyLoadStore extends DataFlow::LegacyFlowStep {
|
||||
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
private class ArrayCopyLoadStore extends LegacyPreCallGraphStep {
|
||||
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
|
||||
exists(DataFlow::CallNode call |
|
||||
call = arrayCopyCall(pred) and
|
||||
succ = call and
|
||||
|
||||
@@ -63,6 +63,12 @@ private module Cached {
|
||||
TSynthCaptureNode(VariableCapture::VariableCaptureOutput::SynthesizedCaptureNode node) or
|
||||
TGenericSynthesizedNode(AstNode node, string tag, DataFlowPrivate::DataFlowCallable container) {
|
||||
any(AdditionalFlowInternal flow).needsSynthesizedNode(node, tag, container)
|
||||
} or
|
||||
TForbiddenRecursionGuard() {
|
||||
none() and
|
||||
// We want to prune irrelevant models before materialising data flow nodes, so types contributed
|
||||
// directly from CodeQL must expose their pruning info without depending on data flow nodes.
|
||||
(any(ModelInput::TypeModel tm).isTypeUsed("") implies any())
|
||||
}
|
||||
|
||||
cached
|
||||
|
||||
@@ -1003,7 +1003,7 @@ module NodeJSLib {
|
||||
exists(ClientRequestLoginCallback callback | this = callback.getACall().getArgument(0))
|
||||
}
|
||||
|
||||
override string getCredentialsKind() { result = "Node.js http(s) client login username" }
|
||||
override string getCredentialsKind() { result = "user name" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1014,7 +1014,7 @@ module NodeJSLib {
|
||||
exists(ClientRequestLoginCallback callback | this = callback.getACall().getArgument(1))
|
||||
}
|
||||
|
||||
override string getCredentialsKind() { result = "Node.js http(s) client login password" }
|
||||
override string getCredentialsKind() { result = "password" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,15 +1,29 @@
|
||||
/**
|
||||
* Models the `shelljs` library in terms of `FileSystemAccess` and `SystemCommandExecution`.
|
||||
*
|
||||
* https://www.npmjs.com/package/shelljs
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
module ShellJS {
|
||||
private API::Node shellJSMember() {
|
||||
result = API::moduleImport("shelljs")
|
||||
or
|
||||
result =
|
||||
shellJSMember()
|
||||
.getMember([
|
||||
"exec", "cd", "cp", "touch", "chmod", "pushd", "find", "ls", "ln", "mkdir", "mv",
|
||||
"rm", "cat", "head", "sort", "tail", "uniq", "grep", "sed", "to", "toEnd", "echo"
|
||||
])
|
||||
.getReturn()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an import of the `shelljs` or `async-shelljs` module.
|
||||
* Gets a function that can execute a shell command using the `shelljs` or `async-shelljs` modules.
|
||||
*/
|
||||
DataFlow::SourceNode shelljs() {
|
||||
result = DataFlow::moduleImport("shelljs") or
|
||||
result = shellJSMember().asSource() or
|
||||
result = DataFlow::moduleImport("async-shelljs")
|
||||
}
|
||||
|
||||
@@ -39,7 +53,10 @@ module ShellJS {
|
||||
|
||||
/** The `shelljs.exec` library modeled as a `shelljs` member. */
|
||||
private class ShellJsExec extends Range {
|
||||
ShellJsExec() { this = DataFlow::moduleImport("shelljs.exec") }
|
||||
ShellJsExec() {
|
||||
this = DataFlow::moduleImport("shelljs.exec") or
|
||||
this = shellJSMember().getMember("exec").asSource()
|
||||
}
|
||||
|
||||
override string getName() { result = "exec" }
|
||||
}
|
||||
|
||||
@@ -168,9 +168,20 @@ module ModelInput {
|
||||
* A unit class for adding additional type model rows from CodeQL models.
|
||||
*/
|
||||
class TypeModel extends Unit {
|
||||
/**
|
||||
* Holds if any of the other predicates in this class might have a result
|
||||
* for the given `type`.
|
||||
*
|
||||
* The implementation of this predicate should not depend on `DataFlow::Node`.
|
||||
*/
|
||||
bindingset[type]
|
||||
predicate isTypeUsed(string type) { none() }
|
||||
|
||||
/**
|
||||
* Gets a data-flow node that is a source of the given `type`.
|
||||
*
|
||||
* Note that `type` should also be included in `isTypeUsed`.
|
||||
*
|
||||
* This must not depend on API graphs, but ensures that an API node is generated for
|
||||
* the source.
|
||||
*/
|
||||
@@ -180,6 +191,8 @@ module ModelInput {
|
||||
* Gets a data-flow node that is a sink of the given `type`,
|
||||
* usually because it is an argument passed to a parameter of that type.
|
||||
*
|
||||
* Note that `type` should also be included in `isTypeUsed`.
|
||||
*
|
||||
* This must not depend on API graphs, but ensures that an API node is generated for
|
||||
* the sink.
|
||||
*/
|
||||
@@ -188,6 +201,8 @@ module ModelInput {
|
||||
/**
|
||||
* Gets an API node that is a source or sink of the given `type`.
|
||||
*
|
||||
* Note that `type` should also be included in `isTypeUsed`.
|
||||
*
|
||||
* Unlike `getASource` and `getASink`, this may depend on API graphs.
|
||||
*/
|
||||
API::Node getAnApiNode(string type) { none() }
|
||||
@@ -354,6 +369,28 @@ private predicate typeVariableModel(string name, string path) {
|
||||
Extensions::typeVariableModel(name, path)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given extension tuple `madId` should pretty-print as `model`.
|
||||
*
|
||||
* This predicate should only be used in tests.
|
||||
*/
|
||||
predicate interpretModelForTest(QlBuiltins::ExtensionId madId, string model) {
|
||||
exists(string type, string path, string kind |
|
||||
Extensions::sourceModel(type, path, kind, madId) and
|
||||
model = "Source: " + type + "; " + path + "; " + kind
|
||||
)
|
||||
or
|
||||
exists(string type, string path, string kind |
|
||||
Extensions::sinkModel(type, path, kind, madId) and
|
||||
model = "Sink: " + type + "; " + path + "; " + kind
|
||||
)
|
||||
or
|
||||
exists(string type, string path, string input, string output, string kind |
|
||||
Extensions::summaryModel(type, path, input, output, kind, madId) and
|
||||
model = "Summary: " + type + "; " + path + "; " + input + "; " + output + "; " + kind
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if rows involving `type` might be relevant for the analysis of this database.
|
||||
*/
|
||||
@@ -367,6 +404,8 @@ predicate isRelevantType(string type) {
|
||||
(
|
||||
Specific::isTypeUsed(type)
|
||||
or
|
||||
any(TypeModel model).isTypeUsed(type)
|
||||
or
|
||||
exists(TestAllModels t)
|
||||
)
|
||||
or
|
||||
|
||||
@@ -14,13 +14,14 @@
|
||||
* - id: a user name or other account information;
|
||||
* - password: a password or authorization key;
|
||||
* - certificate: a certificate.
|
||||
* - private: private data such as credit card numbers
|
||||
*
|
||||
* While classifications are represented as strings, this should not be relied upon.
|
||||
* Instead, use the predicates in `SensitiveDataClassification::` to work with
|
||||
* classifications.
|
||||
*/
|
||||
class SensitiveDataClassification extends string {
|
||||
SensitiveDataClassification() { this in ["secret", "id", "password", "certificate"] }
|
||||
SensitiveDataClassification() { this in ["secret", "id", "password", "certificate", "private"] }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,6 +39,9 @@ module SensitiveDataClassification {
|
||||
|
||||
/** Gets the classification for certificates. */
|
||||
SensitiveDataClassification certificate() { result = "certificate" }
|
||||
|
||||
/** Gets the classification for private data. */
|
||||
SensitiveDataClassification private() { result = "private" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,6 +81,40 @@ module HeuristicNames {
|
||||
*/
|
||||
string maybeCertificate() { result = "(?is).*(cert)(?!.*(format|name|ification)).*" }
|
||||
|
||||
/**
|
||||
* Gets a regular expression that identifies strings that may indicate the presence of
|
||||
* private data.
|
||||
*/
|
||||
string maybePrivate() {
|
||||
result =
|
||||
"(?is).*(" +
|
||||
// Inspired by the list on https://cwe.mitre.org/data/definitions/359.html
|
||||
// Government identifiers, such as Social Security Numbers
|
||||
"social.?security|employer.?identification|national.?insurance|resident.?id|" +
|
||||
"passport.?(num|no)|([_-]|\\b)ssn([_-]|\\b)|" +
|
||||
// Contact information, such as home addresses
|
||||
"post.?code|zip.?code|home.?addr|" +
|
||||
// and telephone numbers
|
||||
"(mob(ile)?|home).?(num|no|tel|phone)|(tel|fax|phone).?(num|no)|telephone|" +
|
||||
"emergency.?contact|" +
|
||||
// Geographic location - where the user is (or was)
|
||||
"latitude|longitude|nationality|" +
|
||||
// Financial data - such as credit card numbers, salary, bank accounts, and debts
|
||||
"(credit|debit|bank|visa).?(card|num|no|acc(ou)?nt)|acc(ou)?nt.?(no|num|credit)|" +
|
||||
"salary|billing|credit.?(rating|score)|([_-]|\\b)ccn([_-]|\\b)|" +
|
||||
// Communications - e-mail addresses, private e-mail messages, SMS text messages, chat logs, etc.
|
||||
// "e(mail|_mail)|" + // this seems too noisy
|
||||
// Health - medical conditions, insurance status, prescription records
|
||||
"birth.?da(te|y)|da(te|y).?(of.?)?birth|" +
|
||||
"medical|(health|care).?plan|healthkit|appointment|prescription|" +
|
||||
"blood.?(type|alcohol|glucose|pressure)|heart.?(rate|rhythm)|body.?(mass|fat)|" +
|
||||
"menstrua|pregnan|insulin|inhaler|" +
|
||||
// Relationships - work and family
|
||||
"employ(er|ee)|spouse|maiden.?name" +
|
||||
// ---
|
||||
").*"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a regular expression that identifies strings that may indicate the presence
|
||||
* of sensitive data, with `classification` describing the kind of sensitive data involved.
|
||||
@@ -90,6 +128,9 @@ module HeuristicNames {
|
||||
or
|
||||
result = maybeCertificate() and
|
||||
classification = SensitiveDataClassification::certificate()
|
||||
or
|
||||
result = maybePrivate() and
|
||||
classification = SensitiveDataClassification::private()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
description: Removed unused column from the `folders` and `files` relations
|
||||
compatibility: full
|
||||
files.rel: reorder files.rel (int id, string name, string simple, string ext, int fromSource) id name
|
||||
folders.rel: reorder folders.rel (int id, string name, string simple) id name
|
||||
files.rel: reorder files.rel (@file id, string name, string simple, string ext, int fromSource) id name
|
||||
folders.rel: reorder folders.rel (@folder id, string name, string simple) id name
|
||||
@@ -1,94 +1,94 @@
|
||||
description: Rename multiple relation names.
|
||||
compatibility: backwards
|
||||
is_externs.rel: reorder isExterns.rel(int toplevel) toplevel
|
||||
is_externs.rel: reorder isExterns.rel(@toplevel toplevel) toplevel
|
||||
isExterns.rel: delete
|
||||
is_module.rel: reorder isModule.rel(int toplevel) toplevel
|
||||
is_module.rel: reorder isModule.rel(@toplevel toplevel) toplevel
|
||||
isModule.rel: delete
|
||||
is_nodejs.rel: reorder isNodejs.rel(int toplevel) toplevel
|
||||
is_nodejs.rel: reorder isNodejs.rel(@toplevel toplevel) toplevel
|
||||
isNodejs.rel: delete
|
||||
is_es2015_module.rel: reorder isES2015Module.rel(int toplevel) toplevel
|
||||
is_es2015_module.rel: reorder isES2015Module.rel(@toplevel toplevel) toplevel
|
||||
isES2015Module.rel: delete
|
||||
is_closure_module.rel: reorder isClosureModule.rel(int toplevel) toplevel
|
||||
is_closure_module.rel: reorder isClosureModule.rel(@toplevel toplevel) toplevel
|
||||
isClosureModule.rel: delete
|
||||
stmt_containers.rel: reorder stmtContainers.rel(int stmt, int container) stmt container
|
||||
stmt_containers.rel: reorder stmtContainers.rel(@stmt stmt, @stmt_container container) stmt container
|
||||
stmtContainers.rel: delete
|
||||
jump_targets.rel: reorder jumpTargets.rel(int jump, int target) jump target
|
||||
jump_targets.rel: reorder jumpTargets.rel(@stmt jump, @stmt target) jump target
|
||||
jumpTargets.rel: delete
|
||||
is_instantiated.rel: reorder isInstantiated.rel(int decl) decl
|
||||
is_instantiated.rel: reorder isInstantiated.rel(@namespacedeclaration decl) decl
|
||||
isInstantiated.rel: delete
|
||||
has_declare_keyword.rel: reorder hasDeclareKeyword.rel(int stmt) stmt
|
||||
has_declare_keyword.rel: reorder hasDeclareKeyword.rel(@declarablenode stmt) stmt
|
||||
hasDeclareKeyword.rel: delete
|
||||
is_for_await_of.rel: reorder isForAwaitOf.rel(int forof) forof
|
||||
is_for_await_of.rel: reorder isForAwaitOf.rel(@forofstmt forof) forof
|
||||
isForAwaitOf.rel: delete
|
||||
enclosing_stmt.rel: reorder enclosingStmt.rel(int expr, int stmt) expr stmt
|
||||
enclosing_stmt.rel: reorder enclosingStmt.rel(@exprortype expr, @stmt stmt) expr stmt
|
||||
enclosingStmt.rel: delete
|
||||
expr_containers.rel: reorder exprContainers.rel(int expr, int container) expr container
|
||||
expr_containers.rel: reorder exprContainers.rel(@exprortype expr, @stmt_container container) expr container
|
||||
exprContainers.rel: delete
|
||||
array_size.rel: reorder arraySize.rel(int ae, int sz) ae sz
|
||||
array_size.rel: reorder arraySize.rel(@arraylike ae, int sz) ae sz
|
||||
arraySize.rel: delete
|
||||
is_delegating.rel: reorder isDelegating.rel(int yield) yield
|
||||
is_delegating.rel: reorder isDelegating.rel(@yieldexpr yield) yield
|
||||
isDelegating.rel: delete
|
||||
is_arguments_object.rel: reorder isArgumentsObject.rel(int id) id
|
||||
is_arguments_object.rel: reorder isArgumentsObject.rel(@variable id) id
|
||||
isArgumentsObject.rel: delete
|
||||
is_computed.rel: reorder isComputed.rel(int prop) prop
|
||||
is_computed.rel: reorder isComputed.rel(@property prop) prop
|
||||
isComputed.rel: delete
|
||||
is_method.rel: reorder isMethod.rel(int prop) prop
|
||||
is_method.rel: reorder isMethod.rel(@property prop) prop
|
||||
isMethod.rel: delete
|
||||
is_static.rel: reorder isStatic.rel(int prop) prop
|
||||
is_static.rel: reorder isStatic.rel(@property prop) prop
|
||||
isStatic.rel: delete
|
||||
is_abstract_member.rel: reorder isAbstractMember.rel(int prop) prop
|
||||
is_abstract_member.rel: reorder isAbstractMember.rel(@property prop) prop
|
||||
isAbstractMember.rel: delete
|
||||
is_const_enum.rel: reorder isConstEnum.rel(int id) id
|
||||
is_const_enum.rel: reorder isConstEnum.rel(@enumdeclaration id) id
|
||||
isConstEnum.rel: delete
|
||||
is_abstract_class.rel: reorder isAbstractClass.rel(int id) id
|
||||
is_abstract_class.rel: reorder isAbstractClass.rel(@classdeclstmt id) id
|
||||
isAbstractClass.rel: delete
|
||||
has_public_keyword.rel: reorder hasPublicKeyword.rel(int prop) prop
|
||||
has_public_keyword.rel: reorder hasPublicKeyword.rel(@property prop) prop
|
||||
hasPublicKeyword.rel: delete
|
||||
has_private_keyword.rel: reorder hasPrivateKeyword.rel(int prop) prop
|
||||
has_private_keyword.rel: reorder hasPrivateKeyword.rel(@property prop) prop
|
||||
hasPrivateKeyword.rel: delete
|
||||
has_protected_keyword.rel: reorder hasProtectedKeyword.rel(int prop) prop
|
||||
has_protected_keyword.rel: reorder hasProtectedKeyword.rel(@property prop) prop
|
||||
hasProtectedKeyword.rel: delete
|
||||
has_readonly_keyword.rel: reorder hasReadonlyKeyword.rel(int prop) prop
|
||||
has_readonly_keyword.rel: reorder hasReadonlyKeyword.rel(@property prop) prop
|
||||
hasReadonlyKeyword.rel: delete
|
||||
has_type_keyword.rel: reorder hasTypeKeyword.rel(int id) id
|
||||
has_type_keyword.rel: reorder hasTypeKeyword.rel(@import_or_export_declaration id) id
|
||||
hasTypeKeyword.rel: delete
|
||||
is_optional_member.rel: reorder isOptionalMember.rel(int id) id
|
||||
is_optional_member.rel: reorder isOptionalMember.rel(@property id) id
|
||||
isOptionalMember.rel: delete
|
||||
has_definite_assignment_assertion.rel: reorder hasDefiniteAssignmentAssertion.rel(int id) id
|
||||
has_definite_assignment_assertion.rel: reorder hasDefiniteAssignmentAssertion.rel(@field_or_vardeclarator id) id
|
||||
hasDefiniteAssignmentAssertion.rel: delete
|
||||
is_optional_parameter_declaration.rel: reorder isOptionalParameterDeclaration.rel(int parameter) parameter
|
||||
is_optional_parameter_declaration.rel: reorder isOptionalParameterDeclaration.rel(@pattern parameter) parameter
|
||||
isOptionalParameterDeclaration.rel: delete
|
||||
has_asserts_keyword.rel: reorder hasAssertsKeyword.rel(int node) node
|
||||
has_asserts_keyword.rel: reorder hasAssertsKeyword.rel(@predicatetypeexpr node) node
|
||||
hasAssertsKeyword.rel: delete
|
||||
js_parse_errors.rel: reorder jsParseErrors.rel(int id, int toplevel, string message, string line) id toplevel message line
|
||||
js_parse_errors.rel: reorder jsParseErrors.rel(@js_parse_error id, @toplevel toplevel, string message, string line) id toplevel message line
|
||||
jsParseErrors.rel: delete
|
||||
regexp_parse_errors.rel: reorder regexpParseErrors.rel(int id, int regexp, string message) id regexp message
|
||||
regexp_parse_errors.rel: reorder regexpParseErrors.rel(@regexp_parse_error id, @regexpterm regexp, string message) id regexp message
|
||||
regexpParseErrors.rel: delete
|
||||
is_greedy.rel: reorder isGreedy.rel(int id) id
|
||||
is_greedy.rel: reorder isGreedy.rel(@regexp_quantifier id) id
|
||||
isGreedy.rel: delete
|
||||
range_quantifier_lower_bound.rel: reorder rangeQuantifierLowerBound.rel(int id, int lo) id lo
|
||||
range_quantifier_lower_bound.rel: reorder rangeQuantifierLowerBound.rel(@regexp_range id, int lo) id lo
|
||||
rangeQuantifierLowerBound.rel: delete
|
||||
range_quantifier_upper_bound.rel: reorder rangeQuantifierUpperBound.rel(int id, int hi) id hi
|
||||
range_quantifier_upper_bound.rel: reorder rangeQuantifierUpperBound.rel(@regexp_range id, int hi) id hi
|
||||
rangeQuantifierUpperBound.rel: delete
|
||||
is_capture.rel: reorder isCapture.rel(int id, int number) id number
|
||||
is_capture.rel: reorder isCapture.rel(@regexp_group id, int number) id number
|
||||
isCapture.rel: delete
|
||||
is_named_capture.rel: reorder isNamedCapture.rel(int id, string name) id name
|
||||
is_named_capture.rel: reorder isNamedCapture.rel(@regexp_group id, string name) id name
|
||||
isNamedCapture.rel: delete
|
||||
is_inverted.rel: reorder isInverted.rel(int id) id
|
||||
is_inverted.rel: reorder isInverted.rel(@regexp_char_class id) id
|
||||
isInverted.rel: delete
|
||||
regexp_const_value.rel: reorder regexpConstValue.rel(int id, string value) id value
|
||||
regexp_const_value.rel: reorder regexpConstValue.rel(@regexp_constant id, string value) id value
|
||||
regexpConstValue.rel: delete
|
||||
char_class_escape.rel: reorder charClassEscape.rel(int id, string value) id value
|
||||
char_class_escape.rel: reorder charClassEscape.rel(@regexp_char_class_escape id, string value) id value
|
||||
charClassEscape.rel: delete
|
||||
named_backref.rel: reorder namedBackref.rel(int id, string name) id name
|
||||
named_backref.rel: reorder namedBackref.rel(@regexp_backref id, string name) id name
|
||||
namedBackref.rel: delete
|
||||
unicode_property_escapename.rel: reorder unicodePropertyEscapeName.rel(int id, string name) id name
|
||||
unicode_property_escapename.rel: reorder unicodePropertyEscapeName.rel(@regexp_unicode_property_escape id, string name) id name
|
||||
unicodePropertyEscapeName.rel: delete
|
||||
unicode_property_escapevalue.rel: reorder unicodePropertyEscapeValue.rel(int id, string value) id value
|
||||
unicode_property_escapevalue.rel: reorder unicodePropertyEscapeValue.rel(@regexp_unicode_property_escape id, string value) id value
|
||||
unicodePropertyEscapeValue.rel: delete
|
||||
is_generator.rel: reorder isGenerator.rel(int fun) fun
|
||||
is_generator.rel: reorder isGenerator.rel(@function fun) fun
|
||||
isGenerator.rel: delete
|
||||
has_rest_parameter.rel: reorder hasRestParameter.rel(int fun) fun
|
||||
has_rest_parameter.rel: reorder hasRestParameter.rel(@function fun) fun
|
||||
hasRestParameter.rel: delete
|
||||
is_async.rel: reorder isAsync.rel(int fun) fun
|
||||
is_async.rel: reorder isAsync.rel(@function fun) fun
|
||||
isAsync.rel: delete
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
## 1.0.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.0.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* CodeQL package management is now generally available, and all GitHub-produced CodeQL packages have had their version numbers increased to 1.0.0.
|
||||
|
||||
## 0.8.16
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.8.15
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<p>
|
||||
|
||||
For JavaScript in the browser,
|
||||
<code>RandomSource.getRandomValues</code> provides a cryptographically
|
||||
<code>crypto.getRandomValues</code> provides a cryptographically
|
||||
secure pseudo-random number generator.
|
||||
|
||||
</p>
|
||||
@@ -69,7 +69,7 @@
|
||||
|
||||
<references>
|
||||
<li>Wikipedia: <a href="http://en.wikipedia.org/wiki/Pseudorandom_number_generator">Pseudo-random number generator</a>.</li>
|
||||
<li>Mozilla Developer Network: <a href="https://developer.mozilla.org/en-US/docs/Web/API/RandomSource/getRandomValues">RandomSource.getRandomValues</a>.</li>
|
||||
<li>Mozilla Developer Network: <a href="https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues">Crypto: getRandomValues()</a>.</li>
|
||||
<li>NodeJS: <a href="https://nodejs.org/api/crypto.html#crypto_crypto_randombytes_size_callback">crypto.randomBytes</a></li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -2,5 +2,7 @@ function securePassword() {
|
||||
// GOOD: the random suffix is cryptographically secure
|
||||
var suffix = window.crypto.getRandomValues(new Uint32Array(1))[0];
|
||||
var password = "myPassword" + suffix;
|
||||
return password;
|
||||
|
||||
// GOOD: if a random value between 0 and 1 is desired
|
||||
var secret = window.crypto.getRandomValues(new Uint32Array(1))[0] * Math.pow(2,-32);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
If possible, store configuration files including credential data separately from the source code,
|
||||
in a secure location with restricted access.
|
||||
</p>
|
||||
<p>
|
||||
If the credentials are a placeholder value, make sure the value is obviously a placeholder by
|
||||
using a name such as <code>"SampleToken"</code> or <code>"MyPassword"</code>.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
|
||||
@@ -30,7 +30,7 @@ where
|
||||
// exclude dummy passwords and templates
|
||||
not (
|
||||
sink.getNode().(Sink).(DefaultCredentialsSink).getKind() =
|
||||
["password", "credentials", "token"] and
|
||||
["password", "credentials", "token", "key"] and
|
||||
PasswordHeuristics::isDummyPassword(val)
|
||||
or
|
||||
sink.getNode().(Sink).getKind() = "authorization header" and
|
||||
|
||||
3
javascript/ql/src/change-notes/released/0.8.16.md
Normal file
3
javascript/ql/src/change-notes/released/0.8.16.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.8.16
|
||||
|
||||
No user-facing changes.
|
||||
5
javascript/ql/src/change-notes/released/1.0.0.md
Normal file
5
javascript/ql/src/change-notes/released/1.0.0.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## 1.0.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* CodeQL package management is now generally available, and all GitHub-produced CodeQL packages have had their version numbers increased to 1.0.0.
|
||||
3
javascript/ql/src/change-notes/released/1.0.1.md
Normal file
3
javascript/ql/src/change-notes/released/1.0.1.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 1.0.1
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.8.15
|
||||
lastReleaseVersion: 1.0.1
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
Directly evaluating user input (for example, an HTTP request parameter) as code without properly
|
||||
sanitizing the input first allows an attacker arbitrary code execution. This can occur when user
|
||||
input is treated as JavaScript, or passed to a framework which interprets it as an expression to be
|
||||
evaluated. Examples include AngularJS expressions or JQuery selectors.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Avoid including user input in any expression which may be dynamically evaluated. If user input must
|
||||
be included, use context-specific escaping before
|
||||
including it. It is important that the correct escaping is used for the type of evaluation that will
|
||||
occur.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
The following example shows part of the page URL being evaluated as JavaScript code on the server. This allows an
|
||||
attacker to provide JavaScript within the URL and send it to server. client side attacks need victim users interaction
|
||||
like clicking on a attacker provided URL.
|
||||
</p>
|
||||
|
||||
<sample src="examples/CodeInjection.js" />
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
OWASP:
|
||||
<a href="https://www.owasp.org/index.php/Code_Injection">Code Injection</a>.
|
||||
</li>
|
||||
<li>
|
||||
Wikipedia: <a href="https://en.wikipedia.org/wiki/Code_injection">Code Injection</a>.
|
||||
</li>
|
||||
<li>
|
||||
PortSwigger Research Blog:
|
||||
<a href="https://portswigger.net/research/server-side-template-injection">Server-Side Template Injection</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,6 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<include src="CodeInjection.inc.qhelp" />
|
||||
</qhelp>
|
||||
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* @name Code injection
|
||||
* @description Interpreting unsanitized user input as code allows a malicious user arbitrary
|
||||
* code execution.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 9.3
|
||||
* @precision high
|
||||
* @id js/code-injection-dynamic-import
|
||||
* @tags security
|
||||
* external/cwe/cwe-094
|
||||
* external/cwe/cwe-095
|
||||
* external/cwe/cwe-079
|
||||
* external/cwe/cwe-116
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import DataFlow
|
||||
import DataFlow::PathGraph
|
||||
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/** A non-first leaf in a string-concatenation. Seen as a sanitizer for dynamic import code injection. */
|
||||
class NonFirstStringConcatLeaf extends Sanitizer {
|
||||
NonFirstStringConcatLeaf() {
|
||||
exists(StringOps::ConcatenationRoot root |
|
||||
this = root.getALeaf() and
|
||||
not this = root.getFirstLeaf()
|
||||
)
|
||||
or
|
||||
exists(DataFlow::CallNode join |
|
||||
join = DataFlow::moduleMember("path", "join").getACall() and
|
||||
this = join.getArgument([1 .. join.getNumArgument() - 1])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The dynamic import expression input can be a `data:` URL which loads any module from that data
|
||||
*/
|
||||
class DynamicImport extends DataFlow::ExprNode {
|
||||
DynamicImport() { this = any(DynamicImportExpr e).getSource().flow() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The dynamic import expression input can be a `data:` URL which loads any module from that data
|
||||
*/
|
||||
class WorkerThreads extends DataFlow::Node {
|
||||
WorkerThreads() {
|
||||
this = API::moduleImport("node:worker_threads").getMember("Worker").getParameter(0).asSink()
|
||||
}
|
||||
}
|
||||
|
||||
class UrlConstructorLabel extends FlowLabel {
|
||||
UrlConstructorLabel() { this = "UrlConstructorLabel" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about code injection vulnerabilities.
|
||||
*/
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "CodeInjection" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof DynamicImport }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink, FlowLabel label) {
|
||||
sink instanceof WorkerThreads and label instanceof UrlConstructorLabel
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
override predicate isAdditionalFlowStep(
|
||||
DataFlow::Node pred, DataFlow::Node succ, FlowLabel predlbl, FlowLabel succlbl
|
||||
) {
|
||||
exists(DataFlow::NewNode newUrl | succ = newUrl |
|
||||
newUrl = DataFlow::globalVarRef("URL").getAnInstantiation() and
|
||||
pred = newUrl.getArgument(0)
|
||||
) and
|
||||
predlbl instanceof StandardFlowLabel and
|
||||
succlbl instanceof UrlConstructorLabel
|
||||
}
|
||||
}
|
||||
|
||||
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "This command line depends on a $@.", source.getNode(),
|
||||
"user-provided value"
|
||||
@@ -0,0 +1,14 @@
|
||||
const { Worker } = require('node:worker_threads');
|
||||
var app = require('express')();
|
||||
|
||||
app.post('/path', async function (req, res) {
|
||||
const payload = req.query.queryParameter // like: payload = 'data:text/javascript,console.log("hello!");//'
|
||||
const payloadURL = new URL(payload)
|
||||
new Worker(payloadURL);
|
||||
});
|
||||
|
||||
app.post('/path2', async function (req, res) {
|
||||
const payload = req.query.queryParameter // like: payload = 'data:text/javascript,console.log("hello!");//'
|
||||
await import(payload)
|
||||
});
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
|
||||
<p>
|
||||
Controlling the value of arbitrary environment variables from user-controllable data is not safe.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
|
||||
<p>
|
||||
Restrict this operation only to privileged users or only for some not important environment variables.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
|
||||
<p>
|
||||
The following example allows unauthorized users to assign a value to any environment variable.
|
||||
</p>
|
||||
|
||||
<sample src="examples/Bad_Value_And_Key_Assignment.js" />
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li> <a href="https://huntr.com/bounties/00ec6847-125b-43e9-9658-d3cace1751d6/">Admin account TakeOver in mintplex-labs/anything-llm</a></li>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* @name User controlled arbitrary environment variable injection
|
||||
* @description creating arbitrary environment variables from user controlled data is not secure
|
||||
* @kind path-problem
|
||||
* @id js/env-key-and-value-injection
|
||||
* @problem.severity error
|
||||
* @security-severity 7.5
|
||||
* @precision medium
|
||||
* @tags security
|
||||
* external/cwe/cwe-089
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/** A taint tracking configuration for unsafe environment injection. */
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "envInjection" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink = keyOfEnv() or
|
||||
sink = valueOfEnv()
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(DataFlow::InvokeNode ikn |
|
||||
ikn = DataFlow::globalVarRef("Object").getAMemberInvocation("keys")
|
||||
|
|
||||
pred = ikn.getArgument(0) and
|
||||
(
|
||||
succ = ikn.getAChainedMethodCall(["filter", "map"]) or
|
||||
succ = ikn or
|
||||
succ = ikn.getAChainedMethodCall("forEach").getABoundCallbackParameter(0, 0)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
DataFlow::Node keyOfEnv() {
|
||||
result =
|
||||
NodeJSLib::process().getAPropertyRead("env").getAPropertyWrite().getPropertyNameExpr().flow()
|
||||
}
|
||||
|
||||
DataFlow::Node valueOfEnv() {
|
||||
result = API::moduleImport("process").getMember("env").getAMember().asSink()
|
||||
}
|
||||
|
||||
private predicate readToProcessEnv(DataFlow::Node envKey, DataFlow::Node envValue) {
|
||||
exists(DataFlow::PropWrite env |
|
||||
env = NodeJSLib::process().getAPropertyRead("env").getAPropertyWrite()
|
||||
|
|
||||
envKey = env.getPropertyNameExpr().flow() and
|
||||
envValue = env.getRhs()
|
||||
)
|
||||
}
|
||||
|
||||
from
|
||||
Configuration cfgForValue, Configuration cfgForKey, DataFlow::PathNode source,
|
||||
DataFlow::PathNode envKey, DataFlow::PathNode envValue
|
||||
where
|
||||
cfgForValue.hasFlowPath(source, envKey) and
|
||||
envKey.getNode() = keyOfEnv() and
|
||||
cfgForKey.hasFlowPath(source, envValue) and
|
||||
envValue.getNode() = valueOfEnv() and
|
||||
readToProcessEnv(envKey.getNode(), envValue.getNode())
|
||||
select envKey.getNode(), source, envKey, "arbitrary environment variable assignment from this $@.",
|
||||
source.getNode(), "user controllable source"
|
||||
@@ -0,0 +1,36 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
|
||||
<p>
|
||||
Assigning Value to environment variables from user-controllable data is not safe.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
|
||||
<p>
|
||||
Restrict this operation only to privileged users or only for some not important environment variables.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
|
||||
<p>
|
||||
The following example allows unauthorized users to assign a value to a critical environment variable.
|
||||
</p>
|
||||
|
||||
<sample src="examples/Bad_Value_Assignment.js" />
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li><a href="https://huntr.com/bounties/00ec6847-125b-43e9-9658-d3cace1751d6/">Admin account TakeOver in mintplex-labs/anything-llm</a></li>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* @name User controlled environment variable value injection
|
||||
* @description assigning important environment variables from user controlled data is not secure
|
||||
* @kind path-problem
|
||||
* @id js/env-value-injection
|
||||
* @problem.severity error
|
||||
* @security-severity 7.5
|
||||
* @precision medium
|
||||
* @tags security
|
||||
* external/cwe/cwe-089
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/** A taint tracking configuration for unsafe environment injection. */
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "envInjection" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink = API::moduleImport("process").getMember("env").getAMember().asSink()
|
||||
}
|
||||
}
|
||||
|
||||
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "this environment variable assignment is $@.",
|
||||
source.getNode(), "user controllable"
|
||||
@@ -0,0 +1,8 @@
|
||||
const http = require('node:http');
|
||||
|
||||
http.createServer((req, res) => {
|
||||
const { EnvValue, EnvKey } = req.body;
|
||||
process.env[EnvKey] = EnvValue; // NOT OK
|
||||
|
||||
res.end('env has been injected!');
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
const http = require('node:http');
|
||||
|
||||
http.createServer((req, res) => {
|
||||
const { EnvValue } = req.body;
|
||||
process.env["A_Critical_Env"] = EnvValue; // NOT OK
|
||||
|
||||
res.end('env has been injected!');
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
const express = require('express')
|
||||
const app = express()
|
||||
const jwtJsonwebtoken = require('jsonwebtoken');
|
||||
const jwt_decode = require('jwt-decode');
|
||||
const jwt_simple = require('jwt-simple');
|
||||
const jose = require('jose')
|
||||
const port = 3000
|
||||
|
||||
function getSecret() {
|
||||
return "A Safe generated random key"
|
||||
}
|
||||
app.get('/jose', (req, res) => {
|
||||
const UserToken = req.headers.authorization;
|
||||
// BAD: no signature verification
|
||||
jose.decodeJwt(UserToken)
|
||||
})
|
||||
|
||||
app.get('/jwtDecode', (req, res) => {
|
||||
const UserToken = req.headers.authorization;
|
||||
// BAD: no signature verification
|
||||
jwt_decode(UserToken)
|
||||
})
|
||||
|
||||
app.get('/jwtSimple', (req, res) => {
|
||||
const UserToken = req.headers.authorization;
|
||||
// jwt.decode(token, key, noVerify, algorithm)
|
||||
// BAD: no signature verification
|
||||
jwt_simple.decode(UserToken, getSecret(), true);
|
||||
})
|
||||
|
||||
app.get('/jwtJsonwebtoken', (req, res) => {
|
||||
const UserToken = req.headers.authorization;
|
||||
// BAD: no signature verification
|
||||
jwtJsonwebtoken.decode(UserToken)
|
||||
})
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Example app listening on port ${port}`)
|
||||
})
|
||||
@@ -0,0 +1,56 @@
|
||||
const express = require('express')
|
||||
const app = express()
|
||||
const jwtJsonwebtoken = require('jsonwebtoken');
|
||||
const jwt_decode = require('jwt-decode');
|
||||
const jwt_simple = require('jwt-simple');
|
||||
const jose = require('jose')
|
||||
const port = 3000
|
||||
|
||||
function getSecret() {
|
||||
return "A Safe generated random key"
|
||||
}
|
||||
|
||||
app.get('/jose1', async (req, res) => {
|
||||
const UserToken = req.headers.authorization;
|
||||
// GOOD: with signature verification
|
||||
await jose.jwtVerify(UserToken, new TextEncoder().encode(getSecret()))
|
||||
})
|
||||
|
||||
app.get('/jose2', async (req, res) => {
|
||||
const UserToken = req.headers.authorization;
|
||||
// GOOD: first without signature verification then with signature verification for same UserToken
|
||||
jose.decodeJwt(UserToken)
|
||||
await jose.jwtVerify(UserToken, new TextEncoder().encode(getSecret()))
|
||||
})
|
||||
|
||||
app.get('/jwtSimple1', (req, res) => {
|
||||
const UserToken = req.headers.authorization;
|
||||
// GOOD: first without signature verification then with signature verification for same UserToken
|
||||
jwt_simple.decode(UserToken, getSecret(), false);
|
||||
jwt_simple.decode(UserToken, getSecret());
|
||||
})
|
||||
|
||||
app.get('/jwtSimple2', (req, res) => {
|
||||
const UserToken = req.headers.authorization;
|
||||
// GOOD: with signature verification
|
||||
jwt_simple.decode(UserToken, getSecret(), true);
|
||||
jwt_simple.decode(UserToken, getSecret());
|
||||
})
|
||||
|
||||
app.get('/jwtJsonwebtoken1', (req, res) => {
|
||||
const UserToken = req.headers.authorization;
|
||||
// GOOD: with signature verification
|
||||
jwtJsonwebtoken.verify(UserToken, getSecret())
|
||||
})
|
||||
|
||||
app.get('/jwtJsonwebtoken2', (req, res) => {
|
||||
const UserToken = req.headers.authorization;
|
||||
// GOOD: first without signature verification then with signature verification for same UserToken
|
||||
jwtJsonwebtoken.decode(UserToken)
|
||||
jwtJsonwebtoken.verify(UserToken, getSecret())
|
||||
})
|
||||
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Example app listening on port ${port}`)
|
||||
})
|
||||
54
javascript/ql/src/experimental/Security/CWE-347/JWT.qll
Normal file
54
javascript/ql/src/experimental/Security/CWE-347/JWT.qll
Normal file
@@ -0,0 +1,54 @@
|
||||
import javascript
|
||||
|
||||
DataFlow::Node unverifiedDecode() {
|
||||
result = API::moduleImport("jsonwebtoken").getMember("decode").getParameter(0).asSink()
|
||||
or
|
||||
exists(API::Node verify | verify = API::moduleImport("jsonwebtoken").getMember("verify") |
|
||||
verify
|
||||
.getParameter(2)
|
||||
.getMember("algorithms")
|
||||
.getUnknownMember()
|
||||
.asSink()
|
||||
.mayHaveStringValue("none") and
|
||||
result = verify.getParameter(0).asSink()
|
||||
)
|
||||
or
|
||||
// jwt-simple
|
||||
exists(API::Node n | n = API::moduleImport("jwt-simple").getMember("decode") |
|
||||
n.getParameter(2).asSink().asExpr() = any(BoolLiteral b | b.getBoolValue() = true) and
|
||||
result = n.getParameter(0).asSink()
|
||||
)
|
||||
or
|
||||
// jwt-decode
|
||||
result = API::moduleImport("jwt-decode").getParameter(0).asSink()
|
||||
or
|
||||
//jose
|
||||
result = API::moduleImport("jose").getMember("decodeJwt").getParameter(0).asSink()
|
||||
}
|
||||
|
||||
DataFlow::Node verifiedDecode() {
|
||||
exists(API::Node verify | verify = API::moduleImport("jsonwebtoken").getMember("verify") |
|
||||
(
|
||||
not verify
|
||||
.getParameter(2)
|
||||
.getMember("algorithms")
|
||||
.getUnknownMember()
|
||||
.asSink()
|
||||
.mayHaveStringValue("none") or
|
||||
not exists(verify.getParameter(2).getMember("algorithms"))
|
||||
) and
|
||||
result = verify.getParameter(0).asSink()
|
||||
)
|
||||
or
|
||||
// jwt-simple
|
||||
exists(API::Node n | n = API::moduleImport("jwt-simple").getMember("decode") |
|
||||
(
|
||||
n.getParameter(2).asSink().asExpr() = any(BoolLiteral b | b.getBoolValue() = false) or
|
||||
not exists(n.getParameter(2))
|
||||
) and
|
||||
result = n.getParameter(0).asSink()
|
||||
or
|
||||
//jose
|
||||
result = API::moduleImport("jose").getMember("jwtVerify").getParameter(0).asSink()
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
A JSON Web Token (JWT) is used for authenticating and managing users in an application.
|
||||
</p>
|
||||
<p>
|
||||
Only Decoding JWTs without checking if they have a valid signature or not can lead to security vulnerabilities.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>
|
||||
Don't use methods that only decode JWT, Instead use methods that verify the signature of JWT.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>
|
||||
In the following code, you can see the proper usage of the most popular JWT libraries.
|
||||
</p>
|
||||
<sample src="Examples/Good.js" />
|
||||
|
||||
<p>
|
||||
In the following code, you can see the improper usage of the most popular JWT libraries.
|
||||
</p>
|
||||
<sample src="Examples/Bad.js" />
|
||||
</example>
|
||||
<references>
|
||||
<li>
|
||||
<a href="https://www.ghostccamm.com/blog/multi_strapi_vulns/#cve-2023-22893-authentication-bypass-for-aws-cognito-login-provider-in-strapi-versions-456">JWT claim has not been verified</a>
|
||||
</li>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* @name JWT missing secret or public key verification
|
||||
* @description The application does not verify the JWT payload with a cryptographic secret or public key.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 8.0
|
||||
* @precision high
|
||||
* @id js/decode-jwt-without-verification
|
||||
* @tags security
|
||||
* external/cwe/cwe-347
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import DataFlow::PathGraph
|
||||
import JWT
|
||||
|
||||
class ConfigurationUnverifiedDecode extends TaintTracking::Configuration {
|
||||
ConfigurationUnverifiedDecode() { this = "jsonwebtoken without any signature verification" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink = unverifiedDecode() }
|
||||
}
|
||||
|
||||
class ConfigurationVerifiedDecode extends TaintTracking::Configuration {
|
||||
ConfigurationVerifiedDecode() { this = "jsonwebtoken with signature verification" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink = verifiedDecode() }
|
||||
}
|
||||
|
||||
from ConfigurationUnverifiedDecode cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
not exists(ConfigurationVerifiedDecode cfg2 |
|
||||
cfg2.hasFlowPath(any(DataFlow::PathNode p | p.getNode() = source.getNode()), _)
|
||||
)
|
||||
select source.getNode(), source, sink, "Decoding JWT $@.", sink.getNode(),
|
||||
"without signature verification"
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* @name JWT missing secret or public key verification
|
||||
* @description The application does not verify the JWT payload with a cryptographic secret or public key.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 8.0
|
||||
* @precision high
|
||||
* @id js/decode-jwt-without-verification-local-source
|
||||
* @tags security
|
||||
* external/cwe/cwe-347
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import DataFlow::PathGraph
|
||||
import JWT
|
||||
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "jsonwebtoken without any signature verification" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source = [unverifiedDecode(), verifiedDecode()].getALocalSource()
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink = unverifiedDecode()
|
||||
or
|
||||
sink = verifiedDecode()
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if `source` flows to the first parameter of jsonwebtoken.verify */
|
||||
predicate isSafe(Configuration cfg, DataFlow::Node source) {
|
||||
exists(DataFlow::Node sink |
|
||||
cfg.hasFlow(source, sink) and
|
||||
sink = verifiedDecode()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if:
|
||||
* - `source` does not flow to the first parameter of `jsonwebtoken.verify`, and
|
||||
* - `source` flows to the first parameter of `jsonwebtoken.decode`
|
||||
*/
|
||||
predicate isVulnerable(Configuration cfg, DataFlow::Node source, DataFlow::Node sink) {
|
||||
not isSafe(cfg, source) and // i.e., source does not flow to a verify call
|
||||
cfg.hasFlow(source, sink) and // but it does flow to something else
|
||||
sink = unverifiedDecode() // and that something else is a call to decode.
|
||||
}
|
||||
|
||||
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
isVulnerable(cfg, source.getNode(), sink.getNode())
|
||||
select source.getNode(), source, sink, "Decoding JWT $@.", sink.getNode(),
|
||||
"without signature verification"
|
||||
@@ -0,0 +1,43 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Extracting Compressed files with any compression algorithm like gzip can cause to denial of service attacks.</p>
|
||||
<p>Attackers can compress a huge file which created by repeated similiar byte and convert it to a small compressed file.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>When you want to decompress a user-provided compressed file you must be careful about the decompression ratio or read these files within a loop byte by byte to be able to manage the decompressed size in each cycle of the loop.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>
|
||||
JsZip: check uncompressedSize Object Field before extraction.
|
||||
</p>
|
||||
<sample src="jszip_good.js"/>
|
||||
|
||||
<p>
|
||||
nodejs Zlib: use <a href="https://nodejs.org/dist/latest-v18.x/docs/api/zlib.html#class-options">maxOutputLength option</a> which it'll limit the buffer read size
|
||||
</p>
|
||||
<sample src="zlib_good.js" />
|
||||
|
||||
<p>
|
||||
node-tar: use <a href="https://github.com/isaacs/node-tar/blob/8c5af15e43a769fd24aa7f1c84d93e54824d19d2/lib/list.js#L90">maxReadSize option</a> which it'll limit the buffer read size
|
||||
</p>
|
||||
<sample src="node-tar_good.js" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
<a href="https://github.com/advisories/GHSA-8225-6cvr-8pqp">CVE-2017-16129</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://www.bamsoftware.com/hacks/zipbomb/">A great research to gain more impact by this kind of attacks</a>
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* @name User-controlled file decompression
|
||||
* @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 7.8
|
||||
* @precision high
|
||||
* @id js/user-controlled-data-decompression
|
||||
* @tags security
|
||||
* experimental
|
||||
* external/cwe/cwe-522
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import DataFlow::PathGraph
|
||||
import DecompressionBombs
|
||||
|
||||
class BombConfiguration extends TaintTracking::Configuration {
|
||||
BombConfiguration() { this = "DecompressionBombs" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof DecompressionBomb::Sink }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(DecompressionBomb::AdditionalTaintStep addstep |
|
||||
addstep.isAdditionalTaintStep(pred, succ)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from BombConfiguration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "This Decompression depends on a $@.", source.getNode(),
|
||||
"potentially untrusted source"
|
||||
@@ -0,0 +1,432 @@
|
||||
import javascript
|
||||
import experimental.semmle.javascript.FormParsers
|
||||
import experimental.semmle.javascript.ReadableStream
|
||||
import DataFlow::PathGraph
|
||||
|
||||
module DecompressionBomb {
|
||||
/**
|
||||
* The Sinks of uncontrolled data decompression
|
||||
*/
|
||||
class Sink extends DataFlow::Node {
|
||||
Sink() { this = any(Range r).sink() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The additional taint steps that need for creating taint tracking or dataflow.
|
||||
*/
|
||||
abstract class AdditionalTaintStep extends string {
|
||||
AdditionalTaintStep() { this = "AdditionalTaintStep" }
|
||||
|
||||
/**
|
||||
* Holds if there is a additional taint step between pred and succ.
|
||||
*/
|
||||
abstract predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ);
|
||||
}
|
||||
|
||||
/**
|
||||
* A abstract class responsible for extending new decompression sinks
|
||||
*/
|
||||
abstract class Range extends API::Node {
|
||||
/**
|
||||
* Gets the sink of responsible for decompression node
|
||||
*
|
||||
* it can be a path, stream of compressed data,
|
||||
* or a call to function that use pipe
|
||||
*/
|
||||
abstract DataFlow::Node sink();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides additional taint steps for Readable Stream object
|
||||
*/
|
||||
module ReadableStream {
|
||||
class ReadableStreamAdditionalTaintStep extends DecompressionBomb::AdditionalTaintStep {
|
||||
ReadableStreamAdditionalTaintStep() { this = "AdditionalTaintStep" }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
(
|
||||
readablePipeAdditionalTaintStep(pred, succ)
|
||||
or
|
||||
streamPipelineAdditionalTaintStep(pred, succ)
|
||||
or
|
||||
promisesFileHandlePipeAdditionalTaintStep(pred, succ)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides additional taint steps for File system access functions
|
||||
*/
|
||||
module FileSystemAccessAdditionalTaintStep {
|
||||
class ReadableStreamAdditionalTaintStep extends DecompressionBomb::AdditionalTaintStep {
|
||||
ReadableStreamAdditionalTaintStep() { this = "AdditionalTaintStep" }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
// additional taint step for fs.readFile(pred)
|
||||
// It can be global additional step too
|
||||
exists(DataFlow::CallNode n | n = DataFlow::moduleMember("fs", "readFile").getACall() |
|
||||
pred = n.getArgument(0) and succ = n.getABoundCallbackParameter(1, 1)
|
||||
)
|
||||
or
|
||||
exists(FileSystemReadAccess cn |
|
||||
pred = cn.getAPathArgument() and
|
||||
succ = cn.getADataNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides Models for [jszip](https://www.npmjs.com/package/jszip) package
|
||||
*/
|
||||
module JsZip {
|
||||
/**
|
||||
* The decompression bomb sinks
|
||||
*/
|
||||
class DecompressionBomb extends DecompressionBomb::Range {
|
||||
DecompressionBomb() { this = API::moduleImport("jszip").getMember("loadAsync") }
|
||||
|
||||
override DataFlow::Node sink() {
|
||||
result = this.getParameter(0).asSink() and not this.sanitizer(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a jszip `loadAsync` instance
|
||||
* and Holds if member of name `uncompressedSize` exists
|
||||
*/
|
||||
predicate sanitizer(API::Node loadAsync) {
|
||||
exists(loadAsync.getASuccessor*().getMember("_data").getMember("uncompressedSize"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides Models for [node-tar](https://www.npmjs.com/package/tar) package
|
||||
*/
|
||||
module NodeTar {
|
||||
/**
|
||||
* The decompression bomb sinks
|
||||
*/
|
||||
class DecompressionBomb extends DecompressionBomb::Range {
|
||||
DecompressionBomb() { this = API::moduleImport("tar").getMember(["x", "extract"]) }
|
||||
|
||||
override DataFlow::Node sink() {
|
||||
(
|
||||
// piping tar.x()
|
||||
result = this.getACall()
|
||||
or
|
||||
// tar.x({file: filename})
|
||||
result = this.getParameter(0).getMember("file").asSink()
|
||||
) and
|
||||
// and there shouldn't be a "maxReadSize: ANum" option
|
||||
not this.sanitizer(this.getParameter(0))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a options parameter that belong to a `tar` instance
|
||||
* and Holds if "maxReadSize: ANumber" option exists
|
||||
*/
|
||||
predicate sanitizer(API::Node tarExtract) { exists(tarExtract.getMember("maxReadSize")) }
|
||||
}
|
||||
|
||||
/**
|
||||
* The decompression Additional Taint Steps
|
||||
*/
|
||||
class DecompressionAdditionalSteps extends DecompressionBomb::AdditionalTaintStep {
|
||||
DecompressionAdditionalSteps() { this = "AdditionalTaintStep" }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(API::Node n | n = API::moduleImport("tar") |
|
||||
pred = n.asSource() and
|
||||
(
|
||||
succ = n.getMember("x").getACall() or
|
||||
succ = n.getMember("x").getACall().getArgument(0)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides Models for `node:zlib` package
|
||||
*/
|
||||
module Zlib {
|
||||
/**
|
||||
* The decompression sinks of `node:zlib`
|
||||
*/
|
||||
class DecompressionBomb extends DecompressionBomb::Range {
|
||||
boolean isSynk;
|
||||
|
||||
DecompressionBomb() {
|
||||
this =
|
||||
API::moduleImport("zlib")
|
||||
.getMember([
|
||||
"gunzip", "gunzipSync", "unzip", "unzipSync", "brotliDecompress",
|
||||
"brotliDecompressSync", "inflateSync", "inflateRawSync", "inflate", "inflateRaw"
|
||||
]) and
|
||||
isSynk = true
|
||||
or
|
||||
this =
|
||||
API::moduleImport("zlib")
|
||||
.getMember([
|
||||
"createGunzip", "createBrotliDecompress", "createUnzip", "createInflate",
|
||||
"createInflateRaw"
|
||||
]) and
|
||||
isSynk = false
|
||||
}
|
||||
|
||||
override DataFlow::Node sink() {
|
||||
result = this.getACall() and
|
||||
not this.sanitizer(this.getParameter(0)) and
|
||||
isSynk = false
|
||||
or
|
||||
result = this.getACall().getArgument(0) and
|
||||
not this.sanitizer(this.getParameter(1)) and
|
||||
isSynk = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a options parameter that belong to a zlib instance
|
||||
* and Holds if "maxOutputLength: ANumber" option exists
|
||||
*/
|
||||
predicate sanitizer(API::Node zlib) { exists(zlib.getMember("maxOutputLength")) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides Models for [pako](https://www.npmjs.com/package/pako) package
|
||||
*/
|
||||
module Pako {
|
||||
/**
|
||||
* The decompression bomb sinks
|
||||
*/
|
||||
class DecompressionBomb extends DecompressionBomb::Range {
|
||||
DecompressionBomb() {
|
||||
this = API::moduleImport("pako").getMember(["inflate", "inflateRaw", "ungzip"])
|
||||
}
|
||||
|
||||
override DataFlow::Node sink() { result = this.getParameter(0).asSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The decompression Additional Taint Steps
|
||||
*/
|
||||
class DecompressionAdditionalSteps extends DecompressionBomb::AdditionalTaintStep {
|
||||
DecompressionAdditionalSteps() { this = "AdditionalTaintStep" }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
// succ = new Uint8Array(pred)
|
||||
exists(DataFlow::Node n, NewExpr ne | ne = n.asExpr() |
|
||||
pred.asExpr() = ne.getArgument(0) and
|
||||
succ.asExpr() = ne and
|
||||
ne.getCalleeName() = "Uint8Array"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides Models for [adm-zip](https://www.npmjs.com/package/adm-zip) package
|
||||
*/
|
||||
module AdmZip {
|
||||
/**
|
||||
* The decompression bomb sinks
|
||||
*/
|
||||
class DecompressionBomb extends DecompressionBomb::Range {
|
||||
DecompressionBomb() { this = API::moduleImport("adm-zip").getInstance() }
|
||||
|
||||
override DataFlow::Node sink() {
|
||||
result =
|
||||
this.getMember(["extractAllTo", "extractEntryTo", "readAsText"]).getReturn().asSource()
|
||||
or
|
||||
result = this.getASuccessor*().getMember("getData").getReturn().asSource()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The decompression Additional Taint Steps
|
||||
*/
|
||||
class DecompressionAdditionalSteps extends DecompressionBomb::AdditionalTaintStep {
|
||||
DecompressionAdditionalSteps() { this = "AdditionalTaintStep" }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(API::Node n | n = API::moduleImport("adm-zip") |
|
||||
pred = n.getParameter(0).asSink() and
|
||||
(
|
||||
succ =
|
||||
n.getInstance()
|
||||
.getMember(["extractAllTo", "extractEntryTo", "readAsText"])
|
||||
.getReturn()
|
||||
.asSource()
|
||||
or
|
||||
succ =
|
||||
n.getInstance()
|
||||
.getMember("getEntries")
|
||||
.getASuccessor*()
|
||||
.getMember("getData")
|
||||
.getReturn()
|
||||
.asSource()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides Models for [decompress](https://www.npmjs.com/package/decompress) package
|
||||
*/
|
||||
module Decompress {
|
||||
/**
|
||||
* The decompression bomb sinks
|
||||
*/
|
||||
class DecompressionBomb extends DecompressionBomb::Range {
|
||||
DecompressionBomb() { this = API::moduleImport("decompress") }
|
||||
|
||||
override DataFlow::Node sink() { result = this.getACall().getArgument(0) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides Models for [gunzip-maybe][https://www.npmjs.com/package/gunzip-maybe] package
|
||||
*/
|
||||
module GunzipMaybe {
|
||||
/**
|
||||
* The decompression bomb sinks
|
||||
*/
|
||||
class DecompressionBomb extends DecompressionBomb::Range {
|
||||
DecompressionBomb() { this = API::moduleImport("gunzip-maybe") }
|
||||
|
||||
override DataFlow::Node sink() { result = this.getACall() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides Models for [unbzip2-stream](https://www.npmjs.com/package/unbzip2-stream) package
|
||||
*/
|
||||
module Unbzip2Stream {
|
||||
/**
|
||||
* The decompression bomb sinks
|
||||
*/
|
||||
class DecompressionBomb extends DecompressionBomb::Range {
|
||||
DecompressionBomb() { this = API::moduleImport("unbzip2-stream") }
|
||||
|
||||
override DataFlow::Node sink() { result = this.getACall() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides Models for [unzipper](https://www.npmjs.com/package/unzipper) package
|
||||
*/
|
||||
module Unzipper {
|
||||
/**
|
||||
* The decompression bomb sinks
|
||||
*/
|
||||
class DecompressionBomb extends DecompressionBomb::Range {
|
||||
string funcName;
|
||||
|
||||
DecompressionBomb() {
|
||||
this = API::moduleImport("unzipper").getMember(["Extract", "Parse", "ParseOne"]) and
|
||||
funcName = ["Extract", "Parse", "ParseOne"]
|
||||
or
|
||||
this = API::moduleImport("unzipper").getMember("Open") and
|
||||
// open has some functions which will be specified in sink predicate
|
||||
funcName = "Open"
|
||||
}
|
||||
|
||||
override DataFlow::Node sink() {
|
||||
result = this.getMember(["buffer", "file", "url", "file"]).getACall().getArgument(0) and
|
||||
funcName = "Open"
|
||||
or
|
||||
result = this.getACall() and
|
||||
funcName = ["Extract", "Parse", "ParseOne"]
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a
|
||||
* and Holds if unzipper instance has a member `uncompressedSize`
|
||||
*
|
||||
* it is really difficult to implement this sanitizer,
|
||||
* so i'm going to check if there is a member like `vars.uncompressedSize` in whole DB or not!
|
||||
*/
|
||||
predicate sanitizer() {
|
||||
exists(this.getASuccessor*().getMember("vars").getMember("uncompressedSize")) and
|
||||
funcName = ["Extract", "Parse", "ParseOne"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides Models for [yauzl](https://www.npmjs.com/package/yauzl) package
|
||||
*/
|
||||
module Yauzl {
|
||||
API::Node test() { result = API::moduleImport("yauzl").getASuccessor*() }
|
||||
|
||||
/**
|
||||
* The decompression bomb sinks
|
||||
*/
|
||||
class DecompressionBomb extends DecompressionBomb::Range {
|
||||
// open function has a sanitizer
|
||||
string methodName;
|
||||
|
||||
DecompressionBomb() {
|
||||
this =
|
||||
API::moduleImport("yauzl").getMember(["fromFd", "fromBuffer", "fromRandomAccessReader"]) and
|
||||
methodName = "from"
|
||||
or
|
||||
this = API::moduleImport("yauzl").getMember("open") and
|
||||
methodName = "open"
|
||||
}
|
||||
|
||||
override DataFlow::Node sink() {
|
||||
(
|
||||
result = this.getParameter(2).getParameter(1).getMember("readEntry").getACall() or
|
||||
result =
|
||||
this.getParameter(2)
|
||||
.getParameter(1)
|
||||
.getMember("openReadStream")
|
||||
.getParameter(1)
|
||||
.getParameter(1)
|
||||
.asSource()
|
||||
) and
|
||||
not this.sanitizer() and
|
||||
methodName = "open"
|
||||
or
|
||||
result = this.getParameter(0).asSink() and
|
||||
methodName = "from"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a
|
||||
* and Holds if yauzl `open` instance has a member `uncompressedSize`
|
||||
*/
|
||||
predicate sanitizer() {
|
||||
exists(this.getASuccessor*().getMember("uncompressedSize")) and
|
||||
methodName = ["readStream", "open"]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The decompression Additional Taint Steps
|
||||
*/
|
||||
class DecompressionAdditionalSteps extends DecompressionBomb::AdditionalTaintStep {
|
||||
DecompressionAdditionalSteps() { this = "AdditionalTaintStep" }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(API::Node open | open = API::moduleImport("yauzl").getMember("open") |
|
||||
pred = open.getParameter(0).asSink() and
|
||||
(
|
||||
succ = open.getParameter(2).getParameter(1).getMember("readEntry").getACall() or
|
||||
succ =
|
||||
open.getParameter(2)
|
||||
.getParameter(1)
|
||||
.getMember("openReadStream")
|
||||
.getParameter(1)
|
||||
.getParameter(1)
|
||||
.asSource()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
const jszipp = require("jszip");
|
||||
function zipBombSafe(zipFile) {
|
||||
jszipp.loadAsync(zipFile.data).then(function (zip) {
|
||||
if (zip.file("10GB")["_data"]["uncompressedSize"] > 1024 * 1024 * 8) {
|
||||
console.log("error")
|
||||
}
|
||||
zip.file("10GB").async("uint8array").then(function (u8) {
|
||||
console.log(u8);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
const tar = require("tar");
|
||||
|
||||
tar.x({
|
||||
file: tarFileName,
|
||||
strip: 1,
|
||||
C: 'some-dir',
|
||||
maxReadSize: 16 * 1024 * 1024 // 16 MB
|
||||
})
|
||||
@@ -0,0 +1,11 @@
|
||||
const zlib = require("zlib");
|
||||
|
||||
zlib.gunzip(
|
||||
inputZipFile.data,
|
||||
{ maxOutputLength: 1024 * 1024 * 5 },
|
||||
(err, buffer) => {
|
||||
doSomeThingWithData(buffer);
|
||||
});
|
||||
zlib.gunzipSync(inputZipFile.data, { maxOutputLength: 1024 * 1024 * 5 });
|
||||
|
||||
inputZipFile.pipe(zlib.createGunzip({ maxOutputLength: 1024 * 1024 * 5 })).pipe(outputFile);
|
||||
211
javascript/ql/src/experimental/semmle/javascript/Execa.qll
Normal file
211
javascript/ql/src/experimental/semmle/javascript/Execa.qll
Normal file
@@ -0,0 +1,211 @@
|
||||
/**
|
||||
* Models the `execa` library in terms of `FileSystemAccess` and `SystemCommandExecution`.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* Provide model for [Execa](https://github.com/sindresorhus/execa) package
|
||||
*/
|
||||
module Execa {
|
||||
/**
|
||||
* The Execa input file read and output file write
|
||||
*/
|
||||
class ExecaFileSystemAccess extends FileSystemReadAccess, DataFlow::Node {
|
||||
API::Node execaArg;
|
||||
boolean isPipedToFile;
|
||||
|
||||
ExecaFileSystemAccess() {
|
||||
(
|
||||
execaArg = API::moduleImport("execa").getMember("$").getParameter(0) and
|
||||
isPipedToFile = false
|
||||
or
|
||||
execaArg =
|
||||
API::moduleImport("execa")
|
||||
.getMember(["execa", "execaCommand", "execaCommandSync", "execaSync"])
|
||||
.getParameter([0, 1, 2]) and
|
||||
isPipedToFile = false
|
||||
or
|
||||
execaArg =
|
||||
API::moduleImport("execa")
|
||||
.getMember(["execa", "execaCommand", "execaCommandSync", "execaSync"])
|
||||
.getReturn()
|
||||
.getMember(["pipeStdout", "pipeAll", "pipeStderr"])
|
||||
.getParameter(0) and
|
||||
isPipedToFile = true
|
||||
) and
|
||||
this = execaArg.asSink()
|
||||
}
|
||||
|
||||
override DataFlow::Node getADataNode() { none() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
result = execaArg.getMember("inputFile").asSink() and isPipedToFile = false
|
||||
or
|
||||
result = execaArg.asSink() and isPipedToFile = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `execa.execa` or `execa.execaSync`
|
||||
*/
|
||||
class ExecaCall extends API::CallNode {
|
||||
boolean isSync;
|
||||
|
||||
ExecaCall() {
|
||||
this = API::moduleImport("execa").getMember("execa").getACall() and
|
||||
isSync = false
|
||||
or
|
||||
this = API::moduleImport("execa").getMember("execaSync").getACall() and
|
||||
isSync = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The system command execution nodes for `execa.execa` or `execa.execaSync` functions
|
||||
*/
|
||||
class ExecaExec extends SystemCommandExecution, ExecaCall {
|
||||
ExecaExec() { isSync = [false, true] }
|
||||
|
||||
override DataFlow::Node getACommandArgument() { result = this.getArgument(0) }
|
||||
|
||||
override predicate isShellInterpreted(DataFlow::Node arg) {
|
||||
// if shell: true then first and second args are sinks
|
||||
// options can be third argument
|
||||
arg = [this.getArgument(0), this.getParameter(1).getUnknownMember().asSink()] and
|
||||
isExecaShellEnable(this.getParameter(2))
|
||||
or
|
||||
// options can be second argument
|
||||
arg = this.getArgument(0) and
|
||||
isExecaShellEnable(this.getParameter(1))
|
||||
}
|
||||
|
||||
override DataFlow::Node getArgumentList() {
|
||||
// execa(cmd, [arg]);
|
||||
exists(DataFlow::Node arg | arg = this.getArgument(1) |
|
||||
// if it is a object then it is a option argument not command argument
|
||||
result = arg and not arg.asExpr() instanceof ObjectExpr
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSync() { isSync = true }
|
||||
|
||||
override DataFlow::Node getOptionsArg() {
|
||||
result = this.getLastArgument() and result.asExpr() instanceof ObjectExpr
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `execa.$` or `execa.$.sync` or `execa.$({})` or `execa.$.sync({})` tag functions
|
||||
*/
|
||||
private class ExecaScriptCall extends API::CallNode {
|
||||
boolean isSync;
|
||||
|
||||
ExecaScriptCall() {
|
||||
exists(API::Node script |
|
||||
script =
|
||||
[
|
||||
API::moduleImport("execa").getMember("$"),
|
||||
API::moduleImport("execa").getMember("$").getReturn()
|
||||
]
|
||||
|
|
||||
this = script.getACall() and
|
||||
isSync = false
|
||||
or
|
||||
this = script.getMember("sync").getACall() and
|
||||
isSync = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The system command execution nodes for `execa.$` or `execa.$.sync` tag functions
|
||||
*/
|
||||
class ExecaScript extends SystemCommandExecution, ExecaScriptCall {
|
||||
ExecaScript() { isSync = [false, true] }
|
||||
|
||||
override DataFlow::Node getACommandArgument() {
|
||||
result = this.getParameter(1).asSink() and
|
||||
not isTaggedTemplateFirstChildAnElement(this.getParameter(1).asSink().asExpr().getParent())
|
||||
}
|
||||
|
||||
override predicate isShellInterpreted(DataFlow::Node arg) {
|
||||
isExecaShellEnable(this.getParameter(0)) and
|
||||
arg = this.getAParameter().asSink()
|
||||
}
|
||||
|
||||
override DataFlow::Node getArgumentList() {
|
||||
result = this.getParameter(any(int i | i >= 1)).asSink() and
|
||||
isTaggedTemplateFirstChildAnElement(this.getParameter(1).asSink().asExpr().getParent())
|
||||
or
|
||||
result = this.getParameter(any(int i | i >= 2)).asSink() and
|
||||
not isTaggedTemplateFirstChildAnElement(this.getParameter(1).asSink().asExpr().getParent())
|
||||
}
|
||||
|
||||
override DataFlow::Node getOptionsArg() { result = this.getParameter(0).asSink() }
|
||||
|
||||
override predicate isSync() { isSync = true }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `execa.execaCommandSync` or `execa.execaCommand`
|
||||
*/
|
||||
private class ExecaCommandCall extends API::CallNode {
|
||||
boolean isSync;
|
||||
|
||||
ExecaCommandCall() {
|
||||
this = API::moduleImport("execa").getMember("execaCommandSync").getACall() and
|
||||
isSync = true
|
||||
or
|
||||
this = API::moduleImport("execa").getMember("execaCommand").getACall() and
|
||||
isSync = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The system command execution nodes for `execa.execaCommand` or `execa.execaCommandSync` functions
|
||||
*/
|
||||
class ExecaCommandExec extends SystemCommandExecution, ExecaCommandCall {
|
||||
ExecaCommandExec() { isSync = [false, true] }
|
||||
|
||||
override DataFlow::Node getACommandArgument() {
|
||||
result = this.(DataFlow::CallNode).getArgument(0)
|
||||
}
|
||||
|
||||
override DataFlow::Node getArgumentList() {
|
||||
// execaCommand(`${cmd} ${arg}`);
|
||||
result.asExpr() = this.getParameter(0).asSink().asExpr().getAChildExpr() and
|
||||
not result.asExpr() = this.getArgument(0).asExpr().getChildExpr(0)
|
||||
}
|
||||
|
||||
override predicate isShellInterpreted(DataFlow::Node arg) {
|
||||
// execaCommandSync(`${cmd} ${arg}`, {shell: true})
|
||||
arg.asExpr() = this.getArgument(0).asExpr().getAChildExpr+() and
|
||||
isExecaShellEnable(this.getParameter(1))
|
||||
or
|
||||
// there is only one argument that is constructed in previous nodes,
|
||||
// it makes sanitizing really hard to select whether it is vulnerable to argument injection or not
|
||||
arg = this.getParameter(0).asSink() and
|
||||
not exists(this.getArgument(0).asExpr().getChildExpr(1))
|
||||
}
|
||||
|
||||
override predicate isSync() { isSync = true }
|
||||
|
||||
override DataFlow::Node getOptionsArg() {
|
||||
result = this.getLastArgument() and result.asExpr() instanceof ObjectExpr
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets a TemplateLiteral and check if first child is a template element */
|
||||
private predicate isTaggedTemplateFirstChildAnElement(TemplateLiteral templateLit) {
|
||||
exists(templateLit.getChildExpr(0).(TemplateElement))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds whether Execa has shell enabled options or not, get Parameter responsible for options
|
||||
*/
|
||||
pragma[inline]
|
||||
private predicate isExecaShellEnable(API::Node n) {
|
||||
n.getMember("shell").asSink().asExpr().(BooleanLiteral).getValue() = "true"
|
||||
}
|
||||
}
|
||||
179
javascript/ql/src/experimental/semmle/javascript/FormParsers.qll
Normal file
179
javascript/ql/src/experimental/semmle/javascript/FormParsers.qll
Normal file
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* Provides classes for modeling the server-side form/file parsing libraries.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import experimental.semmle.javascript.ReadableStream
|
||||
|
||||
/**
|
||||
* A module for modeling [busboy](https://www.npmjs.com/package/busboy) package
|
||||
*/
|
||||
module BusBoy {
|
||||
/**
|
||||
* A source of remote flow from the `Busboy` library.
|
||||
*/
|
||||
private class BusBoyRemoteFlow extends RemoteFlowSource {
|
||||
BusBoyRemoteFlow() {
|
||||
exists(API::Node busboyOnEvent |
|
||||
busboyOnEvent = API::moduleImport("busboy").getReturn().getMember("on")
|
||||
|
|
||||
// Files
|
||||
busboyOnEvent.getParameter(0).asSink().mayHaveStringValue("file") and
|
||||
// second param of 'file' event is a Readable stream
|
||||
this = readableStreamDataNode(busboyOnEvent.getParameter(1).getParameter(1))
|
||||
or
|
||||
// Fields
|
||||
busboyOnEvent.getParameter(0).asSink().mayHaveStringValue(["file", "field"]) and
|
||||
this =
|
||||
API::moduleImport("busboy")
|
||||
.getReturn()
|
||||
.getMember("on")
|
||||
.getParameter(1)
|
||||
.getAParameter()
|
||||
.asSource()
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "parsed user value from Busbuy" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A busboy file data step according to a Readable Stream type
|
||||
*/
|
||||
private class AdditionalTaintStep extends TaintTracking::SharedTaintStep {
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(API::Node busboyOnEvent |
|
||||
busboyOnEvent = API::moduleImport("busboy").getReturn().getMember("on")
|
||||
|
|
||||
busboyOnEvent.getParameter(0).asSink().mayHaveStringValue("file") and
|
||||
customStreamPipeAdditionalTaintStep(busboyOnEvent.getParameter(1).getParameter(1), pred,
|
||||
succ)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A module for modeling [formidable](https://www.npmjs.com/package/formidable) package
|
||||
*/
|
||||
module Formidable {
|
||||
/**
|
||||
* A source of remote flow from the `Formidable` library parsing a HTTP request.
|
||||
*/
|
||||
private class FormidableRemoteFlow extends RemoteFlowSource {
|
||||
FormidableRemoteFlow() {
|
||||
exists(API::Node formidable |
|
||||
formidable = API::moduleImport("formidable").getReturn()
|
||||
or
|
||||
formidable = API::moduleImport("formidable").getMember("formidable").getReturn()
|
||||
or
|
||||
formidable =
|
||||
API::moduleImport("formidable").getMember(["IncomingForm", "Formidable"]).getInstance()
|
||||
|
|
||||
this =
|
||||
formidable.getMember("parse").getACall().getABoundCallbackParameter(1, any(int i | i > 0))
|
||||
or
|
||||
// if callback is not provide a promise will be returned,
|
||||
// return values contains [fields,files] members
|
||||
exists(API::Node parseMethod |
|
||||
parseMethod = formidable.getMember("parse") and parseMethod.getNumParameter() = 1
|
||||
|
|
||||
this = parseMethod.getReturn().asSource()
|
||||
)
|
||||
or
|
||||
// event handler
|
||||
this = formidable.getMember("on").getParameter(1).getAParameter().asSource()
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "parsed user value from Formidable" }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A module for modeling [multiparty](https://www.npmjs.com/package/multiparty) package
|
||||
*/
|
||||
module Multiparty {
|
||||
/**
|
||||
* A source of remote flow from the `Multiparty` library.
|
||||
*/
|
||||
private class MultipartyRemoteFlow extends RemoteFlowSource {
|
||||
MultipartyRemoteFlow() {
|
||||
exists(API::Node form |
|
||||
form = API::moduleImport("multiparty").getMember("Form").getInstance()
|
||||
|
|
||||
exists(API::CallNode parse | parse = form.getMember("parse").getACall() |
|
||||
this = parse.getParameter(1).getParameter([1, 2]).asSource()
|
||||
)
|
||||
or
|
||||
exists(API::Node on | on = form.getMember("on") |
|
||||
(
|
||||
on.getParameter(0).asSink().mayHaveStringValue(["file", "field"]) and
|
||||
this = on.getParameter(1).getParameter([0, 1]).asSource()
|
||||
or
|
||||
on.getParameter(0).asSink().mayHaveStringValue("part") and
|
||||
this = readableStreamDataNode(on.getParameter(1).getParameter(0))
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "parsed user value from Multiparty" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A multiparty part data step according to a Readable Stream type
|
||||
*/
|
||||
private class AdditionalTaintStep extends TaintTracking::SharedTaintStep {
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(API::Node multipartyOnEvent |
|
||||
multipartyOnEvent =
|
||||
API::moduleImport("multiparty").getMember("Form").getInstance().getMember("on")
|
||||
|
|
||||
multipartyOnEvent.getParameter(0).asSink().mayHaveStringValue("part") and
|
||||
customStreamPipeAdditionalTaintStep(multipartyOnEvent.getParameter(1).getParameter(0), pred,
|
||||
succ)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A module for modeling [dicer](https://www.npmjs.com/package/dicer) package
|
||||
*/
|
||||
module Dicer {
|
||||
/**
|
||||
* A source of remote flow from the `dicer` library.
|
||||
*/
|
||||
private class DicerRemoteFlow extends RemoteFlowSource {
|
||||
DicerRemoteFlow() {
|
||||
exists(API::Node dicer | dicer = API::moduleImport("dicer").getInstance() |
|
||||
exists(API::Node on | on = dicer.getMember("on") |
|
||||
on.getParameter(0).asSink().mayHaveStringValue("part") and
|
||||
this = readableStreamDataNode(on.getParameter(1).getParameter(0))
|
||||
or
|
||||
exists(API::Node onPart | onPart = on.getParameter(1).getParameter(0).getMember("on") |
|
||||
onPart.getParameter(0).asSink().mayHaveStringValue("header") and
|
||||
this = onPart.getParameter(1).getParameter(0).asSource()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "parsed user value from Dicer" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A dicer part data step according to a Readable Stream type
|
||||
*/
|
||||
private class AdditionalTaintStep extends TaintTracking::SharedTaintStep {
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(API::Node onEvent |
|
||||
onEvent = API::moduleImport("dicer").getInstance().getMember("on")
|
||||
|
|
||||
onEvent.getParameter(0).asSink().mayHaveStringValue("part") and
|
||||
customStreamPipeAdditionalTaintStep(onEvent.getParameter(1).getParameter(0), pred, succ)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* Provides helper predicates to work with any Readable Stream in dataflow queries
|
||||
*
|
||||
* main predicate in which you can use by passing a Readable Stream is `customStreamPipeAdditionalTaintStep`
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* Holds if there is a step between `fs.createReadStream` and `stream.Readable.from` first parameters to all other piped parameters
|
||||
*
|
||||
* It can be global additional step too
|
||||
*/
|
||||
predicate readablePipeAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(API::Node receiver |
|
||||
receiver =
|
||||
[
|
||||
API::moduleImport("fs").getMember("createReadStream"),
|
||||
API::moduleImport("stream").getMember("Readable").getMember("from")
|
||||
]
|
||||
|
|
||||
customStreamPipeAdditionalTaintStep(receiver, pred, succ)
|
||||
or
|
||||
pred = receiver.getParameter(0).asSink() and
|
||||
succ = receiver.getReturn().asSource()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* additional taint steps for piped stream from `createReadStream` method of `fs/promises.open`
|
||||
*
|
||||
* It can be global additional step too
|
||||
*/
|
||||
predicate promisesFileHandlePipeAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(API::Node receiver | receiver = nodeJsPromisesFileSystem().getMember("open") |
|
||||
customStreamPipeAdditionalTaintStep(receiver, pred, succ)
|
||||
or
|
||||
pred = receiver.getParameter(0).asSink() and
|
||||
succ = receiver.getReturn().asSource()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets nodejs `fs` Promises API
|
||||
*/
|
||||
API::Node nodeJsPromisesFileSystem() {
|
||||
result = [API::moduleImport("fs").getMember("promises"), API::moduleImport("fs/promises")]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if
|
||||
* or `receiver.pipe(pred).pipe(sth).pipe(succ)`
|
||||
*
|
||||
* or `receiver.pipe(sth).pipe(pred).pipe(succ)`
|
||||
*
|
||||
* or `receiver.pipe(succ)` and receiver is pred
|
||||
*
|
||||
* Receiver is a Readable Stream object
|
||||
*/
|
||||
predicate customStreamPipeAdditionalTaintStep(
|
||||
API::Node receiver, DataFlow::Node pred, DataFlow::Node succ
|
||||
) {
|
||||
// following connect the first pipe parameter to the last pipe parameter
|
||||
exists(API::Node firstPipe | firstPipe = receiver.getMember("pipe") |
|
||||
pred = firstPipe.getParameter(0).asSink() and
|
||||
succ = firstPipe.getASuccessor*().getMember("pipe").getParameter(0).asSink()
|
||||
)
|
||||
or
|
||||
// following connect a pipe parameter to the next pipe parameter
|
||||
exists(API::Node cn | cn = receiver.getASuccessor+() |
|
||||
pred = cn.getParameter(0).asSink() and
|
||||
succ = cn.getReturn().getMember("pipe").getParameter(0).asSink()
|
||||
)
|
||||
or
|
||||
// it is a function that its return value is a Readable stream object
|
||||
pred = receiver.getReturn().asSource() and
|
||||
succ = receiver.getReturn().getMember("pipe").getParameter(0).asSink()
|
||||
or
|
||||
// it is a Readable stream object
|
||||
pred = receiver.asSource() and
|
||||
succ = receiver.getMember("pipe").getParameter(0).asSink()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if
|
||||
*
|
||||
* ```js
|
||||
* await pipeline(
|
||||
* pred,
|
||||
* succ_or_pred,
|
||||
* succ
|
||||
* )
|
||||
* ```
|
||||
*
|
||||
* It can be global additional step too
|
||||
*/
|
||||
predicate streamPipelineAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
// this step connect the a pipeline parameter to the next pipeline parameter
|
||||
exists(API::CallNode cn, int i |
|
||||
// we assume that there are maximum 10 pipes mostly or maybe less
|
||||
i in [0 .. 10] and
|
||||
cn = nodeJsStream().getMember("pipeline").getACall()
|
||||
|
|
||||
pred = cn.getParameter(i).asSink() and
|
||||
succ = cn.getParameter(i + 1).asSink()
|
||||
)
|
||||
or
|
||||
// this step connect the first pipeline parameter to the next parameters
|
||||
exists(API::CallNode cn, int i |
|
||||
// we assume that there are maximum 10 pipes mostly or maybe less
|
||||
i in [1 .. 10] and
|
||||
cn = nodeJsStream().getMember("pipeline").getACall()
|
||||
|
|
||||
pred = cn.getParameter(0).asSink() and
|
||||
succ = cn.getParameter(i).asSink()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets `stream` Promises API
|
||||
*/
|
||||
API::Node nodeJsStream() {
|
||||
result = [API::moduleImport("stream/promises"), API::moduleImport("stream").getMember("promises")]
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a Readable stream object,
|
||||
* and returns all nodes responsible for a data read of this Readable stream
|
||||
*/
|
||||
DataFlow::Node readableStreamDataNode(API::Node stream) {
|
||||
result = stream.asSource()
|
||||
or
|
||||
// 'data' event
|
||||
exists(API::CallNode onEvent | onEvent = stream.getMember("on").getACall() |
|
||||
result = onEvent.getParameter(1).getParameter(0).asSource() and
|
||||
onEvent.getParameter(0).asSink().mayHaveStringValue("data")
|
||||
)
|
||||
or
|
||||
// 'Readable' event
|
||||
exists(API::CallNode onEvent | onEvent = stream.getMember("on").getACall() |
|
||||
(
|
||||
result = onEvent.getParameter(1).getReceiver().getMember("read").getReturn().asSource() or
|
||||
result = stream.getMember("read").getReturn().asSource()
|
||||
) and
|
||||
onEvent.getParameter(0).asSink().mayHaveStringValue("readable")
|
||||
)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/javascript-queries
|
||||
version: 0.8.16-dev
|
||||
version: 1.0.2-dev
|
||||
groups:
|
||||
- javascript
|
||||
- queries
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
passingPositiveTests
|
||||
| PASSED | CommandInjection | tests.js:11:46:11:70 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:12:43:12:67 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:13:63:13:87 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:14:62:14:86 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:15:60:15:84 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:17:45:17:69 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:18:42:18:66 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:19:62:19:86 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:20:63:20:87 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:21:60:21:84 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:23:43:23:67 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:24:40:24:64 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:25:40:25:64 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:26:60:26:84 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:28:41:28:65 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:29:58:29:82 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:31:51:31:75 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:32:68:32:92 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:34:49:34:73 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:35:66:35:90 | // test ... jection |
|
||||
failingPositiveTests
|
||||
@@ -0,0 +1,36 @@
|
||||
import { execa, execaSync, execaCommand, execaCommandSync, $ } from 'execa';
|
||||
import http from 'node:http'
|
||||
import url from 'url'
|
||||
|
||||
http.createServer(async function (req, res) {
|
||||
let cmd = url.parse(req.url, true).query["cmd"][0];
|
||||
let arg1 = url.parse(req.url, true).query["arg1"];
|
||||
let arg2 = url.parse(req.url, true).query["arg2"];
|
||||
let arg3 = url.parse(req.url, true).query["arg3"];
|
||||
|
||||
await $`${cmd} ${arg1} ${arg2} ${arg3}`; // test: CommandInjection
|
||||
await $`ssh ${arg1} ${arg2} ${arg3}`; // test: CommandInjection
|
||||
$({ shell: false }).sync`${cmd} ${arg1} ${arg2} ${arg3}`; // test: CommandInjection
|
||||
$({ shell: true }).sync`${cmd} ${arg1} ${arg2} ${arg3}`; // test: CommandInjection
|
||||
$({ shell: false }).sync`ssh ${arg1} ${arg2} ${arg3}`; // test: CommandInjection
|
||||
|
||||
$.sync`${cmd} ${arg1} ${arg2} ${arg3}`; // test: CommandInjection
|
||||
$.sync`ssh ${arg1} ${arg2} ${arg3}`; // test: CommandInjection
|
||||
await $({ shell: true })`${cmd} ${arg1} ${arg2} ${arg3}` // test: CommandInjection
|
||||
await $({ shell: false })`${cmd} ${arg1} ${arg2} ${arg3}` // test: CommandInjection
|
||||
await $({ shell: false })`ssh ${arg1} ${arg2} ${arg3}` // test: CommandInjection
|
||||
|
||||
await execa(cmd, [arg1, arg2, arg3]); // test: CommandInjection
|
||||
await execa(cmd, { shell: true }); // test: CommandInjection
|
||||
await execa(cmd, { shell: true }); // test: CommandInjection
|
||||
await execa(cmd, [arg1, arg2, arg3], { shell: true }); // test: CommandInjection
|
||||
|
||||
execaSync(cmd, [arg1, arg2, arg3]); // test: CommandInjection
|
||||
execaSync(cmd, [arg1, arg2, arg3], { shell: true }); // test: CommandInjection
|
||||
|
||||
await execaCommand(cmd + arg1 + arg2 + arg3); // test: CommandInjection
|
||||
await execaCommand(cmd + arg1 + arg2 + arg3, { shell: true }); // test: CommandInjection
|
||||
|
||||
execaCommandSync(cmd + arg1 + arg2 + arg3); // test: CommandInjection
|
||||
execaCommandSync(cmd + arg1 + arg2 + arg3, { shell: true }); // test: CommandInjection
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
import javascript
|
||||
|
||||
class InlineTest extends LineComment {
|
||||
string tests;
|
||||
|
||||
InlineTest() { tests = this.getText().regexpCapture("\\s*test:(.*)", 1) }
|
||||
|
||||
string getPositiveTest() {
|
||||
result = tests.trim().splitAt(",").trim() and not result.matches("!%")
|
||||
}
|
||||
|
||||
predicate hasPositiveTest(string test) { test = this.getPositiveTest() }
|
||||
|
||||
predicate inNode(DataFlow::Node n) {
|
||||
this.getLocation().getFile() = n.getFile() and
|
||||
this.getLocation().getStartLine() = n.getStartLine()
|
||||
}
|
||||
}
|
||||
|
||||
import experimental.semmle.javascript.Execa
|
||||
|
||||
query predicate passingPositiveTests(string res, string expectation, InlineTest t) {
|
||||
res = "PASSED" and
|
||||
t.hasPositiveTest(expectation) and
|
||||
expectation = "CommandInjection" and
|
||||
exists(SystemCommandExecution n |
|
||||
t.inNode(n.getArgumentList()) or t.inNode(n.getACommandArgument())
|
||||
)
|
||||
}
|
||||
|
||||
query predicate failingPositiveTests(string res, string expectation, InlineTest t) {
|
||||
res = "FAILED" and
|
||||
t.hasPositiveTest(expectation) and
|
||||
expectation = "CommandInjection" and
|
||||
not exists(SystemCommandExecution n |
|
||||
t.inNode(n.getArgumentList()) or t.inNode(n.getACommandArgument())
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
passingPositiveTests
|
||||
| PASSED | PathInjection | tests.js:9:43:9:64 | // test ... jection |
|
||||
| PASSED | PathInjection | tests.js:12:50:12:71 | // test ... jection |
|
||||
| PASSED | PathInjection | tests.js:15:61:15:82 | // test ... jection |
|
||||
| PASSED | PathInjection | tests.js:18:73:18:94 | // test ... jection |
|
||||
failingPositiveTests
|
||||
19
javascript/ql/test/experimental/Execa/PathInjection/tests.js
Normal file
19
javascript/ql/test/experimental/Execa/PathInjection/tests.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { execa, $ } from 'execa';
|
||||
import http from 'node:http'
|
||||
import url from 'url'
|
||||
|
||||
http.createServer(async function (req, res) {
|
||||
let filePath = url.parse(req.url, true).query["filePath"][0];
|
||||
|
||||
// Piping to stdin from a file
|
||||
await $({ inputFile: filePath })`cat` // test: PathInjection
|
||||
|
||||
// Piping to stdin from a file
|
||||
await execa('cat', { inputFile: filePath }); // test: PathInjection
|
||||
|
||||
// Piping Stdout to file
|
||||
await execa('echo', ['example3']).pipeStdout(filePath); // test: PathInjection
|
||||
|
||||
// Piping all of command output to file
|
||||
await execa('echo', ['example4'], { all: true }).pipeAll(filePath); // test: PathInjection
|
||||
});
|
||||
34
javascript/ql/test/experimental/Execa/PathInjection/tests.ql
Normal file
34
javascript/ql/test/experimental/Execa/PathInjection/tests.ql
Normal file
@@ -0,0 +1,34 @@
|
||||
import javascript
|
||||
|
||||
class InlineTest extends LineComment {
|
||||
string tests;
|
||||
|
||||
InlineTest() { tests = this.getText().regexpCapture("\\s*test:(.*)", 1) }
|
||||
|
||||
string getPositiveTest() {
|
||||
result = tests.trim().splitAt(",").trim() and not result.matches("!%")
|
||||
}
|
||||
|
||||
predicate hasPositiveTest(string test) { test = this.getPositiveTest() }
|
||||
|
||||
predicate inNode(DataFlow::Node n) {
|
||||
this.getLocation().getFile() = n.getFile() and
|
||||
this.getLocation().getStartLine() = n.getStartLine()
|
||||
}
|
||||
}
|
||||
|
||||
import experimental.semmle.javascript.Execa
|
||||
|
||||
query predicate passingPositiveTests(string res, string expectation, InlineTest t) {
|
||||
res = "PASSED" and
|
||||
t.hasPositiveTest(expectation) and
|
||||
expectation = "PathInjection" and
|
||||
exists(FileSystemReadAccess n | t.inNode(n.getAPathArgument()))
|
||||
}
|
||||
|
||||
query predicate failingPositiveTests(string res, string expectation, InlineTest t) {
|
||||
res = "FAILED" and
|
||||
t.hasPositiveTest(expectation) and
|
||||
expectation = "PathInjection" and
|
||||
not exists(FileSystemReadAccess n | t.inNode(n.getAPathArgument()))
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
nodes
|
||||
| busybus.js:9:30:9:33 | file |
|
||||
| busybus.js:9:30:9:33 | file |
|
||||
| busybus.js:9:36:9:39 | info |
|
||||
| busybus.js:9:36:9:39 | info |
|
||||
| busybus.js:10:19:10:50 | { filen ... eType } |
|
||||
| busybus.js:10:19:10:57 | encoding |
|
||||
| busybus.js:10:19:10:57 | filename |
|
||||
| busybus.js:10:19:10:57 | mimeType |
|
||||
| busybus.js:10:21:10:28 | filename |
|
||||
| busybus.js:10:31:10:38 | encoding |
|
||||
| busybus.js:10:41:10:48 | mimeType |
|
||||
| busybus.js:10:54:10:57 | info |
|
||||
| busybus.js:12:18:12:25 | filename |
|
||||
| busybus.js:12:18:12:25 | filename |
|
||||
| busybus.js:12:28:12:35 | encoding |
|
||||
| busybus.js:12:28:12:35 | encoding |
|
||||
| busybus.js:12:38:12:45 | mimeType |
|
||||
| busybus.js:12:38:12:45 | mimeType |
|
||||
| busybus.js:13:23:13:23 | z |
|
||||
| busybus.js:13:31:13:36 | sink() |
|
||||
| busybus.js:13:31:13:36 | sink() |
|
||||
| busybus.js:15:30:15:33 | data |
|
||||
| busybus.js:15:30:15:33 | data |
|
||||
| busybus.js:16:22:16:25 | data |
|
||||
| busybus.js:16:22:16:25 | data |
|
||||
| busybus.js:22:25:22:42 | data |
|
||||
| busybus.js:22:32:22:42 | this.read() |
|
||||
| busybus.js:22:32:22:42 | this.read() |
|
||||
| busybus.js:23:26:23:29 | data |
|
||||
| busybus.js:23:26:23:29 | data |
|
||||
| busybus.js:27:25:27:28 | name |
|
||||
| busybus.js:27:25:27:28 | name |
|
||||
| busybus.js:27:31:27:33 | val |
|
||||
| busybus.js:27:31:27:33 | val |
|
||||
| busybus.js:27:36:27:39 | info |
|
||||
| busybus.js:27:36:27:39 | info |
|
||||
| busybus.js:28:18:28:21 | name |
|
||||
| busybus.js:28:18:28:21 | name |
|
||||
| busybus.js:28:24:28:26 | val |
|
||||
| busybus.js:28:24:28:26 | val |
|
||||
| busybus.js:28:29:28:32 | info |
|
||||
| busybus.js:28:29:28:32 | info |
|
||||
| dicer.js:12:23:12:26 | part |
|
||||
| dicer.js:12:23:12:26 | part |
|
||||
| dicer.js:13:19:13:24 | sink() |
|
||||
| dicer.js:13:19:13:24 | sink() |
|
||||
| dicer.js:14:28:14:33 | header |
|
||||
| dicer.js:14:28:14:33 | header |
|
||||
| dicer.js:16:22:16:27 | header |
|
||||
| dicer.js:16:22:16:30 | header[h] |
|
||||
| dicer.js:16:22:16:30 | header[h] |
|
||||
| dicer.js:19:26:19:29 | data |
|
||||
| dicer.js:19:26:19:29 | data |
|
||||
| dicer.js:20:18:20:21 | data |
|
||||
| dicer.js:20:18:20:21 | data |
|
||||
| formidable.js:7:11:7:25 | [fields, files] |
|
||||
| formidable.js:7:11:7:49 | fields |
|
||||
| formidable.js:7:11:7:49 | files |
|
||||
| formidable.js:7:12:7:17 | fields |
|
||||
| formidable.js:7:20:7:24 | files |
|
||||
| formidable.js:7:29:7:49 | await f ... se(req) |
|
||||
| formidable.js:7:35:7:49 | form.parse(req) |
|
||||
| formidable.js:7:35:7:49 | form.parse(req) |
|
||||
| formidable.js:8:10:8:15 | fields |
|
||||
| formidable.js:8:10:8:15 | fields |
|
||||
| formidable.js:8:18:8:22 | files |
|
||||
| formidable.js:8:18:8:22 | files |
|
||||
| formidable.js:9:27:9:34 | formname |
|
||||
| formidable.js:9:27:9:34 | formname |
|
||||
| formidable.js:9:37:9:40 | file |
|
||||
| formidable.js:9:37:9:40 | file |
|
||||
| formidable.js:10:14:10:21 | formname |
|
||||
| formidable.js:10:14:10:21 | formname |
|
||||
| formidable.js:10:24:10:27 | file |
|
||||
| formidable.js:10:24:10:27 | file |
|
||||
| formidable.js:12:22:12:29 | formname |
|
||||
| formidable.js:12:22:12:29 | formname |
|
||||
| formidable.js:12:32:12:35 | file |
|
||||
| formidable.js:12:32:12:35 | file |
|
||||
| formidable.js:13:14:13:21 | formname |
|
||||
| formidable.js:13:14:13:21 | formname |
|
||||
| formidable.js:13:24:13:27 | file |
|
||||
| formidable.js:13:24:13:27 | file |
|
||||
| formidable.js:15:23:15:31 | fieldName |
|
||||
| formidable.js:15:23:15:31 | fieldName |
|
||||
| formidable.js:15:34:15:43 | fieldValue |
|
||||
| formidable.js:15:34:15:43 | fieldValue |
|
||||
| formidable.js:16:14:16:22 | fieldName |
|
||||
| formidable.js:16:14:16:22 | fieldName |
|
||||
| formidable.js:16:25:16:34 | fieldValue |
|
||||
| formidable.js:16:25:16:34 | fieldValue |
|
||||
| multiparty.js:8:22:8:25 | part |
|
||||
| multiparty.js:8:22:8:25 | part |
|
||||
| multiparty.js:9:14:9:17 | part |
|
||||
| multiparty.js:9:14:9:17 | part |
|
||||
| multiparty.js:10:19:10:24 | sink() |
|
||||
| multiparty.js:10:19:10:24 | sink() |
|
||||
| multiparty.js:14:37:14:42 | fields |
|
||||
| multiparty.js:14:37:14:42 | fields |
|
||||
| multiparty.js:14:45:14:49 | files |
|
||||
| multiparty.js:14:45:14:49 | files |
|
||||
| multiparty.js:15:14:15:19 | fields |
|
||||
| multiparty.js:15:14:15:19 | fields |
|
||||
| multiparty.js:15:22:15:26 | files |
|
||||
| multiparty.js:15:22:15:26 | files |
|
||||
edges
|
||||
| busybus.js:9:30:9:33 | file | busybus.js:13:23:13:23 | z |
|
||||
| busybus.js:9:30:9:33 | file | busybus.js:13:23:13:23 | z |
|
||||
| busybus.js:9:36:9:39 | info | busybus.js:10:54:10:57 | info |
|
||||
| busybus.js:9:36:9:39 | info | busybus.js:10:54:10:57 | info |
|
||||
| busybus.js:10:19:10:50 | { filen ... eType } | busybus.js:10:21:10:28 | filename |
|
||||
| busybus.js:10:19:10:50 | { filen ... eType } | busybus.js:10:31:10:38 | encoding |
|
||||
| busybus.js:10:19:10:50 | { filen ... eType } | busybus.js:10:41:10:48 | mimeType |
|
||||
| busybus.js:10:19:10:57 | encoding | busybus.js:12:28:12:35 | encoding |
|
||||
| busybus.js:10:19:10:57 | encoding | busybus.js:12:28:12:35 | encoding |
|
||||
| busybus.js:10:19:10:57 | filename | busybus.js:12:18:12:25 | filename |
|
||||
| busybus.js:10:19:10:57 | filename | busybus.js:12:18:12:25 | filename |
|
||||
| busybus.js:10:19:10:57 | mimeType | busybus.js:12:38:12:45 | mimeType |
|
||||
| busybus.js:10:19:10:57 | mimeType | busybus.js:12:38:12:45 | mimeType |
|
||||
| busybus.js:10:21:10:28 | filename | busybus.js:10:19:10:57 | filename |
|
||||
| busybus.js:10:31:10:38 | encoding | busybus.js:10:19:10:57 | encoding |
|
||||
| busybus.js:10:41:10:48 | mimeType | busybus.js:10:19:10:57 | mimeType |
|
||||
| busybus.js:10:54:10:57 | info | busybus.js:10:19:10:50 | { filen ... eType } |
|
||||
| busybus.js:13:23:13:23 | z | busybus.js:13:31:13:36 | sink() |
|
||||
| busybus.js:13:23:13:23 | z | busybus.js:13:31:13:36 | sink() |
|
||||
| busybus.js:15:30:15:33 | data | busybus.js:16:22:16:25 | data |
|
||||
| busybus.js:15:30:15:33 | data | busybus.js:16:22:16:25 | data |
|
||||
| busybus.js:15:30:15:33 | data | busybus.js:16:22:16:25 | data |
|
||||
| busybus.js:15:30:15:33 | data | busybus.js:16:22:16:25 | data |
|
||||
| busybus.js:22:25:22:42 | data | busybus.js:23:26:23:29 | data |
|
||||
| busybus.js:22:25:22:42 | data | busybus.js:23:26:23:29 | data |
|
||||
| busybus.js:22:32:22:42 | this.read() | busybus.js:22:25:22:42 | data |
|
||||
| busybus.js:22:32:22:42 | this.read() | busybus.js:22:25:22:42 | data |
|
||||
| busybus.js:27:25:27:28 | name | busybus.js:28:18:28:21 | name |
|
||||
| busybus.js:27:25:27:28 | name | busybus.js:28:18:28:21 | name |
|
||||
| busybus.js:27:25:27:28 | name | busybus.js:28:18:28:21 | name |
|
||||
| busybus.js:27:25:27:28 | name | busybus.js:28:18:28:21 | name |
|
||||
| busybus.js:27:31:27:33 | val | busybus.js:28:24:28:26 | val |
|
||||
| busybus.js:27:31:27:33 | val | busybus.js:28:24:28:26 | val |
|
||||
| busybus.js:27:31:27:33 | val | busybus.js:28:24:28:26 | val |
|
||||
| busybus.js:27:31:27:33 | val | busybus.js:28:24:28:26 | val |
|
||||
| busybus.js:27:36:27:39 | info | busybus.js:28:29:28:32 | info |
|
||||
| busybus.js:27:36:27:39 | info | busybus.js:28:29:28:32 | info |
|
||||
| busybus.js:27:36:27:39 | info | busybus.js:28:29:28:32 | info |
|
||||
| busybus.js:27:36:27:39 | info | busybus.js:28:29:28:32 | info |
|
||||
| dicer.js:12:23:12:26 | part | dicer.js:13:19:13:24 | sink() |
|
||||
| dicer.js:12:23:12:26 | part | dicer.js:13:19:13:24 | sink() |
|
||||
| dicer.js:12:23:12:26 | part | dicer.js:13:19:13:24 | sink() |
|
||||
| dicer.js:12:23:12:26 | part | dicer.js:13:19:13:24 | sink() |
|
||||
| dicer.js:14:28:14:33 | header | dicer.js:16:22:16:27 | header |
|
||||
| dicer.js:14:28:14:33 | header | dicer.js:16:22:16:27 | header |
|
||||
| dicer.js:16:22:16:27 | header | dicer.js:16:22:16:30 | header[h] |
|
||||
| dicer.js:16:22:16:27 | header | dicer.js:16:22:16:30 | header[h] |
|
||||
| dicer.js:19:26:19:29 | data | dicer.js:20:18:20:21 | data |
|
||||
| dicer.js:19:26:19:29 | data | dicer.js:20:18:20:21 | data |
|
||||
| dicer.js:19:26:19:29 | data | dicer.js:20:18:20:21 | data |
|
||||
| dicer.js:19:26:19:29 | data | dicer.js:20:18:20:21 | data |
|
||||
| formidable.js:7:11:7:25 | [fields, files] | formidable.js:7:12:7:17 | fields |
|
||||
| formidable.js:7:11:7:25 | [fields, files] | formidable.js:7:20:7:24 | files |
|
||||
| formidable.js:7:11:7:49 | fields | formidable.js:8:10:8:15 | fields |
|
||||
| formidable.js:7:11:7:49 | fields | formidable.js:8:10:8:15 | fields |
|
||||
| formidable.js:7:11:7:49 | files | formidable.js:8:18:8:22 | files |
|
||||
| formidable.js:7:11:7:49 | files | formidable.js:8:18:8:22 | files |
|
||||
| formidable.js:7:12:7:17 | fields | formidable.js:7:11:7:49 | fields |
|
||||
| formidable.js:7:20:7:24 | files | formidable.js:7:11:7:49 | files |
|
||||
| formidable.js:7:29:7:49 | await f ... se(req) | formidable.js:7:11:7:25 | [fields, files] |
|
||||
| formidable.js:7:35:7:49 | form.parse(req) | formidable.js:7:29:7:49 | await f ... se(req) |
|
||||
| formidable.js:7:35:7:49 | form.parse(req) | formidable.js:7:29:7:49 | await f ... se(req) |
|
||||
| formidable.js:9:27:9:34 | formname | formidable.js:10:14:10:21 | formname |
|
||||
| formidable.js:9:27:9:34 | formname | formidable.js:10:14:10:21 | formname |
|
||||
| formidable.js:9:27:9:34 | formname | formidable.js:10:14:10:21 | formname |
|
||||
| formidable.js:9:27:9:34 | formname | formidable.js:10:14:10:21 | formname |
|
||||
| formidable.js:9:37:9:40 | file | formidable.js:10:24:10:27 | file |
|
||||
| formidable.js:9:37:9:40 | file | formidable.js:10:24:10:27 | file |
|
||||
| formidable.js:9:37:9:40 | file | formidable.js:10:24:10:27 | file |
|
||||
| formidable.js:9:37:9:40 | file | formidable.js:10:24:10:27 | file |
|
||||
| formidable.js:12:22:12:29 | formname | formidable.js:13:14:13:21 | formname |
|
||||
| formidable.js:12:22:12:29 | formname | formidable.js:13:14:13:21 | formname |
|
||||
| formidable.js:12:22:12:29 | formname | formidable.js:13:14:13:21 | formname |
|
||||
| formidable.js:12:22:12:29 | formname | formidable.js:13:14:13:21 | formname |
|
||||
| formidable.js:12:32:12:35 | file | formidable.js:13:24:13:27 | file |
|
||||
| formidable.js:12:32:12:35 | file | formidable.js:13:24:13:27 | file |
|
||||
| formidable.js:12:32:12:35 | file | formidable.js:13:24:13:27 | file |
|
||||
| formidable.js:12:32:12:35 | file | formidable.js:13:24:13:27 | file |
|
||||
| formidable.js:15:23:15:31 | fieldName | formidable.js:16:14:16:22 | fieldName |
|
||||
| formidable.js:15:23:15:31 | fieldName | formidable.js:16:14:16:22 | fieldName |
|
||||
| formidable.js:15:23:15:31 | fieldName | formidable.js:16:14:16:22 | fieldName |
|
||||
| formidable.js:15:23:15:31 | fieldName | formidable.js:16:14:16:22 | fieldName |
|
||||
| formidable.js:15:34:15:43 | fieldValue | formidable.js:16:25:16:34 | fieldValue |
|
||||
| formidable.js:15:34:15:43 | fieldValue | formidable.js:16:25:16:34 | fieldValue |
|
||||
| formidable.js:15:34:15:43 | fieldValue | formidable.js:16:25:16:34 | fieldValue |
|
||||
| formidable.js:15:34:15:43 | fieldValue | formidable.js:16:25:16:34 | fieldValue |
|
||||
| multiparty.js:8:22:8:25 | part | multiparty.js:9:14:9:17 | part |
|
||||
| multiparty.js:8:22:8:25 | part | multiparty.js:9:14:9:17 | part |
|
||||
| multiparty.js:8:22:8:25 | part | multiparty.js:9:14:9:17 | part |
|
||||
| multiparty.js:8:22:8:25 | part | multiparty.js:9:14:9:17 | part |
|
||||
| multiparty.js:8:22:8:25 | part | multiparty.js:10:19:10:24 | sink() |
|
||||
| multiparty.js:8:22:8:25 | part | multiparty.js:10:19:10:24 | sink() |
|
||||
| multiparty.js:8:22:8:25 | part | multiparty.js:10:19:10:24 | sink() |
|
||||
| multiparty.js:8:22:8:25 | part | multiparty.js:10:19:10:24 | sink() |
|
||||
| multiparty.js:14:37:14:42 | fields | multiparty.js:15:14:15:19 | fields |
|
||||
| multiparty.js:14:37:14:42 | fields | multiparty.js:15:14:15:19 | fields |
|
||||
| multiparty.js:14:37:14:42 | fields | multiparty.js:15:14:15:19 | fields |
|
||||
| multiparty.js:14:37:14:42 | fields | multiparty.js:15:14:15:19 | fields |
|
||||
| multiparty.js:14:45:14:49 | files | multiparty.js:15:22:15:26 | files |
|
||||
| multiparty.js:14:45:14:49 | files | multiparty.js:15:22:15:26 | files |
|
||||
| multiparty.js:14:45:14:49 | files | multiparty.js:15:22:15:26 | files |
|
||||
| multiparty.js:14:45:14:49 | files | multiparty.js:15:22:15:26 | files |
|
||||
#select
|
||||
| busybus.js:12:18:12:25 | filename | busybus.js:9:36:9:39 | info | busybus.js:12:18:12:25 | filename | This entity depends on a $@. | busybus.js:9:36:9:39 | info | user-provided value |
|
||||
| busybus.js:12:28:12:35 | encoding | busybus.js:9:36:9:39 | info | busybus.js:12:28:12:35 | encoding | This entity depends on a $@. | busybus.js:9:36:9:39 | info | user-provided value |
|
||||
| busybus.js:12:38:12:45 | mimeType | busybus.js:9:36:9:39 | info | busybus.js:12:38:12:45 | mimeType | This entity depends on a $@. | busybus.js:9:36:9:39 | info | user-provided value |
|
||||
| busybus.js:13:31:13:36 | sink() | busybus.js:9:30:9:33 | file | busybus.js:13:31:13:36 | sink() | This entity depends on a $@. | busybus.js:9:30:9:33 | file | user-provided value |
|
||||
| busybus.js:16:22:16:25 | data | busybus.js:15:30:15:33 | data | busybus.js:16:22:16:25 | data | This entity depends on a $@. | busybus.js:15:30:15:33 | data | user-provided value |
|
||||
| busybus.js:23:26:23:29 | data | busybus.js:22:32:22:42 | this.read() | busybus.js:23:26:23:29 | data | This entity depends on a $@. | busybus.js:22:32:22:42 | this.read() | user-provided value |
|
||||
| busybus.js:28:18:28:21 | name | busybus.js:27:25:27:28 | name | busybus.js:28:18:28:21 | name | This entity depends on a $@. | busybus.js:27:25:27:28 | name | user-provided value |
|
||||
| busybus.js:28:24:28:26 | val | busybus.js:27:31:27:33 | val | busybus.js:28:24:28:26 | val | This entity depends on a $@. | busybus.js:27:31:27:33 | val | user-provided value |
|
||||
| busybus.js:28:29:28:32 | info | busybus.js:27:36:27:39 | info | busybus.js:28:29:28:32 | info | This entity depends on a $@. | busybus.js:27:36:27:39 | info | user-provided value |
|
||||
| dicer.js:13:19:13:24 | sink() | dicer.js:12:23:12:26 | part | dicer.js:13:19:13:24 | sink() | This entity depends on a $@. | dicer.js:12:23:12:26 | part | user-provided value |
|
||||
| dicer.js:16:22:16:30 | header[h] | dicer.js:14:28:14:33 | header | dicer.js:16:22:16:30 | header[h] | This entity depends on a $@. | dicer.js:14:28:14:33 | header | user-provided value |
|
||||
| dicer.js:20:18:20:21 | data | dicer.js:19:26:19:29 | data | dicer.js:20:18:20:21 | data | This entity depends on a $@. | dicer.js:19:26:19:29 | data | user-provided value |
|
||||
| formidable.js:8:10:8:15 | fields | formidable.js:7:35:7:49 | form.parse(req) | formidable.js:8:10:8:15 | fields | This entity depends on a $@. | formidable.js:7:35:7:49 | form.parse(req) | user-provided value |
|
||||
| formidable.js:8:18:8:22 | files | formidable.js:7:35:7:49 | form.parse(req) | formidable.js:8:18:8:22 | files | This entity depends on a $@. | formidable.js:7:35:7:49 | form.parse(req) | user-provided value |
|
||||
| formidable.js:10:14:10:21 | formname | formidable.js:9:27:9:34 | formname | formidable.js:10:14:10:21 | formname | This entity depends on a $@. | formidable.js:9:27:9:34 | formname | user-provided value |
|
||||
| formidable.js:10:24:10:27 | file | formidable.js:9:37:9:40 | file | formidable.js:10:24:10:27 | file | This entity depends on a $@. | formidable.js:9:37:9:40 | file | user-provided value |
|
||||
| formidable.js:13:14:13:21 | formname | formidable.js:12:22:12:29 | formname | formidable.js:13:14:13:21 | formname | This entity depends on a $@. | formidable.js:12:22:12:29 | formname | user-provided value |
|
||||
| formidable.js:13:24:13:27 | file | formidable.js:12:32:12:35 | file | formidable.js:13:24:13:27 | file | This entity depends on a $@. | formidable.js:12:32:12:35 | file | user-provided value |
|
||||
| formidable.js:16:14:16:22 | fieldName | formidable.js:15:23:15:31 | fieldName | formidable.js:16:14:16:22 | fieldName | This entity depends on a $@. | formidable.js:15:23:15:31 | fieldName | user-provided value |
|
||||
| formidable.js:16:25:16:34 | fieldValue | formidable.js:15:34:15:43 | fieldValue | formidable.js:16:25:16:34 | fieldValue | This entity depends on a $@. | formidable.js:15:34:15:43 | fieldValue | user-provided value |
|
||||
| multiparty.js:9:14:9:17 | part | multiparty.js:8:22:8:25 | part | multiparty.js:9:14:9:17 | part | This entity depends on a $@. | multiparty.js:8:22:8:25 | part | user-provided value |
|
||||
| multiparty.js:10:19:10:24 | sink() | multiparty.js:8:22:8:25 | part | multiparty.js:10:19:10:24 | sink() | This entity depends on a $@. | multiparty.js:8:22:8:25 | part | user-provided value |
|
||||
| multiparty.js:15:14:15:19 | fields | multiparty.js:14:37:14:42 | fields | multiparty.js:15:14:15:19 | fields | This entity depends on a $@. | multiparty.js:14:37:14:42 | fields | user-provided value |
|
||||
| multiparty.js:15:22:15:26 | files | multiparty.js:14:45:14:49 | files | multiparty.js:15:22:15:26 | files | This entity depends on a $@. | multiparty.js:14:45:14:49 | files | user-provided value |
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* @name Remote Form Flow Sources
|
||||
* @description Using remote user controlled sources from Forms
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 5
|
||||
* @precision high
|
||||
* @id js/remote-flow-source
|
||||
* @tags correctness
|
||||
* security
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import DataFlow::PathGraph
|
||||
import experimental.semmle.javascript.FormParsers
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for test
|
||||
*/
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "RemoteFlowSourcesOUserForm" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink = API::moduleImport("sink").getAParameter().asSink() or
|
||||
sink = API::moduleImport("sink").getReturn().asSource()
|
||||
}
|
||||
}
|
||||
|
||||
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "This entity depends on a $@.", source.getNode(),
|
||||
"user-provided value"
|
||||
33
javascript/ql/test/experimental/FormParsers/busybus.js
Normal file
33
javascript/ql/test/experimental/FormParsers/busybus.js
Normal file
@@ -0,0 +1,33 @@
|
||||
const http = require('http');
|
||||
const zlib = require('node:zlib');
|
||||
const busboy = require('busboy');
|
||||
const sink = require('sink');
|
||||
|
||||
http.createServer((req, res) => {
|
||||
if (req.method === 'POST') {
|
||||
const bb = busboy({ headers: req.headers });
|
||||
bb.on('file', (name, file, info) => {
|
||||
const { filename, encoding, mimeType } = info;
|
||||
const z = zlib.createGzip();
|
||||
sink(filename, encoding, mimeType) // sink
|
||||
file.pipe(z).pipe(sink())
|
||||
|
||||
file.on('data', (data) => {
|
||||
sink(data)
|
||||
})
|
||||
|
||||
file.on('readable', function () {
|
||||
// There is some data to read now.
|
||||
let data;
|
||||
while ((data = this.read()) !== null) {
|
||||
sink(data)
|
||||
}
|
||||
});
|
||||
});
|
||||
bb.on('field', (name, val, info) => {
|
||||
sink(name, val, info)
|
||||
});
|
||||
}
|
||||
}).listen(8000, () => {
|
||||
console.log('Listening for requests');
|
||||
});
|
||||
25
javascript/ql/test/experimental/FormParsers/dicer.js
Normal file
25
javascript/ql/test/experimental/FormParsers/dicer.js
Normal file
@@ -0,0 +1,25 @@
|
||||
const { inspect } = require('util');
|
||||
const http = require('http');
|
||||
const Dicer = require('dicer');
|
||||
const sink = require('sink');
|
||||
|
||||
const PORT = 8080;
|
||||
|
||||
http.createServer((req, res) => {
|
||||
let m;
|
||||
const dicer = new Dicer({ boundary: m[1] || m[2] });
|
||||
|
||||
dicer.on('part', (part) => {
|
||||
part.pipe(sink())
|
||||
part.on('header', (header) => {
|
||||
for (h in header) {
|
||||
sink(header[h])
|
||||
}
|
||||
});
|
||||
part.on('data', (data) => {
|
||||
sink(data)
|
||||
});
|
||||
});
|
||||
}).listen(PORT, () => {
|
||||
console.log(`Listening for requests on port ${PORT}`);
|
||||
});
|
||||
22
javascript/ql/test/experimental/FormParsers/formidable.js
Normal file
22
javascript/ql/test/experimental/FormParsers/formidable.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import http from 'node:http';
|
||||
import formidable from 'formidable';
|
||||
const sink = require('sink');
|
||||
|
||||
const server = http.createServer(async (req, res) => {
|
||||
const form = formidable({});
|
||||
const [fields, files] = await form.parse(req);
|
||||
sink(fields, files)
|
||||
form.on('fileBegin', (formname, file) => {
|
||||
sink(formname, file)
|
||||
});
|
||||
form.on('file', (formname, file) => {
|
||||
sink(formname, file)
|
||||
});
|
||||
form.on('field', (fieldName, fieldValue) => {
|
||||
sink(fieldName, fieldValue)
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(8080, () => {
|
||||
console.log('Server listening on http://localhost:8080/ ...');
|
||||
});
|
||||
19
javascript/ql/test/experimental/FormParsers/multiparty.js
Normal file
19
javascript/ql/test/experimental/FormParsers/multiparty.js
Normal file
@@ -0,0 +1,19 @@
|
||||
var multiparty = require('multiparty');
|
||||
var http = require('http');
|
||||
var util = require('util');
|
||||
const sink = require('sink');
|
||||
|
||||
http.createServer(function (req, res) {
|
||||
var form = new multiparty.Form();
|
||||
form.on('part', (part) => {
|
||||
sink(part)
|
||||
part.pipe(sink())
|
||||
});
|
||||
|
||||
var form2 = new multiparty.Form();
|
||||
form2.parse(req, function (err, fields, files) {
|
||||
sink(fields, files)
|
||||
});
|
||||
form2.parse(req);
|
||||
|
||||
}).listen(8080);
|
||||
@@ -0,0 +1,51 @@
|
||||
nodes
|
||||
| test.js:5:11:5:44 | payload |
|
||||
| test.js:5:21:5:44 | req.que ... rameter |
|
||||
| test.js:5:21:5:44 | req.que ... rameter |
|
||||
| test.js:6:9:6:43 | payloadURL |
|
||||
| test.js:6:22:6:43 | new URL ... + sth) |
|
||||
| test.js:6:30:6:36 | payload |
|
||||
| test.js:6:30:6:42 | payload + sth |
|
||||
| test.js:7:16:7:25 | payloadURL |
|
||||
| test.js:7:16:7:25 | payloadURL |
|
||||
| test.js:9:5:9:39 | payloadURL |
|
||||
| test.js:9:18:9:39 | new URL ... + sth) |
|
||||
| test.js:9:26:9:32 | payload |
|
||||
| test.js:9:26:9:38 | payload + sth |
|
||||
| test.js:10:16:10:25 | payloadURL |
|
||||
| test.js:10:16:10:25 | payloadURL |
|
||||
| test.js:17:11:17:44 | payload |
|
||||
| test.js:17:21:17:44 | req.que ... rameter |
|
||||
| test.js:17:21:17:44 | req.que ... rameter |
|
||||
| test.js:18:18:18:24 | payload |
|
||||
| test.js:18:18:18:24 | payload |
|
||||
| test.js:19:18:19:24 | payload |
|
||||
| test.js:19:18:19:30 | payload + sth |
|
||||
| test.js:19:18:19:30 | payload + sth |
|
||||
edges
|
||||
| test.js:5:11:5:44 | payload | test.js:6:30:6:36 | payload |
|
||||
| test.js:5:11:5:44 | payload | test.js:9:26:9:32 | payload |
|
||||
| test.js:5:21:5:44 | req.que ... rameter | test.js:5:11:5:44 | payload |
|
||||
| test.js:5:21:5:44 | req.que ... rameter | test.js:5:11:5:44 | payload |
|
||||
| test.js:6:9:6:43 | payloadURL | test.js:7:16:7:25 | payloadURL |
|
||||
| test.js:6:9:6:43 | payloadURL | test.js:7:16:7:25 | payloadURL |
|
||||
| test.js:6:22:6:43 | new URL ... + sth) | test.js:6:9:6:43 | payloadURL |
|
||||
| test.js:6:30:6:36 | payload | test.js:6:30:6:42 | payload + sth |
|
||||
| test.js:6:30:6:42 | payload + sth | test.js:6:22:6:43 | new URL ... + sth) |
|
||||
| test.js:9:5:9:39 | payloadURL | test.js:10:16:10:25 | payloadURL |
|
||||
| test.js:9:5:9:39 | payloadURL | test.js:10:16:10:25 | payloadURL |
|
||||
| test.js:9:18:9:39 | new URL ... + sth) | test.js:9:5:9:39 | payloadURL |
|
||||
| test.js:9:26:9:32 | payload | test.js:9:26:9:38 | payload + sth |
|
||||
| test.js:9:26:9:38 | payload + sth | test.js:9:18:9:39 | new URL ... + sth) |
|
||||
| test.js:17:11:17:44 | payload | test.js:18:18:18:24 | payload |
|
||||
| test.js:17:11:17:44 | payload | test.js:18:18:18:24 | payload |
|
||||
| test.js:17:11:17:44 | payload | test.js:19:18:19:24 | payload |
|
||||
| test.js:17:21:17:44 | req.que ... rameter | test.js:17:11:17:44 | payload |
|
||||
| test.js:17:21:17:44 | req.que ... rameter | test.js:17:11:17:44 | payload |
|
||||
| test.js:19:18:19:24 | payload | test.js:19:18:19:30 | payload + sth |
|
||||
| test.js:19:18:19:24 | payload | test.js:19:18:19:30 | payload + sth |
|
||||
#select
|
||||
| test.js:7:16:7:25 | payloadURL | test.js:5:21:5:44 | req.que ... rameter | test.js:7:16:7:25 | payloadURL | This command line depends on a $@. | test.js:5:21:5:44 | req.que ... rameter | user-provided value |
|
||||
| test.js:10:16:10:25 | payloadURL | test.js:5:21:5:44 | req.que ... rameter | test.js:10:16:10:25 | payloadURL | This command line depends on a $@. | test.js:5:21:5:44 | req.que ... rameter | user-provided value |
|
||||
| test.js:18:18:18:24 | payload | test.js:17:21:17:44 | req.que ... rameter | test.js:18:18:18:24 | payload | This command line depends on a $@. | test.js:17:21:17:44 | req.que ... rameter | user-provided value |
|
||||
| test.js:19:18:19:30 | payload + sth | test.js:17:21:17:44 | req.que ... rameter | test.js:19:18:19:30 | payload + sth | This command line depends on a $@. | test.js:17:21:17:44 | req.que ... rameter | user-provided value |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE-094-dataURL/CodeInjection.ql
|
||||
@@ -0,0 +1,22 @@
|
||||
const { Worker } = require('node:worker_threads');
|
||||
var app = require('express')();
|
||||
|
||||
app.post('/path', async function (req, res) {
|
||||
const payload = req.query.queryParameter // like: payload = 'data:text/javascript,console.log("hello!");//'
|
||||
let payloadURL = new URL(payload + sth) // NOT OK
|
||||
new Worker(payloadURL);
|
||||
|
||||
payloadURL = new URL(payload + sth) // NOT OK
|
||||
new Worker(payloadURL);
|
||||
|
||||
payloadURL = new URL(sth + payload) // OK
|
||||
new Worker(payloadURL);
|
||||
});
|
||||
|
||||
app.post('/path2', async function (req, res) {
|
||||
const payload = req.query.queryParameter // like: payload = 'data:text/javascript,console.log("hello!");//'
|
||||
await import(payload) // NOT OK
|
||||
await import(payload + sth) // NOT OK
|
||||
await import(sth + payload) // OK
|
||||
});
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
nodes
|
||||
| test.js:5:9:5:28 | { EnvValue, EnvKey } |
|
||||
| test.js:5:9:5:39 | EnvKey |
|
||||
| test.js:5:9:5:39 | EnvValue |
|
||||
| test.js:5:11:5:18 | EnvValue |
|
||||
| test.js:5:21:5:26 | EnvKey |
|
||||
| test.js:5:32:5:39 | req.body |
|
||||
| test.js:5:32:5:39 | req.body |
|
||||
| test.js:6:15:6:20 | EnvKey |
|
||||
| test.js:6:15:6:20 | EnvKey |
|
||||
| test.js:6:25:6:32 | EnvValue |
|
||||
| test.js:6:25:6:32 | EnvValue |
|
||||
| test.js:7:15:7:20 | EnvKey |
|
||||
| test.js:7:15:7:20 | EnvKey |
|
||||
| test.js:7:25:7:32 | EnvValue |
|
||||
| test.js:7:25:7:32 | EnvValue |
|
||||
| test.js:13:9:13:28 | { EnvValue, EnvKey } |
|
||||
| test.js:13:9:13:39 | EnvKey |
|
||||
| test.js:13:9:13:39 | EnvValue |
|
||||
| test.js:13:11:13:18 | EnvValue |
|
||||
| test.js:13:21:13:26 | EnvKey |
|
||||
| test.js:13:32:13:39 | req.body |
|
||||
| test.js:13:32:13:39 | req.body |
|
||||
| test.js:15:15:15:20 | EnvKey |
|
||||
| test.js:15:15:15:20 | EnvKey |
|
||||
| test.js:16:26:16:33 | EnvValue |
|
||||
| test.js:16:26:16:33 | EnvValue |
|
||||
edges
|
||||
| test.js:5:9:5:28 | { EnvValue, EnvKey } | test.js:5:11:5:18 | EnvValue |
|
||||
| test.js:5:9:5:28 | { EnvValue, EnvKey } | test.js:5:21:5:26 | EnvKey |
|
||||
| test.js:5:9:5:39 | EnvKey | test.js:6:15:6:20 | EnvKey |
|
||||
| test.js:5:9:5:39 | EnvKey | test.js:6:15:6:20 | EnvKey |
|
||||
| test.js:5:9:5:39 | EnvKey | test.js:7:15:7:20 | EnvKey |
|
||||
| test.js:5:9:5:39 | EnvKey | test.js:7:15:7:20 | EnvKey |
|
||||
| test.js:5:9:5:39 | EnvValue | test.js:6:25:6:32 | EnvValue |
|
||||
| test.js:5:9:5:39 | EnvValue | test.js:6:25:6:32 | EnvValue |
|
||||
| test.js:5:9:5:39 | EnvValue | test.js:7:25:7:32 | EnvValue |
|
||||
| test.js:5:9:5:39 | EnvValue | test.js:7:25:7:32 | EnvValue |
|
||||
| test.js:5:11:5:18 | EnvValue | test.js:5:9:5:39 | EnvValue |
|
||||
| test.js:5:21:5:26 | EnvKey | test.js:5:9:5:39 | EnvKey |
|
||||
| test.js:5:32:5:39 | req.body | test.js:5:9:5:28 | { EnvValue, EnvKey } |
|
||||
| test.js:5:32:5:39 | req.body | test.js:5:9:5:28 | { EnvValue, EnvKey } |
|
||||
| test.js:13:9:13:28 | { EnvValue, EnvKey } | test.js:13:11:13:18 | EnvValue |
|
||||
| test.js:13:9:13:28 | { EnvValue, EnvKey } | test.js:13:21:13:26 | EnvKey |
|
||||
| test.js:13:9:13:39 | EnvKey | test.js:15:15:15:20 | EnvKey |
|
||||
| test.js:13:9:13:39 | EnvKey | test.js:15:15:15:20 | EnvKey |
|
||||
| test.js:13:9:13:39 | EnvValue | test.js:16:26:16:33 | EnvValue |
|
||||
| test.js:13:9:13:39 | EnvValue | test.js:16:26:16:33 | EnvValue |
|
||||
| test.js:13:11:13:18 | EnvValue | test.js:13:9:13:39 | EnvValue |
|
||||
| test.js:13:21:13:26 | EnvKey | test.js:13:9:13:39 | EnvKey |
|
||||
| test.js:13:32:13:39 | req.body | test.js:13:9:13:28 | { EnvValue, EnvKey } |
|
||||
| test.js:13:32:13:39 | req.body | test.js:13:9:13:28 | { EnvValue, EnvKey } |
|
||||
#select
|
||||
| test.js:6:15:6:20 | EnvKey | test.js:5:32:5:39 | req.body | test.js:6:15:6:20 | EnvKey | arbitrary environment variable assignment from this $@. | test.js:5:32:5:39 | req.body | user controllable source |
|
||||
| test.js:7:15:7:20 | EnvKey | test.js:5:32:5:39 | req.body | test.js:7:15:7:20 | EnvKey | arbitrary environment variable assignment from this $@. | test.js:5:32:5:39 | req.body | user controllable source |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE-099/EnvValueAndKeyInjection.ql
|
||||
@@ -0,0 +1,19 @@
|
||||
const http = require('node:http');
|
||||
|
||||
|
||||
http.createServer((req, res) => {
|
||||
const { EnvValue, EnvKey } = req.body;
|
||||
process.env[EnvKey] = EnvValue; // NOT OK
|
||||
process.env[EnvKey] = EnvValue; // NOT OK
|
||||
|
||||
res.end('env has been injected!');
|
||||
});
|
||||
|
||||
http.createServer((req, res) => {
|
||||
const { EnvValue, EnvKey } = req.body;
|
||||
|
||||
process.env[EnvKey] = "constant" // OK
|
||||
process.env.constant = EnvValue // OK
|
||||
|
||||
res.end('env has been injected!');
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
nodes
|
||||
| test.js:4:9:4:20 | { EnvValue } |
|
||||
| test.js:4:9:4:31 | EnvValue |
|
||||
| test.js:4:11:4:18 | EnvValue |
|
||||
| test.js:4:24:4:31 | req.body |
|
||||
| test.js:4:24:4:31 | req.body |
|
||||
| test.js:5:35:5:42 | EnvValue |
|
||||
| test.js:5:35:5:42 | EnvValue |
|
||||
| test.js:6:23:6:30 | EnvValue |
|
||||
| test.js:6:23:6:30 | EnvValue |
|
||||
| test.js:7:22:7:29 | EnvValue |
|
||||
| test.js:7:22:7:29 | EnvValue |
|
||||
edges
|
||||
| test.js:4:9:4:20 | { EnvValue } | test.js:4:11:4:18 | EnvValue |
|
||||
| test.js:4:9:4:31 | EnvValue | test.js:5:35:5:42 | EnvValue |
|
||||
| test.js:4:9:4:31 | EnvValue | test.js:5:35:5:42 | EnvValue |
|
||||
| test.js:4:9:4:31 | EnvValue | test.js:6:23:6:30 | EnvValue |
|
||||
| test.js:4:9:4:31 | EnvValue | test.js:6:23:6:30 | EnvValue |
|
||||
| test.js:4:9:4:31 | EnvValue | test.js:7:22:7:29 | EnvValue |
|
||||
| test.js:4:9:4:31 | EnvValue | test.js:7:22:7:29 | EnvValue |
|
||||
| test.js:4:11:4:18 | EnvValue | test.js:4:9:4:31 | EnvValue |
|
||||
| test.js:4:24:4:31 | req.body | test.js:4:9:4:20 | { EnvValue } |
|
||||
| test.js:4:24:4:31 | req.body | test.js:4:9:4:20 | { EnvValue } |
|
||||
#select
|
||||
| test.js:5:35:5:42 | EnvValue | test.js:4:24:4:31 | req.body | test.js:5:35:5:42 | EnvValue | this environment variable assignment is $@. | test.js:4:24:4:31 | req.body | user controllable |
|
||||
| test.js:6:23:6:30 | EnvValue | test.js:4:24:4:31 | req.body | test.js:6:23:6:30 | EnvValue | this environment variable assignment is $@. | test.js:4:24:4:31 | req.body | user controllable |
|
||||
| test.js:7:22:7:29 | EnvValue | test.js:4:24:4:31 | req.body | test.js:7:22:7:29 | EnvValue | this environment variable assignment is $@. | test.js:4:24:4:31 | req.body | user controllable |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE-099/EnvValueInjection.ql
|
||||
@@ -0,0 +1,10 @@
|
||||
const http = require('node:http');
|
||||
|
||||
http.createServer((req, res) => {
|
||||
const { EnvValue } = req.body;
|
||||
process.env["A_Critical_Env"] = EnvValue; // NOT OK
|
||||
process.env[AKey] = EnvValue; // NOT OK
|
||||
process.env.AKey = EnvValue; // NOT OK
|
||||
|
||||
res.end('env has been injected!');
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
const express = require('express')
|
||||
const jwtJsonwebtoken = require('jsonwebtoken');
|
||||
|
||||
function getSecret() {
|
||||
return "A Safe generated random key"
|
||||
}
|
||||
|
||||
function aJWT() {
|
||||
return "A JWT provided by user"
|
||||
}
|
||||
|
||||
(function () {
|
||||
const UserToken = aJwt()
|
||||
|
||||
// BAD: no signature verification
|
||||
jwtJsonwebtoken.decode(UserToken) // NOT OK
|
||||
})();
|
||||
|
||||
(function () {
|
||||
const UserToken = aJwt()
|
||||
|
||||
// BAD: no signature verification
|
||||
jwtJsonwebtoken.decode(UserToken) // NOT OK
|
||||
jwtJsonwebtoken.verify(UserToken, getSecret(), { algorithms: ["HS256", "none"] }) // NOT OK
|
||||
})();
|
||||
|
||||
(function () {
|
||||
const UserToken = aJwt()
|
||||
|
||||
// GOOD: with signature verification
|
||||
jwtJsonwebtoken.verify(UserToken, getSecret()) // OK
|
||||
})();
|
||||
|
||||
(function () {
|
||||
const UserToken = aJwt()
|
||||
|
||||
// GOOD: first without signature verification then with signature verification for same UserToken
|
||||
jwtJsonwebtoken.decode(UserToken) // OK
|
||||
jwtJsonwebtoken.verify(UserToken, getSecret()) // OK
|
||||
})();
|
||||
|
||||
(function () {
|
||||
const UserToken = aJwt()
|
||||
|
||||
// GOOD: first without signature verification then with signature verification for same UserToken
|
||||
jwtJsonwebtoken.decode(UserToken) // OK
|
||||
jwtJsonwebtoken.verify(UserToken, getSecret(), { algorithms: ["HS256"] }) // OK
|
||||
})();
|
||||
@@ -0,0 +1,141 @@
|
||||
nodes
|
||||
| JsonWebToken.js:13:11:13:28 | UserToken |
|
||||
| JsonWebToken.js:13:23:13:28 | aJwt() |
|
||||
| JsonWebToken.js:13:23:13:28 | aJwt() |
|
||||
| JsonWebToken.js:16:28:16:36 | UserToken |
|
||||
| JsonWebToken.js:16:28:16:36 | UserToken |
|
||||
| JsonWebToken.js:20:11:20:28 | UserToken |
|
||||
| JsonWebToken.js:20:23:20:28 | aJwt() |
|
||||
| JsonWebToken.js:20:23:20:28 | aJwt() |
|
||||
| JsonWebToken.js:23:28:23:36 | UserToken |
|
||||
| JsonWebToken.js:23:28:23:36 | UserToken |
|
||||
| JsonWebToken.js:24:28:24:36 | UserToken |
|
||||
| JsonWebToken.js:24:28:24:36 | UserToken |
|
||||
| JsonWebToken.js:28:11:28:28 | UserToken |
|
||||
| JsonWebToken.js:28:23:28:28 | aJwt() |
|
||||
| JsonWebToken.js:28:23:28:28 | aJwt() |
|
||||
| JsonWebToken.js:31:28:31:36 | UserToken |
|
||||
| JsonWebToken.js:31:28:31:36 | UserToken |
|
||||
| JsonWebToken.js:35:11:35:28 | UserToken |
|
||||
| JsonWebToken.js:35:23:35:28 | aJwt() |
|
||||
| JsonWebToken.js:35:23:35:28 | aJwt() |
|
||||
| JsonWebToken.js:38:28:38:36 | UserToken |
|
||||
| JsonWebToken.js:38:28:38:36 | UserToken |
|
||||
| JsonWebToken.js:39:28:39:36 | UserToken |
|
||||
| JsonWebToken.js:39:28:39:36 | UserToken |
|
||||
| JsonWebToken.js:43:11:43:28 | UserToken |
|
||||
| JsonWebToken.js:43:23:43:28 | aJwt() |
|
||||
| JsonWebToken.js:43:23:43:28 | aJwt() |
|
||||
| JsonWebToken.js:46:28:46:36 | UserToken |
|
||||
| JsonWebToken.js:46:28:46:36 | UserToken |
|
||||
| JsonWebToken.js:47:28:47:36 | UserToken |
|
||||
| JsonWebToken.js:47:28:47:36 | UserToken |
|
||||
| jose.js:12:11:12:28 | UserToken |
|
||||
| jose.js:12:23:12:28 | aJwt() |
|
||||
| jose.js:12:23:12:28 | aJwt() |
|
||||
| jose.js:15:20:15:28 | UserToken |
|
||||
| jose.js:15:20:15:28 | UserToken |
|
||||
| jose.js:19:11:19:28 | UserToken |
|
||||
| jose.js:19:23:19:28 | aJwt() |
|
||||
| jose.js:19:23:19:28 | aJwt() |
|
||||
| jose.js:22:20:22:28 | UserToken |
|
||||
| jose.js:22:20:22:28 | UserToken |
|
||||
| jose.js:23:26:23:34 | UserToken |
|
||||
| jose.js:23:26:23:34 | UserToken |
|
||||
| jose.js:27:11:27:28 | UserToken |
|
||||
| jose.js:27:23:27:28 | aJwt() |
|
||||
| jose.js:27:23:27:28 | aJwt() |
|
||||
| jose.js:30:26:30:34 | UserToken |
|
||||
| jose.js:30:26:30:34 | UserToken |
|
||||
| jwtDecode.js:13:11:13:28 | UserToken |
|
||||
| jwtDecode.js:13:23:13:28 | aJwt() |
|
||||
| jwtDecode.js:13:23:13:28 | aJwt() |
|
||||
| jwtDecode.js:17:16:17:24 | UserToken |
|
||||
| jwtDecode.js:17:16:17:24 | UserToken |
|
||||
| jwtSimple.js:13:11:13:28 | UserToken |
|
||||
| jwtSimple.js:13:23:13:28 | aJwt() |
|
||||
| jwtSimple.js:13:23:13:28 | aJwt() |
|
||||
| jwtSimple.js:16:23:16:31 | UserToken |
|
||||
| jwtSimple.js:16:23:16:31 | UserToken |
|
||||
| jwtSimple.js:20:11:20:28 | UserToken |
|
||||
| jwtSimple.js:20:23:20:28 | aJwt() |
|
||||
| jwtSimple.js:20:23:20:28 | aJwt() |
|
||||
| jwtSimple.js:23:23:23:31 | UserToken |
|
||||
| jwtSimple.js:23:23:23:31 | UserToken |
|
||||
| jwtSimple.js:24:23:24:31 | UserToken |
|
||||
| jwtSimple.js:24:23:24:31 | UserToken |
|
||||
| jwtSimple.js:28:11:28:28 | UserToken |
|
||||
| jwtSimple.js:28:23:28:28 | aJwt() |
|
||||
| jwtSimple.js:28:23:28:28 | aJwt() |
|
||||
| jwtSimple.js:31:23:31:31 | UserToken |
|
||||
| jwtSimple.js:31:23:31:31 | UserToken |
|
||||
| jwtSimple.js:32:23:32:31 | UserToken |
|
||||
| jwtSimple.js:32:23:32:31 | UserToken |
|
||||
edges
|
||||
| JsonWebToken.js:13:11:13:28 | UserToken | JsonWebToken.js:16:28:16:36 | UserToken |
|
||||
| JsonWebToken.js:13:11:13:28 | UserToken | JsonWebToken.js:16:28:16:36 | UserToken |
|
||||
| JsonWebToken.js:13:23:13:28 | aJwt() | JsonWebToken.js:13:11:13:28 | UserToken |
|
||||
| JsonWebToken.js:13:23:13:28 | aJwt() | JsonWebToken.js:13:11:13:28 | UserToken |
|
||||
| JsonWebToken.js:20:11:20:28 | UserToken | JsonWebToken.js:23:28:23:36 | UserToken |
|
||||
| JsonWebToken.js:20:11:20:28 | UserToken | JsonWebToken.js:23:28:23:36 | UserToken |
|
||||
| JsonWebToken.js:20:11:20:28 | UserToken | JsonWebToken.js:24:28:24:36 | UserToken |
|
||||
| JsonWebToken.js:20:11:20:28 | UserToken | JsonWebToken.js:24:28:24:36 | UserToken |
|
||||
| JsonWebToken.js:20:23:20:28 | aJwt() | JsonWebToken.js:20:11:20:28 | UserToken |
|
||||
| JsonWebToken.js:20:23:20:28 | aJwt() | JsonWebToken.js:20:11:20:28 | UserToken |
|
||||
| JsonWebToken.js:28:11:28:28 | UserToken | JsonWebToken.js:31:28:31:36 | UserToken |
|
||||
| JsonWebToken.js:28:11:28:28 | UserToken | JsonWebToken.js:31:28:31:36 | UserToken |
|
||||
| JsonWebToken.js:28:23:28:28 | aJwt() | JsonWebToken.js:28:11:28:28 | UserToken |
|
||||
| JsonWebToken.js:28:23:28:28 | aJwt() | JsonWebToken.js:28:11:28:28 | UserToken |
|
||||
| JsonWebToken.js:35:11:35:28 | UserToken | JsonWebToken.js:38:28:38:36 | UserToken |
|
||||
| JsonWebToken.js:35:11:35:28 | UserToken | JsonWebToken.js:38:28:38:36 | UserToken |
|
||||
| JsonWebToken.js:35:11:35:28 | UserToken | JsonWebToken.js:39:28:39:36 | UserToken |
|
||||
| JsonWebToken.js:35:11:35:28 | UserToken | JsonWebToken.js:39:28:39:36 | UserToken |
|
||||
| JsonWebToken.js:35:23:35:28 | aJwt() | JsonWebToken.js:35:11:35:28 | UserToken |
|
||||
| JsonWebToken.js:35:23:35:28 | aJwt() | JsonWebToken.js:35:11:35:28 | UserToken |
|
||||
| JsonWebToken.js:43:11:43:28 | UserToken | JsonWebToken.js:46:28:46:36 | UserToken |
|
||||
| JsonWebToken.js:43:11:43:28 | UserToken | JsonWebToken.js:46:28:46:36 | UserToken |
|
||||
| JsonWebToken.js:43:11:43:28 | UserToken | JsonWebToken.js:47:28:47:36 | UserToken |
|
||||
| JsonWebToken.js:43:11:43:28 | UserToken | JsonWebToken.js:47:28:47:36 | UserToken |
|
||||
| JsonWebToken.js:43:23:43:28 | aJwt() | JsonWebToken.js:43:11:43:28 | UserToken |
|
||||
| JsonWebToken.js:43:23:43:28 | aJwt() | JsonWebToken.js:43:11:43:28 | UserToken |
|
||||
| jose.js:12:11:12:28 | UserToken | jose.js:15:20:15:28 | UserToken |
|
||||
| jose.js:12:11:12:28 | UserToken | jose.js:15:20:15:28 | UserToken |
|
||||
| jose.js:12:23:12:28 | aJwt() | jose.js:12:11:12:28 | UserToken |
|
||||
| jose.js:12:23:12:28 | aJwt() | jose.js:12:11:12:28 | UserToken |
|
||||
| jose.js:19:11:19:28 | UserToken | jose.js:22:20:22:28 | UserToken |
|
||||
| jose.js:19:11:19:28 | UserToken | jose.js:22:20:22:28 | UserToken |
|
||||
| jose.js:19:11:19:28 | UserToken | jose.js:23:26:23:34 | UserToken |
|
||||
| jose.js:19:11:19:28 | UserToken | jose.js:23:26:23:34 | UserToken |
|
||||
| jose.js:19:23:19:28 | aJwt() | jose.js:19:11:19:28 | UserToken |
|
||||
| jose.js:19:23:19:28 | aJwt() | jose.js:19:11:19:28 | UserToken |
|
||||
| jose.js:27:11:27:28 | UserToken | jose.js:30:26:30:34 | UserToken |
|
||||
| jose.js:27:11:27:28 | UserToken | jose.js:30:26:30:34 | UserToken |
|
||||
| jose.js:27:23:27:28 | aJwt() | jose.js:27:11:27:28 | UserToken |
|
||||
| jose.js:27:23:27:28 | aJwt() | jose.js:27:11:27:28 | UserToken |
|
||||
| jwtDecode.js:13:11:13:28 | UserToken | jwtDecode.js:17:16:17:24 | UserToken |
|
||||
| jwtDecode.js:13:11:13:28 | UserToken | jwtDecode.js:17:16:17:24 | UserToken |
|
||||
| jwtDecode.js:13:23:13:28 | aJwt() | jwtDecode.js:13:11:13:28 | UserToken |
|
||||
| jwtDecode.js:13:23:13:28 | aJwt() | jwtDecode.js:13:11:13:28 | UserToken |
|
||||
| jwtSimple.js:13:11:13:28 | UserToken | jwtSimple.js:16:23:16:31 | UserToken |
|
||||
| jwtSimple.js:13:11:13:28 | UserToken | jwtSimple.js:16:23:16:31 | UserToken |
|
||||
| jwtSimple.js:13:23:13:28 | aJwt() | jwtSimple.js:13:11:13:28 | UserToken |
|
||||
| jwtSimple.js:13:23:13:28 | aJwt() | jwtSimple.js:13:11:13:28 | UserToken |
|
||||
| jwtSimple.js:20:11:20:28 | UserToken | jwtSimple.js:23:23:23:31 | UserToken |
|
||||
| jwtSimple.js:20:11:20:28 | UserToken | jwtSimple.js:23:23:23:31 | UserToken |
|
||||
| jwtSimple.js:20:11:20:28 | UserToken | jwtSimple.js:24:23:24:31 | UserToken |
|
||||
| jwtSimple.js:20:11:20:28 | UserToken | jwtSimple.js:24:23:24:31 | UserToken |
|
||||
| jwtSimple.js:20:23:20:28 | aJwt() | jwtSimple.js:20:11:20:28 | UserToken |
|
||||
| jwtSimple.js:20:23:20:28 | aJwt() | jwtSimple.js:20:11:20:28 | UserToken |
|
||||
| jwtSimple.js:28:11:28:28 | UserToken | jwtSimple.js:31:23:31:31 | UserToken |
|
||||
| jwtSimple.js:28:11:28:28 | UserToken | jwtSimple.js:31:23:31:31 | UserToken |
|
||||
| jwtSimple.js:28:11:28:28 | UserToken | jwtSimple.js:32:23:32:31 | UserToken |
|
||||
| jwtSimple.js:28:11:28:28 | UserToken | jwtSimple.js:32:23:32:31 | UserToken |
|
||||
| jwtSimple.js:28:23:28:28 | aJwt() | jwtSimple.js:28:11:28:28 | UserToken |
|
||||
| jwtSimple.js:28:23:28:28 | aJwt() | jwtSimple.js:28:11:28:28 | UserToken |
|
||||
#select
|
||||
| JsonWebToken.js:13:23:13:28 | aJwt() | JsonWebToken.js:13:23:13:28 | aJwt() | JsonWebToken.js:16:28:16:36 | UserToken | Decoding JWT $@. | JsonWebToken.js:16:28:16:36 | UserToken | without signature verification |
|
||||
| JsonWebToken.js:20:23:20:28 | aJwt() | JsonWebToken.js:20:23:20:28 | aJwt() | JsonWebToken.js:23:28:23:36 | UserToken | Decoding JWT $@. | JsonWebToken.js:23:28:23:36 | UserToken | without signature verification |
|
||||
| JsonWebToken.js:20:23:20:28 | aJwt() | JsonWebToken.js:20:23:20:28 | aJwt() | JsonWebToken.js:24:28:24:36 | UserToken | Decoding JWT $@. | JsonWebToken.js:24:28:24:36 | UserToken | without signature verification |
|
||||
| jose.js:12:23:12:28 | aJwt() | jose.js:12:23:12:28 | aJwt() | jose.js:15:20:15:28 | UserToken | Decoding JWT $@. | jose.js:15:20:15:28 | UserToken | without signature verification |
|
||||
| jwtDecode.js:13:23:13:28 | aJwt() | jwtDecode.js:13:23:13:28 | aJwt() | jwtDecode.js:17:16:17:24 | UserToken | Decoding JWT $@. | jwtDecode.js:17:16:17:24 | UserToken | without signature verification |
|
||||
| jwtSimple.js:13:23:13:28 | aJwt() | jwtSimple.js:13:23:13:28 | aJwt() | jwtSimple.js:16:23:16:31 | UserToken | Decoding JWT $@. | jwtSimple.js:16:23:16:31 | UserToken | without signature verification |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE-347/decodeJwtWithoutVerificationLocalSource.ql
|
||||
@@ -0,0 +1,31 @@
|
||||
const jose = require('jose')
|
||||
|
||||
function getSecret() {
|
||||
return "A Safe generated random key"
|
||||
}
|
||||
|
||||
function aJWT() {
|
||||
return "A JWT provided by user"
|
||||
}
|
||||
|
||||
(function () {
|
||||
const UserToken = aJwt()
|
||||
|
||||
// no signature verification
|
||||
jose.decodeJwt(UserToken) // NOT OK
|
||||
})();
|
||||
|
||||
(async function () {
|
||||
const UserToken = aJwt()
|
||||
|
||||
// first without signature verification then with signature verification for same UserToken
|
||||
jose.decodeJwt(UserToken) // OK
|
||||
await jose.jwtVerify(UserToken, new TextEncoder().encode(getSecret())) // OK
|
||||
})();
|
||||
|
||||
(async function () {
|
||||
const UserToken = aJwt()
|
||||
|
||||
// with signature verification
|
||||
await jose.jwtVerify(UserToken, new TextEncoder().encode(getSecret())) // OK
|
||||
})();
|
||||
@@ -0,0 +1,18 @@
|
||||
const express = require('express')
|
||||
const jwt_decode = require('jwt-decode');
|
||||
|
||||
function getSecret() {
|
||||
return "A Safe generated random key"
|
||||
}
|
||||
|
||||
function aJWT() {
|
||||
return "A JWT provided by user"
|
||||
}
|
||||
|
||||
(function () {
|
||||
const UserToken = aJwt()
|
||||
|
||||
// jwt-decode
|
||||
// no signature verification
|
||||
jwt_decode(UserToken) // NOT OK
|
||||
})();
|
||||
@@ -0,0 +1,33 @@
|
||||
const express = require('express')
|
||||
const jwt_simple = require('jwt-simple');
|
||||
|
||||
function getSecret() {
|
||||
return "A Safe generated random key"
|
||||
}
|
||||
|
||||
function aJWT() {
|
||||
return "A JWT provided by user"
|
||||
}
|
||||
|
||||
(function () {
|
||||
const UserToken = aJwt()
|
||||
|
||||
// BAD: no signature verification
|
||||
jwt_simple.decode(UserToken, getSecret(), true); // NOT OK
|
||||
})();
|
||||
|
||||
(function () {
|
||||
const UserToken = aJwt()
|
||||
|
||||
// GOOD: all with with signature verification
|
||||
jwt_simple.decode(UserToken, getSecret(), false); // OK
|
||||
jwt_simple.decode(UserToken, getSecret()); // OK
|
||||
})();
|
||||
|
||||
(function () {
|
||||
const UserToken = aJwt()
|
||||
|
||||
// GOOD: first without signature verification then with signature verification for same UserToken
|
||||
jwt_simple.decode(UserToken, getSecret(), true); // OK
|
||||
jwt_simple.decode(UserToken, getSecret()); // OK
|
||||
})();
|
||||
@@ -0,0 +1,49 @@
|
||||
const express = require('express')
|
||||
const app = express()
|
||||
const jwtJsonwebtoken = require('jsonwebtoken');
|
||||
const port = 3000
|
||||
|
||||
function getSecret() {
|
||||
return "A Safe generated random key"
|
||||
}
|
||||
app.get('/jwtJsonwebtoken1', (req, res) => {
|
||||
const UserToken = req.headers.authorization;
|
||||
|
||||
// BAD: no signature verification
|
||||
jwtJsonwebtoken.decode(UserToken) // NOT OK
|
||||
})
|
||||
|
||||
app.get('/jwtJsonwebtoken2', (req, res) => {
|
||||
const UserToken = req.headers.authorization;
|
||||
|
||||
// BAD: no signature verification
|
||||
jwtJsonwebtoken.decode(UserToken) // NOT OK
|
||||
jwtJsonwebtoken.verify(UserToken, getSecret(), { algorithms: ["HS256", "none"] }) // NOT OK
|
||||
})
|
||||
|
||||
app.get('/jwtJsonwebtoken3', (req, res) => {
|
||||
const UserToken = req.headers.authorization;
|
||||
|
||||
// GOOD: with signature verification
|
||||
jwtJsonwebtoken.verify(UserToken, getSecret()) // OK
|
||||
})
|
||||
|
||||
app.get('/jwtJsonwebtoken4', (req, res) => {
|
||||
const UserToken = req.headers.authorization;
|
||||
|
||||
// GOOD: first without signature verification then with signature verification for same UserToken
|
||||
jwtJsonwebtoken.decode(UserToken) // OK
|
||||
jwtJsonwebtoken.verify(UserToken, getSecret()) // OK
|
||||
})
|
||||
|
||||
app.get('/jwtJsonwebtoken5', (req, res) => {
|
||||
const UserToken = req.headers.authorization;
|
||||
|
||||
// GOOD: first without signature verification then with signature verification for same UserToken
|
||||
jwtJsonwebtoken.decode(UserToken) // OK
|
||||
jwtJsonwebtoken.verify(UserToken, getSecret(), { algorithms: ["HS256"] }) // OK
|
||||
})
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Example app listening on port ${port}`)
|
||||
})
|
||||
@@ -0,0 +1,161 @@
|
||||
nodes
|
||||
| JsonWebToken.js:10:11:10:47 | UserToken |
|
||||
| JsonWebToken.js:10:23:10:47 | req.hea ... ization |
|
||||
| JsonWebToken.js:10:23:10:47 | req.hea ... ization |
|
||||
| JsonWebToken.js:13:28:13:36 | UserToken |
|
||||
| JsonWebToken.js:13:28:13:36 | UserToken |
|
||||
| JsonWebToken.js:17:11:17:47 | UserToken |
|
||||
| JsonWebToken.js:17:23:17:47 | req.hea ... ization |
|
||||
| JsonWebToken.js:17:23:17:47 | req.hea ... ization |
|
||||
| JsonWebToken.js:20:28:20:36 | UserToken |
|
||||
| JsonWebToken.js:20:28:20:36 | UserToken |
|
||||
| JsonWebToken.js:21:28:21:36 | UserToken |
|
||||
| JsonWebToken.js:21:28:21:36 | UserToken |
|
||||
| JsonWebToken.js:25:11:25:47 | UserToken |
|
||||
| JsonWebToken.js:25:23:25:47 | req.hea ... ization |
|
||||
| JsonWebToken.js:25:23:25:47 | req.hea ... ization |
|
||||
| JsonWebToken.js:28:28:28:36 | UserToken |
|
||||
| JsonWebToken.js:28:28:28:36 | UserToken |
|
||||
| JsonWebToken.js:32:11:32:47 | UserToken |
|
||||
| JsonWebToken.js:32:11:32:47 | UserToken |
|
||||
| JsonWebToken.js:32:23:32:47 | req.hea ... ization |
|
||||
| JsonWebToken.js:32:23:32:47 | req.hea ... ization |
|
||||
| JsonWebToken.js:32:23:32:47 | req.hea ... ization |
|
||||
| JsonWebToken.js:32:23:32:47 | req.hea ... ization |
|
||||
| JsonWebToken.js:35:28:35:36 | UserToken |
|
||||
| JsonWebToken.js:35:28:35:36 | UserToken |
|
||||
| JsonWebToken.js:36:28:36:36 | UserToken |
|
||||
| JsonWebToken.js:36:28:36:36 | UserToken |
|
||||
| JsonWebToken.js:40:11:40:47 | UserToken |
|
||||
| JsonWebToken.js:40:11:40:47 | UserToken |
|
||||
| JsonWebToken.js:40:23:40:47 | req.hea ... ization |
|
||||
| JsonWebToken.js:40:23:40:47 | req.hea ... ization |
|
||||
| JsonWebToken.js:40:23:40:47 | req.hea ... ization |
|
||||
| JsonWebToken.js:40:23:40:47 | req.hea ... ization |
|
||||
| JsonWebToken.js:43:28:43:36 | UserToken |
|
||||
| JsonWebToken.js:43:28:43:36 | UserToken |
|
||||
| JsonWebToken.js:44:28:44:36 | UserToken |
|
||||
| JsonWebToken.js:44:28:44:36 | UserToken |
|
||||
| jose.js:11:11:11:47 | UserToken |
|
||||
| jose.js:11:23:11:47 | req.hea ... ization |
|
||||
| jose.js:11:23:11:47 | req.hea ... ization |
|
||||
| jose.js:13:20:13:28 | UserToken |
|
||||
| jose.js:13:20:13:28 | UserToken |
|
||||
| jose.js:18:11:18:47 | UserToken |
|
||||
| jose.js:18:23:18:47 | req.hea ... ization |
|
||||
| jose.js:18:23:18:47 | req.hea ... ization |
|
||||
| jose.js:20:26:20:34 | UserToken |
|
||||
| jose.js:20:26:20:34 | UserToken |
|
||||
| jose.js:24:11:24:47 | UserToken |
|
||||
| jose.js:24:11:24:47 | UserToken |
|
||||
| jose.js:24:23:24:47 | req.hea ... ization |
|
||||
| jose.js:24:23:24:47 | req.hea ... ization |
|
||||
| jose.js:24:23:24:47 | req.hea ... ization |
|
||||
| jose.js:24:23:24:47 | req.hea ... ization |
|
||||
| jose.js:26:20:26:28 | UserToken |
|
||||
| jose.js:26:20:26:28 | UserToken |
|
||||
| jose.js:27:26:27:34 | UserToken |
|
||||
| jose.js:27:26:27:34 | UserToken |
|
||||
| jwtDecode.js:11:11:11:47 | UserToken |
|
||||
| jwtDecode.js:11:23:11:47 | req.hea ... ization |
|
||||
| jwtDecode.js:11:23:11:47 | req.hea ... ization |
|
||||
| jwtDecode.js:15:16:15:24 | UserToken |
|
||||
| jwtDecode.js:15:16:15:24 | UserToken |
|
||||
| jwtSimple.js:10:11:10:47 | UserToken |
|
||||
| jwtSimple.js:10:23:10:47 | req.hea ... ization |
|
||||
| jwtSimple.js:10:23:10:47 | req.hea ... ization |
|
||||
| jwtSimple.js:13:23:13:31 | UserToken |
|
||||
| jwtSimple.js:13:23:13:31 | UserToken |
|
||||
| jwtSimple.js:17:11:17:47 | UserToken |
|
||||
| jwtSimple.js:17:23:17:47 | req.hea ... ization |
|
||||
| jwtSimple.js:17:23:17:47 | req.hea ... ization |
|
||||
| jwtSimple.js:20:23:20:31 | UserToken |
|
||||
| jwtSimple.js:20:23:20:31 | UserToken |
|
||||
| jwtSimple.js:21:23:21:31 | UserToken |
|
||||
| jwtSimple.js:21:23:21:31 | UserToken |
|
||||
| jwtSimple.js:25:11:25:47 | UserToken |
|
||||
| jwtSimple.js:25:11:25:47 | UserToken |
|
||||
| jwtSimple.js:25:23:25:47 | req.hea ... ization |
|
||||
| jwtSimple.js:25:23:25:47 | req.hea ... ization |
|
||||
| jwtSimple.js:25:23:25:47 | req.hea ... ization |
|
||||
| jwtSimple.js:25:23:25:47 | req.hea ... ization |
|
||||
| jwtSimple.js:28:23:28:31 | UserToken |
|
||||
| jwtSimple.js:28:23:28:31 | UserToken |
|
||||
| jwtSimple.js:29:23:29:31 | UserToken |
|
||||
| jwtSimple.js:29:23:29:31 | UserToken |
|
||||
edges
|
||||
| JsonWebToken.js:10:11:10:47 | UserToken | JsonWebToken.js:13:28:13:36 | UserToken |
|
||||
| JsonWebToken.js:10:11:10:47 | UserToken | JsonWebToken.js:13:28:13:36 | UserToken |
|
||||
| JsonWebToken.js:10:23:10:47 | req.hea ... ization | JsonWebToken.js:10:11:10:47 | UserToken |
|
||||
| JsonWebToken.js:10:23:10:47 | req.hea ... ization | JsonWebToken.js:10:11:10:47 | UserToken |
|
||||
| JsonWebToken.js:17:11:17:47 | UserToken | JsonWebToken.js:20:28:20:36 | UserToken |
|
||||
| JsonWebToken.js:17:11:17:47 | UserToken | JsonWebToken.js:20:28:20:36 | UserToken |
|
||||
| JsonWebToken.js:17:11:17:47 | UserToken | JsonWebToken.js:21:28:21:36 | UserToken |
|
||||
| JsonWebToken.js:17:11:17:47 | UserToken | JsonWebToken.js:21:28:21:36 | UserToken |
|
||||
| JsonWebToken.js:17:23:17:47 | req.hea ... ization | JsonWebToken.js:17:11:17:47 | UserToken |
|
||||
| JsonWebToken.js:17:23:17:47 | req.hea ... ization | JsonWebToken.js:17:11:17:47 | UserToken |
|
||||
| JsonWebToken.js:25:11:25:47 | UserToken | JsonWebToken.js:28:28:28:36 | UserToken |
|
||||
| JsonWebToken.js:25:11:25:47 | UserToken | JsonWebToken.js:28:28:28:36 | UserToken |
|
||||
| JsonWebToken.js:25:23:25:47 | req.hea ... ization | JsonWebToken.js:25:11:25:47 | UserToken |
|
||||
| JsonWebToken.js:25:23:25:47 | req.hea ... ization | JsonWebToken.js:25:11:25:47 | UserToken |
|
||||
| JsonWebToken.js:32:11:32:47 | UserToken | JsonWebToken.js:35:28:35:36 | UserToken |
|
||||
| JsonWebToken.js:32:11:32:47 | UserToken | JsonWebToken.js:35:28:35:36 | UserToken |
|
||||
| JsonWebToken.js:32:11:32:47 | UserToken | JsonWebToken.js:36:28:36:36 | UserToken |
|
||||
| JsonWebToken.js:32:11:32:47 | UserToken | JsonWebToken.js:36:28:36:36 | UserToken |
|
||||
| JsonWebToken.js:32:23:32:47 | req.hea ... ization | JsonWebToken.js:32:11:32:47 | UserToken |
|
||||
| JsonWebToken.js:32:23:32:47 | req.hea ... ization | JsonWebToken.js:32:11:32:47 | UserToken |
|
||||
| JsonWebToken.js:32:23:32:47 | req.hea ... ization | JsonWebToken.js:32:11:32:47 | UserToken |
|
||||
| JsonWebToken.js:32:23:32:47 | req.hea ... ization | JsonWebToken.js:32:11:32:47 | UserToken |
|
||||
| JsonWebToken.js:40:11:40:47 | UserToken | JsonWebToken.js:43:28:43:36 | UserToken |
|
||||
| JsonWebToken.js:40:11:40:47 | UserToken | JsonWebToken.js:43:28:43:36 | UserToken |
|
||||
| JsonWebToken.js:40:11:40:47 | UserToken | JsonWebToken.js:44:28:44:36 | UserToken |
|
||||
| JsonWebToken.js:40:11:40:47 | UserToken | JsonWebToken.js:44:28:44:36 | UserToken |
|
||||
| JsonWebToken.js:40:23:40:47 | req.hea ... ization | JsonWebToken.js:40:11:40:47 | UserToken |
|
||||
| JsonWebToken.js:40:23:40:47 | req.hea ... ization | JsonWebToken.js:40:11:40:47 | UserToken |
|
||||
| JsonWebToken.js:40:23:40:47 | req.hea ... ization | JsonWebToken.js:40:11:40:47 | UserToken |
|
||||
| JsonWebToken.js:40:23:40:47 | req.hea ... ization | JsonWebToken.js:40:11:40:47 | UserToken |
|
||||
| jose.js:11:11:11:47 | UserToken | jose.js:13:20:13:28 | UserToken |
|
||||
| jose.js:11:11:11:47 | UserToken | jose.js:13:20:13:28 | UserToken |
|
||||
| jose.js:11:23:11:47 | req.hea ... ization | jose.js:11:11:11:47 | UserToken |
|
||||
| jose.js:11:23:11:47 | req.hea ... ization | jose.js:11:11:11:47 | UserToken |
|
||||
| jose.js:18:11:18:47 | UserToken | jose.js:20:26:20:34 | UserToken |
|
||||
| jose.js:18:11:18:47 | UserToken | jose.js:20:26:20:34 | UserToken |
|
||||
| jose.js:18:23:18:47 | req.hea ... ization | jose.js:18:11:18:47 | UserToken |
|
||||
| jose.js:18:23:18:47 | req.hea ... ization | jose.js:18:11:18:47 | UserToken |
|
||||
| jose.js:24:11:24:47 | UserToken | jose.js:26:20:26:28 | UserToken |
|
||||
| jose.js:24:11:24:47 | UserToken | jose.js:26:20:26:28 | UserToken |
|
||||
| jose.js:24:11:24:47 | UserToken | jose.js:27:26:27:34 | UserToken |
|
||||
| jose.js:24:11:24:47 | UserToken | jose.js:27:26:27:34 | UserToken |
|
||||
| jose.js:24:23:24:47 | req.hea ... ization | jose.js:24:11:24:47 | UserToken |
|
||||
| jose.js:24:23:24:47 | req.hea ... ization | jose.js:24:11:24:47 | UserToken |
|
||||
| jose.js:24:23:24:47 | req.hea ... ization | jose.js:24:11:24:47 | UserToken |
|
||||
| jose.js:24:23:24:47 | req.hea ... ization | jose.js:24:11:24:47 | UserToken |
|
||||
| jwtDecode.js:11:11:11:47 | UserToken | jwtDecode.js:15:16:15:24 | UserToken |
|
||||
| jwtDecode.js:11:11:11:47 | UserToken | jwtDecode.js:15:16:15:24 | UserToken |
|
||||
| jwtDecode.js:11:23:11:47 | req.hea ... ization | jwtDecode.js:11:11:11:47 | UserToken |
|
||||
| jwtDecode.js:11:23:11:47 | req.hea ... ization | jwtDecode.js:11:11:11:47 | UserToken |
|
||||
| jwtSimple.js:10:11:10:47 | UserToken | jwtSimple.js:13:23:13:31 | UserToken |
|
||||
| jwtSimple.js:10:11:10:47 | UserToken | jwtSimple.js:13:23:13:31 | UserToken |
|
||||
| jwtSimple.js:10:23:10:47 | req.hea ... ization | jwtSimple.js:10:11:10:47 | UserToken |
|
||||
| jwtSimple.js:10:23:10:47 | req.hea ... ization | jwtSimple.js:10:11:10:47 | UserToken |
|
||||
| jwtSimple.js:17:11:17:47 | UserToken | jwtSimple.js:20:23:20:31 | UserToken |
|
||||
| jwtSimple.js:17:11:17:47 | UserToken | jwtSimple.js:20:23:20:31 | UserToken |
|
||||
| jwtSimple.js:17:11:17:47 | UserToken | jwtSimple.js:21:23:21:31 | UserToken |
|
||||
| jwtSimple.js:17:11:17:47 | UserToken | jwtSimple.js:21:23:21:31 | UserToken |
|
||||
| jwtSimple.js:17:23:17:47 | req.hea ... ization | jwtSimple.js:17:11:17:47 | UserToken |
|
||||
| jwtSimple.js:17:23:17:47 | req.hea ... ization | jwtSimple.js:17:11:17:47 | UserToken |
|
||||
| jwtSimple.js:25:11:25:47 | UserToken | jwtSimple.js:28:23:28:31 | UserToken |
|
||||
| jwtSimple.js:25:11:25:47 | UserToken | jwtSimple.js:28:23:28:31 | UserToken |
|
||||
| jwtSimple.js:25:11:25:47 | UserToken | jwtSimple.js:29:23:29:31 | UserToken |
|
||||
| jwtSimple.js:25:11:25:47 | UserToken | jwtSimple.js:29:23:29:31 | UserToken |
|
||||
| jwtSimple.js:25:23:25:47 | req.hea ... ization | jwtSimple.js:25:11:25:47 | UserToken |
|
||||
| jwtSimple.js:25:23:25:47 | req.hea ... ization | jwtSimple.js:25:11:25:47 | UserToken |
|
||||
| jwtSimple.js:25:23:25:47 | req.hea ... ization | jwtSimple.js:25:11:25:47 | UserToken |
|
||||
| jwtSimple.js:25:23:25:47 | req.hea ... ization | jwtSimple.js:25:11:25:47 | UserToken |
|
||||
#select
|
||||
| JsonWebToken.js:10:23:10:47 | req.hea ... ization | JsonWebToken.js:10:23:10:47 | req.hea ... ization | JsonWebToken.js:13:28:13:36 | UserToken | Decoding JWT $@. | JsonWebToken.js:13:28:13:36 | UserToken | without signature verification |
|
||||
| JsonWebToken.js:17:23:17:47 | req.hea ... ization | JsonWebToken.js:17:23:17:47 | req.hea ... ization | JsonWebToken.js:20:28:20:36 | UserToken | Decoding JWT $@. | JsonWebToken.js:20:28:20:36 | UserToken | without signature verification |
|
||||
| JsonWebToken.js:17:23:17:47 | req.hea ... ization | JsonWebToken.js:17:23:17:47 | req.hea ... ization | JsonWebToken.js:21:28:21:36 | UserToken | Decoding JWT $@. | JsonWebToken.js:21:28:21:36 | UserToken | without signature verification |
|
||||
| jose.js:11:23:11:47 | req.hea ... ization | jose.js:11:23:11:47 | req.hea ... ization | jose.js:13:20:13:28 | UserToken | Decoding JWT $@. | jose.js:13:20:13:28 | UserToken | without signature verification |
|
||||
| jwtDecode.js:11:23:11:47 | req.hea ... ization | jwtDecode.js:11:23:11:47 | req.hea ... ization | jwtDecode.js:15:16:15:24 | UserToken | Decoding JWT $@. | jwtDecode.js:15:16:15:24 | UserToken | without signature verification |
|
||||
| jwtSimple.js:10:23:10:47 | req.hea ... ization | jwtSimple.js:10:23:10:47 | req.hea ... ization | jwtSimple.js:13:23:13:31 | UserToken | Decoding JWT $@. | jwtSimple.js:13:23:13:31 | UserToken | without signature verification |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE-347/decodeJwtWithoutVerification.ql
|
||||
@@ -0,0 +1,32 @@
|
||||
const express = require('express')
|
||||
const app = express()
|
||||
const jose = require('jose')
|
||||
const port = 3000
|
||||
|
||||
function getSecret() {
|
||||
return "A Safe generated random key"
|
||||
}
|
||||
|
||||
app.get('/jose1', (req, res) => {
|
||||
const UserToken = req.headers.authorization;
|
||||
// no signature verification
|
||||
jose.decodeJwt(UserToken) // NOT OK
|
||||
})
|
||||
|
||||
|
||||
app.get('/jose2', async (req, res) => {
|
||||
const UserToken = req.headers.authorization;
|
||||
// with signature verification
|
||||
await jose.jwtVerify(UserToken, new TextEncoder().encode(getSecret())) // OK
|
||||
})
|
||||
|
||||
app.get('/jose3', async (req, res) => {
|
||||
const UserToken = req.headers.authorization;
|
||||
// first without signature verification then with signature verification for same UserToken
|
||||
jose.decodeJwt(UserToken) // OK
|
||||
await jose.jwtVerify(UserToken, new TextEncoder().encode(getSecret())) // OK
|
||||
})
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Example app listening on port ${port}`)
|
||||
})
|
||||
@@ -0,0 +1,20 @@
|
||||
const express = require('express')
|
||||
const app = express()
|
||||
const jwt_decode = require('jwt-decode');
|
||||
const port = 3000
|
||||
|
||||
function getSecret() {
|
||||
return "A Safe generated random key"
|
||||
}
|
||||
|
||||
app.get('/jwtDecode', (req, res) => {
|
||||
const UserToken = req.headers.authorization;
|
||||
|
||||
// jwt-decode
|
||||
// no signature verification
|
||||
jwt_decode(UserToken) // NOT OK
|
||||
})
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Example app listening on port ${port}`)
|
||||
})
|
||||
@@ -0,0 +1,34 @@
|
||||
const express = require('express')
|
||||
const app = express()
|
||||
const jwt_simple = require('jwt-simple');
|
||||
const port = 3000
|
||||
|
||||
function getSecret() {
|
||||
return "A Safe generated random key"
|
||||
}
|
||||
app.get('/jwtSimple1', (req, res) => {
|
||||
const UserToken = req.headers.authorization;
|
||||
|
||||
// no signature verification
|
||||
jwt_simple.decode(UserToken, getSecret(), true); // NOT OK
|
||||
})
|
||||
|
||||
app.get('/jwtSimple2', (req, res) => {
|
||||
const UserToken = req.headers.authorization;
|
||||
|
||||
// GOOD: all with with signature verification
|
||||
jwt_simple.decode(UserToken, getSecret(), false); // OK
|
||||
jwt_simple.decode(UserToken, getSecret()); // OK
|
||||
})
|
||||
|
||||
app.get('/jwtSimple3', (req, res) => {
|
||||
const UserToken = req.headers.authorization;
|
||||
|
||||
// GOOD: first without signature verification then with signature verification for same UserToken
|
||||
jwt_simple.decode(UserToken, getSecret(), true); // OK
|
||||
jwt_simple.decode(UserToken, getSecret()); // OK
|
||||
})
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Example app listening on port ${port}`)
|
||||
})
|
||||
@@ -5,20 +5,22 @@ flow
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:15:27:15:27 | e |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:16:23:16:23 | e |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:20:8:20:16 | arr.pop() |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:52:10:52:10 | x |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:56:10:56:10 | x |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:60:10:60:10 | x |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:66:10:66:10 | x |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:71:10:71:10 | x |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:74:8:74:29 | arr.fin ... llback) |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:77:8:77:35 | arrayFi ... llback) |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:81:10:81:10 | x |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:84:8:84:17 | arr.at(-1) |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:39:8:39:24 | arr4_spread.pop() |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:61:10:61:10 | x |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:65:10:65:10 | x |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:69:10:69:10 | x |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:75:10:75:10 | x |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:80:10:80:10 | x |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:83:8:83:29 | arr.fin ... llback) |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:86:8:86:35 | arrayFi ... llback) |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:90:10:90:10 | x |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:93:8:93:17 | arr.at(-1) |
|
||||
| arrays.js:18:22:18:29 | "source" | arrays.js:18:50:18:50 | e |
|
||||
| arrays.js:22:15:22:22 | "source" | arrays.js:23:8:23:17 | arr2.pop() |
|
||||
| arrays.js:25:15:25:22 | "source" | arrays.js:26:8:26:17 | arr3.pop() |
|
||||
| arrays.js:29:21:29:28 | "source" | arrays.js:30:8:30:17 | arr4.pop() |
|
||||
| arrays.js:29:21:29:28 | "source" | arrays.js:33:8:33:17 | arr5.pop() |
|
||||
| arrays.js:29:21:29:28 | "source" | arrays.js:35:8:35:26 | arr5.slice(2).pop() |
|
||||
| arrays.js:29:21:29:28 | "source" | arrays.js:41:8:41:17 | arr6.pop() |
|
||||
| arrays.js:44:4:44:11 | "source" | arrays.js:45:10:45:18 | ary.pop() |
|
||||
| arrays.js:29:21:29:28 | "source" | arrays.js:42:8:42:17 | arr5.pop() |
|
||||
| arrays.js:29:21:29:28 | "source" | arrays.js:44:8:44:26 | arr5.slice(2).pop() |
|
||||
| arrays.js:29:21:29:28 | "source" | arrays.js:50:8:50:17 | arr6.pop() |
|
||||
| arrays.js:33:37:33:44 | "source" | arrays.js:35:8:35:25 | arr4_variant.pop() |
|
||||
| arrays.js:53:4:53:11 | "source" | arrays.js:54:10:54:18 | ary.pop() |
|
||||
|
||||
@@ -5,24 +5,26 @@ flow
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:15:27:15:27 | e |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:16:23:16:23 | e |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:20:8:20:16 | arr.pop() |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:49:8:49:13 | arr[0] |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:52:10:52:10 | x |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:56:10:56:10 | x |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:60:10:60:10 | x |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:66:10:66:10 | x |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:71:10:71:10 | x |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:74:8:74:29 | arr.fin ... llback) |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:77:8:77:35 | arrayFi ... llback) |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:81:10:81:10 | x |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:84:8:84:17 | arr.at(-1) |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:39:8:39:24 | arr4_spread.pop() |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:58:8:58:13 | arr[0] |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:61:10:61:10 | x |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:65:10:65:10 | x |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:69:10:69:10 | x |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:75:10:75:10 | x |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:80:10:80:10 | x |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:83:8:83:29 | arr.fin ... llback) |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:86:8:86:35 | arrayFi ... llback) |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:90:10:90:10 | x |
|
||||
| arrays.js:2:16:2:23 | "source" | arrays.js:93:8:93:17 | arr.at(-1) |
|
||||
| arrays.js:18:22:18:29 | "source" | arrays.js:18:50:18:50 | e |
|
||||
| arrays.js:22:15:22:22 | "source" | arrays.js:23:8:23:17 | arr2.pop() |
|
||||
| arrays.js:25:15:25:22 | "source" | arrays.js:26:8:26:17 | arr3.pop() |
|
||||
| arrays.js:29:21:29:28 | "source" | arrays.js:30:8:30:17 | arr4.pop() |
|
||||
| arrays.js:29:21:29:28 | "source" | arrays.js:33:8:33:17 | arr5.pop() |
|
||||
| arrays.js:29:21:29:28 | "source" | arrays.js:35:8:35:26 | arr5.slice(2).pop() |
|
||||
| arrays.js:29:21:29:28 | "source" | arrays.js:41:8:41:17 | arr6.pop() |
|
||||
| arrays.js:44:4:44:11 | "source" | arrays.js:45:10:45:18 | ary.pop() |
|
||||
| arrays.js:44:4:44:11 | "source" | arrays.js:46:10:46:12 | ary |
|
||||
| arrays.js:86:9:86:16 | "source" | arrays.js:86:8:86:34 | ["sourc ... ) => x) |
|
||||
| arrays.js:87:9:87:16 | "source" | arrays.js:87:8:87:36 | ["sourc ... => !!x) |
|
||||
| arrays.js:29:21:29:28 | "source" | arrays.js:42:8:42:17 | arr5.pop() |
|
||||
| arrays.js:29:21:29:28 | "source" | arrays.js:44:8:44:26 | arr5.slice(2).pop() |
|
||||
| arrays.js:29:21:29:28 | "source" | arrays.js:50:8:50:17 | arr6.pop() |
|
||||
| arrays.js:33:37:33:44 | "source" | arrays.js:35:8:35:25 | arr4_variant.pop() |
|
||||
| arrays.js:53:4:53:11 | "source" | arrays.js:54:10:54:18 | ary.pop() |
|
||||
| arrays.js:53:4:53:11 | "source" | arrays.js:55:10:55:12 | ary |
|
||||
| arrays.js:95:9:95:16 | "source" | arrays.js:95:8:95:34 | ["sourc ... ) => x) |
|
||||
| arrays.js:96:9:96:16 | "source" | arrays.js:96:8:96:36 | ["sourc ... => !!x) |
|
||||
|
||||
@@ -29,6 +29,15 @@
|
||||
arr4.splice(0, 0, "source");
|
||||
sink(arr4.pop()); // NOT OK
|
||||
|
||||
var arr4_variant = [];
|
||||
arr4_variant.splice(0, 0, "safe", "source");
|
||||
arr4_variant.pop();
|
||||
sink(arr4_variant.pop()); // NOT OK
|
||||
|
||||
var arr4_spread = [];
|
||||
arr4_spread.splice(0, 0, ...arr);
|
||||
sink(arr4_spread.pop()); // NOT OK
|
||||
|
||||
var arr5 = [].concat(arr4);
|
||||
sink(arr5.pop()); // NOT OK
|
||||
|
||||
@@ -46,7 +55,7 @@
|
||||
sink(ary); // OK - its the array itself, not an element.
|
||||
});
|
||||
|
||||
sink(arr[0]); // OK - tuple like usage.
|
||||
sink(arr[0]); // OK - tuple like usage.
|
||||
|
||||
for (const x of arr) {
|
||||
sink(x); // NOT OK
|
||||
@@ -59,7 +68,7 @@
|
||||
for (const x of [...arr]) {
|
||||
sink(x); // NOT OK
|
||||
}
|
||||
|
||||
|
||||
var arr7 = [];
|
||||
arr7.push(...arr);
|
||||
for (const x of arr7) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,6 +11,7 @@ typeTracking
|
||||
| tst.js:2:16:2:23 | source() | tst.js:37:14:37:14 | e |
|
||||
| tst.js:2:16:2:23 | source() | tst.js:41:14:41:14 | e |
|
||||
| tst.js:2:16:2:23 | source() | tst.js:45:14:45:14 | e |
|
||||
| tst.js:2:16:2:23 | source() | tst.js:49:14:49:14 | e |
|
||||
| tst.js:2:16:2:23 | source() | tst.js:53:8:53:21 | map.get("key") |
|
||||
| tst.js:2:16:2:23 | source() | tst.js:59:8:59:22 | map2.get("foo") |
|
||||
| tst.js:2:16:2:23 | source() | tst.js:64:8:64:26 | map3.get(unknown()) |
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
}
|
||||
|
||||
for (const e of Array.from(set)) {
|
||||
sink(e); // NOT OK (not caught by type-tracking, as it doesn't include array steps).
|
||||
sink(e); // NOT OK
|
||||
}
|
||||
|
||||
sink(map.get("key")); // NOT OK.
|
||||
|
||||
@@ -52,9 +52,14 @@ test_FileSystemAccess
|
||||
| tst.js:56:1:56:18 | shelljs.uniq(file) |
|
||||
| tst.js:57:1:57:26 | shelljs ... file2) |
|
||||
| tst.js:58:1:58:32 | shelljs ... file2) |
|
||||
| tst.js:60:1:60:17 | shelljs.cat(file) |
|
||||
| tst.js:60:1:60:41 | shelljs ... cement) |
|
||||
| tst.js:61:1:61:17 | shelljs.cat(file) |
|
||||
test_MissingFileSystemAccess
|
||||
test_SystemCommandExecution
|
||||
| tst.js:14:1:14:27 | shelljs ... ts, cb) |
|
||||
| tst.js:60:1:60:51 | shelljs ... ec(cmd) |
|
||||
| tst.js:61:1:61:27 | shelljs ... ec(cmd) |
|
||||
test_FileNameSource
|
||||
| tst.js:15:1:15:26 | shelljs ... file2) |
|
||||
| tst.js:24:1:24:16 | shelljs.ls(file) |
|
||||
|
||||
@@ -56,3 +56,6 @@ shelljs.touch(file1, file2);
|
||||
shelljs.uniq(file);
|
||||
shelljs.uniq(file1, file2);
|
||||
shelljs.uniq(opts, file1, file2);
|
||||
|
||||
shelljs.cat(file).sed(regex, replacement).exec(cmd);
|
||||
shelljs.cat(file).exec(cmd);
|
||||
|
||||
@@ -80,6 +80,7 @@ taintFlow
|
||||
| test.js:269:10:269:31 | this.ba ... ource() | test.js:269:10:269:31 | this.ba ... ource() |
|
||||
| test.js:272:6:272:40 | new MyS ... ource() | test.js:272:6:272:40 | new MyS ... ource() |
|
||||
| test.js:274:6:274:39 | testlib ... eName() | test.js:274:6:274:39 | testlib ... eName() |
|
||||
| test.js:277:8:277:31 | "danger ... .danger | test.js:277:8:277:31 | "danger ... .danger |
|
||||
isSink
|
||||
| test.js:54:18:54:25 | source() | test-sink |
|
||||
| test.js:55:22:55:29 | source() | test-sink |
|
||||
|
||||
@@ -11,6 +11,7 @@ extensions:
|
||||
- ['testlib', 'Member[ParamDecoratorSource].DecoratedParameter', 'test-source']
|
||||
- ['testlib', 'Member[getSource].ReturnValue', 'test-source']
|
||||
- ['(testlib)', 'Member[parenthesizedPackageName].ReturnValue', 'test-source']
|
||||
- ['danger-constant', 'Member[danger]', 'test-source']
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/javascript-all
|
||||
|
||||
@@ -272,3 +272,9 @@ class MySubclass2 extends MySubclass {
|
||||
sink(new MySubclass2().baseclassSource()); // NOT OK
|
||||
|
||||
sink(testlib.parenthesizedPackageName()); // NOT OK
|
||||
|
||||
function dangerConstant() {
|
||||
sink("danger-constant".danger); // NOT OK
|
||||
sink("danger-constant".safe); // OK
|
||||
sink("danger-constant"); // OK
|
||||
}
|
||||
|
||||
@@ -2,6 +2,15 @@ import javascript
|
||||
import testUtilities.ConsistencyChecking
|
||||
import semmle.javascript.frameworks.data.internal.ApiGraphModels as ApiGraphModels
|
||||
|
||||
class TypeModelFromCodeQL extends ModelInput::TypeModel {
|
||||
override predicate isTypeUsed(string type) { type = "danger-constant" }
|
||||
|
||||
override DataFlow::Node getASource(string type) {
|
||||
type = "danger-constant" and
|
||||
result.getStringValue() = "danger-constant"
|
||||
}
|
||||
}
|
||||
|
||||
module TestConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source.(DataFlow::CallNode).getCalleeName() = "source"
|
||||
|
||||
@@ -0,0 +1,391 @@
|
||||
nodes
|
||||
| adm-zip.js:13:13:13:21 | req.files |
|
||||
| adm-zip.js:13:13:13:21 | req.files |
|
||||
| adm-zip.js:13:13:13:33 | req.fil ... ombFile |
|
||||
| adm-zip.js:17:18:17:24 | tarFile |
|
||||
| adm-zip.js:24:22:24:28 | tarFile |
|
||||
| adm-zip.js:24:22:24:33 | tarFile.data |
|
||||
| adm-zip.js:28:25:28:42 | zipEntry.getData() |
|
||||
| adm-zip.js:28:25:28:42 | zipEntry.getData() |
|
||||
| adm-zip.js:32:17:32:41 | admZip. ... "10GB") |
|
||||
| adm-zip.js:32:17:32:41 | admZip. ... "10GB") |
|
||||
| adm-zip.js:34:5:34:55 | admZip. ... , true) |
|
||||
| adm-zip.js:34:5:34:55 | admZip. ... , true) |
|
||||
| adm-zip.js:36:5:36:38 | admZip. ... , true) |
|
||||
| adm-zip.js:36:5:36:38 | admZip. ... , true) |
|
||||
| decompress.js:11:16:11:33 | req.query.filePath |
|
||||
| decompress.js:11:16:11:33 | req.query.filePath |
|
||||
| decompress.js:11:16:11:33 | req.query.filePath |
|
||||
| jszip.js:12:13:12:21 | req.files |
|
||||
| jszip.js:12:13:12:21 | req.files |
|
||||
| jszip.js:12:13:12:33 | req.fil ... ombFile |
|
||||
| jszip.js:12:13:12:38 | req.fil ... le.data |
|
||||
| jszip.js:32:18:32:24 | zipFile |
|
||||
| jszip.js:33:22:33:28 | zipFile |
|
||||
| jszip.js:33:22:33:33 | zipFile.data |
|
||||
| jszip.js:33:22:33:33 | zipFile.data |
|
||||
| node-tar.js:15:13:15:21 | req.files |
|
||||
| node-tar.js:15:13:15:21 | req.files |
|
||||
| node-tar.js:15:13:15:33 | req.fil ... ombFile |
|
||||
| node-tar.js:15:13:15:38 | req.fil ... le.data |
|
||||
| node-tar.js:19:18:19:24 | tarFile |
|
||||
| node-tar.js:21:23:21:49 | Readabl ... e.data) |
|
||||
| node-tar.js:21:37:21:43 | tarFile |
|
||||
| node-tar.js:21:37:21:48 | tarFile.data |
|
||||
| node-tar.js:24:9:24:15 | tar.x() |
|
||||
| node-tar.js:24:9:24:15 | tar.x() |
|
||||
| node-tar.js:29:5:29:37 | fs.crea ... e.name) |
|
||||
| node-tar.js:29:25:29:31 | tarFile |
|
||||
| node-tar.js:29:25:29:36 | tarFile.name |
|
||||
| node-tar.js:30:9:33:10 | tar.x({ ... }) |
|
||||
| node-tar.js:30:9:33:10 | tar.x({ ... }) |
|
||||
| node-tar.js:45:5:45:37 | fs.crea ... e.name) |
|
||||
| node-tar.js:45:25:45:31 | tarFile |
|
||||
| node-tar.js:45:25:45:36 | tarFile.name |
|
||||
| node-tar.js:46:9:46:20 | decompressor |
|
||||
| node-tar.js:48:9:50:10 | tar.x({ ... }) |
|
||||
| node-tar.js:48:9:50:10 | tar.x({ ... }) |
|
||||
| node-tar.js:58:19:58:25 | tarFile |
|
||||
| node-tar.js:58:19:58:30 | tarFile.name |
|
||||
| node-tar.js:58:19:58:30 | tarFile.name |
|
||||
| node-tar.js:59:25:59:31 | tarFile |
|
||||
| node-tar.js:59:25:59:36 | tarFile.name |
|
||||
| node-tar.js:59:25:59:36 | tarFile.name |
|
||||
| pako.js:12:14:12:22 | req.files |
|
||||
| pako.js:12:14:12:22 | req.files |
|
||||
| pako.js:12:14:12:34 | req.fil ... ombFile |
|
||||
| pako.js:12:14:12:39 | req.fil ... le.data |
|
||||
| pako.js:13:14:13:22 | req.files |
|
||||
| pako.js:13:14:13:22 | req.files |
|
||||
| pako.js:13:14:13:34 | req.fil ... ombFile |
|
||||
| pako.js:13:14:13:39 | req.fil ... le.data |
|
||||
| pako.js:17:19:17:25 | zipFile |
|
||||
| pako.js:18:11:18:68 | myArray |
|
||||
| pako.js:18:21:18:68 | Buffer. ... uffer)) |
|
||||
| pako.js:18:33:18:67 | new Uin ... buffer) |
|
||||
| pako.js:18:48:18:54 | zipFile |
|
||||
| pako.js:18:48:18:59 | zipFile.data |
|
||||
| pako.js:18:48:18:66 | zipFile.data.buffer |
|
||||
| pako.js:21:31:21:37 | myArray |
|
||||
| pako.js:21:31:21:37 | myArray |
|
||||
| pako.js:28:19:28:25 | zipFile |
|
||||
| pako.js:29:11:29:62 | myArray |
|
||||
| pako.js:29:21:29:55 | new Uin ... buffer) |
|
||||
| pako.js:29:21:29:62 | new Uin ... .buffer |
|
||||
| pako.js:29:36:29:42 | zipFile |
|
||||
| pako.js:29:36:29:47 | zipFile.data |
|
||||
| pako.js:29:36:29:54 | zipFile.data.buffer |
|
||||
| pako.js:32:31:32:37 | myArray |
|
||||
| pako.js:32:31:32:37 | myArray |
|
||||
| unbzip2.js:12:5:12:43 | fs.crea ... lePath) |
|
||||
| unbzip2.js:12:25:12:42 | req.query.FilePath |
|
||||
| unbzip2.js:12:25:12:42 | req.query.FilePath |
|
||||
| unbzip2.js:12:50:12:54 | bz2() |
|
||||
| unbzip2.js:12:50:12:54 | bz2() |
|
||||
| unzipper.js:13:26:13:62 | Readabl ... e.data) |
|
||||
| unzipper.js:13:40:13:48 | req.files |
|
||||
| unzipper.js:13:40:13:48 | req.files |
|
||||
| unzipper.js:13:40:13:56 | req.files.ZipFile |
|
||||
| unzipper.js:13:40:13:61 | req.fil ... le.data |
|
||||
| unzipper.js:16:23:16:63 | unzippe ... ath' }) |
|
||||
| unzipper.js:16:23:16:63 | unzippe ... ath' }) |
|
||||
| unzipper.js:19:23:19:41 | unzipper.ParseOne() |
|
||||
| unzipper.js:19:23:19:41 | unzipper.ParseOne() |
|
||||
| unzipper.js:24:15:24:30 | unzipper.Parse() |
|
||||
| unzipper.js:24:15:24:30 | unzipper.Parse() |
|
||||
| unzipper.js:34:15:34:30 | unzipper.Parse() |
|
||||
| unzipper.js:34:15:34:30 | unzipper.Parse() |
|
||||
| unzipper.js:41:35:41:71 | unzippe ... true }) |
|
||||
| unzipper.js:41:35:41:71 | unzippe ... true }) |
|
||||
| unzipper.js:51:36:51:72 | unzippe ... true }) |
|
||||
| unzipper.js:51:36:51:72 | unzippe ... true }) |
|
||||
| unzipper.js:60:23:60:38 | unzipper.Parse() |
|
||||
| unzipper.js:60:23:60:38 | unzipper.Parse() |
|
||||
| unzipper.js:73:23:73:38 | unzipper.Parse() |
|
||||
| unzipper.js:73:23:73:38 | unzipper.Parse() |
|
||||
| yauzl.js:12:18:12:26 | req.files |
|
||||
| yauzl.js:12:18:12:26 | req.files |
|
||||
| yauzl.js:12:18:12:34 | req.files.zipFile |
|
||||
| yauzl.js:12:18:12:39 | req.fil ... le.data |
|
||||
| yauzl.js:12:18:12:39 | req.fil ... le.data |
|
||||
| yauzl.js:13:22:13:30 | req.files |
|
||||
| yauzl.js:13:22:13:30 | req.files |
|
||||
| yauzl.js:13:22:13:38 | req.files.zipFile |
|
||||
| yauzl.js:13:22:13:43 | req.fil ... le.data |
|
||||
| yauzl.js:13:22:13:43 | req.fil ... le.data |
|
||||
| yauzl.js:14:34:14:42 | req.files |
|
||||
| yauzl.js:14:34:14:42 | req.files |
|
||||
| yauzl.js:14:34:14:50 | req.files.zipFile |
|
||||
| yauzl.js:14:34:14:55 | req.fil ... le.data |
|
||||
| yauzl.js:14:34:14:55 | req.fil ... le.data |
|
||||
| yauzl.js:37:16:37:33 | req.query.filePath |
|
||||
| yauzl.js:37:16:37:33 | req.query.filePath |
|
||||
| yauzl.js:39:9:39:27 | zipfile.readEntry() |
|
||||
| yauzl.js:39:9:39:27 | zipfile.readEntry() |
|
||||
| yauzl.js:41:64:41:73 | readStream |
|
||||
| yauzl.js:41:64:41:73 | readStream |
|
||||
| yauzl.js:43:21:43:39 | zipfile.readEntry() |
|
||||
| yauzl.js:43:21:43:39 | zipfile.readEntry() |
|
||||
| zlib.js:15:19:15:27 | req.files |
|
||||
| zlib.js:15:19:15:27 | req.files |
|
||||
| zlib.js:15:19:15:39 | req.fil ... ombFile |
|
||||
| zlib.js:15:19:15:44 | req.fil ... le.data |
|
||||
| zlib.js:17:18:17:26 | req.files |
|
||||
| zlib.js:17:18:17:26 | req.files |
|
||||
| zlib.js:17:18:17:38 | req.fil ... ombFile |
|
||||
| zlib.js:17:18:17:43 | req.fil ... le.data |
|
||||
| zlib.js:19:24:19:32 | req.files |
|
||||
| zlib.js:19:24:19:32 | req.files |
|
||||
| zlib.js:19:24:19:44 | req.fil ... ombFile |
|
||||
| zlib.js:19:24:19:49 | req.fil ... le.data |
|
||||
| zlib.js:21:32:21:40 | req.files |
|
||||
| zlib.js:21:32:21:40 | req.files |
|
||||
| zlib.js:21:32:21:52 | req.fil ... ombFile |
|
||||
| zlib.js:21:32:21:57 | req.fil ... le.data |
|
||||
| zlib.js:27:24:27:30 | zipFile |
|
||||
| zlib.js:29:9:29:15 | zipFile |
|
||||
| zlib.js:29:9:29:20 | zipFile.data |
|
||||
| zlib.js:29:9:29:20 | zipFile.data |
|
||||
| zlib.js:33:9:33:15 | zipFile |
|
||||
| zlib.js:33:9:33:20 | zipFile.data |
|
||||
| zlib.js:33:9:33:20 | zipFile.data |
|
||||
| zlib.js:38:9:38:15 | zipFile |
|
||||
| zlib.js:38:9:38:20 | zipFile.data |
|
||||
| zlib.js:38:9:38:20 | zipFile.data |
|
||||
| zlib.js:62:23:62:29 | zipFile |
|
||||
| zlib.js:63:21:63:27 | zipFile |
|
||||
| zlib.js:63:21:63:32 | zipFile.data |
|
||||
| zlib.js:63:21:63:32 | zipFile.data |
|
||||
| zlib.js:64:20:64:26 | zipFile |
|
||||
| zlib.js:64:20:64:31 | zipFile.data |
|
||||
| zlib.js:64:20:64:31 | zipFile.data |
|
||||
| zlib.js:65:31:65:37 | zipFile |
|
||||
| zlib.js:65:31:65:42 | zipFile.data |
|
||||
| zlib.js:65:31:65:42 | zipFile.data |
|
||||
| zlib.js:74:29:74:35 | zipFile |
|
||||
| zlib.js:75:25:75:51 | Readabl ... e.data) |
|
||||
| zlib.js:75:39:75:45 | zipFile |
|
||||
| zlib.js:75:39:75:50 | zipFile.data |
|
||||
| zlib.js:77:22:77:40 | zlib.createGunzip() |
|
||||
| zlib.js:77:22:77:40 | zlib.createGunzip() |
|
||||
| zlib.js:78:22:78:39 | zlib.createUnzip() |
|
||||
| zlib.js:78:22:78:39 | zlib.createUnzip() |
|
||||
| zlib.js:79:22:79:50 | zlib.cr ... press() |
|
||||
| zlib.js:79:22:79:50 | zlib.cr ... press() |
|
||||
| zlib.js:82:43:82:49 | zipFile |
|
||||
| zlib.js:83:11:83:51 | inputStream |
|
||||
| zlib.js:83:25:83:51 | Readabl ... e.data) |
|
||||
| zlib.js:83:39:83:45 | zipFile |
|
||||
| zlib.js:83:39:83:50 | zipFile.data |
|
||||
| zlib.js:86:9:86:19 | inputStream |
|
||||
| zlib.js:87:9:87:27 | zlib.createGunzip() |
|
||||
| zlib.js:87:9:87:27 | zlib.createGunzip() |
|
||||
edges
|
||||
| adm-zip.js:13:13:13:21 | req.files | adm-zip.js:13:13:13:33 | req.fil ... ombFile |
|
||||
| adm-zip.js:13:13:13:21 | req.files | adm-zip.js:13:13:13:33 | req.fil ... ombFile |
|
||||
| adm-zip.js:13:13:13:33 | req.fil ... ombFile | adm-zip.js:17:18:17:24 | tarFile |
|
||||
| adm-zip.js:17:18:17:24 | tarFile | adm-zip.js:24:22:24:28 | tarFile |
|
||||
| adm-zip.js:24:22:24:28 | tarFile | adm-zip.js:24:22:24:33 | tarFile.data |
|
||||
| adm-zip.js:24:22:24:33 | tarFile.data | adm-zip.js:28:25:28:42 | zipEntry.getData() |
|
||||
| adm-zip.js:24:22:24:33 | tarFile.data | adm-zip.js:28:25:28:42 | zipEntry.getData() |
|
||||
| adm-zip.js:24:22:24:33 | tarFile.data | adm-zip.js:32:17:32:41 | admZip. ... "10GB") |
|
||||
| adm-zip.js:24:22:24:33 | tarFile.data | adm-zip.js:32:17:32:41 | admZip. ... "10GB") |
|
||||
| adm-zip.js:24:22:24:33 | tarFile.data | adm-zip.js:34:5:34:55 | admZip. ... , true) |
|
||||
| adm-zip.js:24:22:24:33 | tarFile.data | adm-zip.js:34:5:34:55 | admZip. ... , true) |
|
||||
| adm-zip.js:24:22:24:33 | tarFile.data | adm-zip.js:36:5:36:38 | admZip. ... , true) |
|
||||
| adm-zip.js:24:22:24:33 | tarFile.data | adm-zip.js:36:5:36:38 | admZip. ... , true) |
|
||||
| decompress.js:11:16:11:33 | req.query.filePath | decompress.js:11:16:11:33 | req.query.filePath |
|
||||
| jszip.js:12:13:12:21 | req.files | jszip.js:12:13:12:33 | req.fil ... ombFile |
|
||||
| jszip.js:12:13:12:21 | req.files | jszip.js:12:13:12:33 | req.fil ... ombFile |
|
||||
| jszip.js:12:13:12:33 | req.fil ... ombFile | jszip.js:12:13:12:38 | req.fil ... le.data |
|
||||
| jszip.js:12:13:12:38 | req.fil ... le.data | jszip.js:32:18:32:24 | zipFile |
|
||||
| jszip.js:32:18:32:24 | zipFile | jszip.js:33:22:33:28 | zipFile |
|
||||
| jszip.js:33:22:33:28 | zipFile | jszip.js:33:22:33:33 | zipFile.data |
|
||||
| jszip.js:33:22:33:28 | zipFile | jszip.js:33:22:33:33 | zipFile.data |
|
||||
| node-tar.js:15:13:15:21 | req.files | node-tar.js:15:13:15:33 | req.fil ... ombFile |
|
||||
| node-tar.js:15:13:15:21 | req.files | node-tar.js:15:13:15:33 | req.fil ... ombFile |
|
||||
| node-tar.js:15:13:15:33 | req.fil ... ombFile | node-tar.js:15:13:15:38 | req.fil ... le.data |
|
||||
| node-tar.js:15:13:15:38 | req.fil ... le.data | node-tar.js:19:18:19:24 | tarFile |
|
||||
| node-tar.js:19:18:19:24 | tarFile | node-tar.js:21:37:21:43 | tarFile |
|
||||
| node-tar.js:19:18:19:24 | tarFile | node-tar.js:29:25:29:31 | tarFile |
|
||||
| node-tar.js:19:18:19:24 | tarFile | node-tar.js:45:25:45:31 | tarFile |
|
||||
| node-tar.js:19:18:19:24 | tarFile | node-tar.js:58:19:58:25 | tarFile |
|
||||
| node-tar.js:19:18:19:24 | tarFile | node-tar.js:59:25:59:31 | tarFile |
|
||||
| node-tar.js:21:23:21:49 | Readabl ... e.data) | node-tar.js:24:9:24:15 | tar.x() |
|
||||
| node-tar.js:21:23:21:49 | Readabl ... e.data) | node-tar.js:24:9:24:15 | tar.x() |
|
||||
| node-tar.js:21:37:21:43 | tarFile | node-tar.js:21:37:21:48 | tarFile.data |
|
||||
| node-tar.js:21:37:21:48 | tarFile.data | node-tar.js:21:23:21:49 | Readabl ... e.data) |
|
||||
| node-tar.js:29:5:29:37 | fs.crea ... e.name) | node-tar.js:30:9:33:10 | tar.x({ ... }) |
|
||||
| node-tar.js:29:5:29:37 | fs.crea ... e.name) | node-tar.js:30:9:33:10 | tar.x({ ... }) |
|
||||
| node-tar.js:29:25:29:31 | tarFile | node-tar.js:29:25:29:36 | tarFile.name |
|
||||
| node-tar.js:29:25:29:36 | tarFile.name | node-tar.js:29:5:29:37 | fs.crea ... e.name) |
|
||||
| node-tar.js:45:5:45:37 | fs.crea ... e.name) | node-tar.js:46:9:46:20 | decompressor |
|
||||
| node-tar.js:45:25:45:31 | tarFile | node-tar.js:45:25:45:36 | tarFile.name |
|
||||
| node-tar.js:45:25:45:36 | tarFile.name | node-tar.js:45:5:45:37 | fs.crea ... e.name) |
|
||||
| node-tar.js:46:9:46:20 | decompressor | node-tar.js:48:9:50:10 | tar.x({ ... }) |
|
||||
| node-tar.js:46:9:46:20 | decompressor | node-tar.js:48:9:50:10 | tar.x({ ... }) |
|
||||
| node-tar.js:58:19:58:25 | tarFile | node-tar.js:58:19:58:30 | tarFile.name |
|
||||
| node-tar.js:58:19:58:25 | tarFile | node-tar.js:58:19:58:30 | tarFile.name |
|
||||
| node-tar.js:59:25:59:31 | tarFile | node-tar.js:59:25:59:36 | tarFile.name |
|
||||
| node-tar.js:59:25:59:31 | tarFile | node-tar.js:59:25:59:36 | tarFile.name |
|
||||
| pako.js:12:14:12:22 | req.files | pako.js:12:14:12:34 | req.fil ... ombFile |
|
||||
| pako.js:12:14:12:22 | req.files | pako.js:12:14:12:34 | req.fil ... ombFile |
|
||||
| pako.js:12:14:12:34 | req.fil ... ombFile | pako.js:12:14:12:39 | req.fil ... le.data |
|
||||
| pako.js:12:14:12:39 | req.fil ... le.data | pako.js:17:19:17:25 | zipFile |
|
||||
| pako.js:13:14:13:22 | req.files | pako.js:13:14:13:34 | req.fil ... ombFile |
|
||||
| pako.js:13:14:13:22 | req.files | pako.js:13:14:13:34 | req.fil ... ombFile |
|
||||
| pako.js:13:14:13:34 | req.fil ... ombFile | pako.js:13:14:13:39 | req.fil ... le.data |
|
||||
| pako.js:13:14:13:39 | req.fil ... le.data | pako.js:28:19:28:25 | zipFile |
|
||||
| pako.js:17:19:17:25 | zipFile | pako.js:18:48:18:54 | zipFile |
|
||||
| pako.js:18:11:18:68 | myArray | pako.js:21:31:21:37 | myArray |
|
||||
| pako.js:18:11:18:68 | myArray | pako.js:21:31:21:37 | myArray |
|
||||
| pako.js:18:21:18:68 | Buffer. ... uffer)) | pako.js:18:11:18:68 | myArray |
|
||||
| pako.js:18:33:18:67 | new Uin ... buffer) | pako.js:18:21:18:68 | Buffer. ... uffer)) |
|
||||
| pako.js:18:48:18:54 | zipFile | pako.js:18:48:18:59 | zipFile.data |
|
||||
| pako.js:18:48:18:59 | zipFile.data | pako.js:18:48:18:66 | zipFile.data.buffer |
|
||||
| pako.js:18:48:18:66 | zipFile.data.buffer | pako.js:18:33:18:67 | new Uin ... buffer) |
|
||||
| pako.js:28:19:28:25 | zipFile | pako.js:29:36:29:42 | zipFile |
|
||||
| pako.js:29:11:29:62 | myArray | pako.js:32:31:32:37 | myArray |
|
||||
| pako.js:29:11:29:62 | myArray | pako.js:32:31:32:37 | myArray |
|
||||
| pako.js:29:21:29:55 | new Uin ... buffer) | pako.js:29:21:29:62 | new Uin ... .buffer |
|
||||
| pako.js:29:21:29:62 | new Uin ... .buffer | pako.js:29:11:29:62 | myArray |
|
||||
| pako.js:29:36:29:42 | zipFile | pako.js:29:36:29:47 | zipFile.data |
|
||||
| pako.js:29:36:29:47 | zipFile.data | pako.js:29:36:29:54 | zipFile.data.buffer |
|
||||
| pako.js:29:36:29:54 | zipFile.data.buffer | pako.js:29:21:29:55 | new Uin ... buffer) |
|
||||
| unbzip2.js:12:5:12:43 | fs.crea ... lePath) | unbzip2.js:12:50:12:54 | bz2() |
|
||||
| unbzip2.js:12:5:12:43 | fs.crea ... lePath) | unbzip2.js:12:50:12:54 | bz2() |
|
||||
| unbzip2.js:12:25:12:42 | req.query.FilePath | unbzip2.js:12:5:12:43 | fs.crea ... lePath) |
|
||||
| unbzip2.js:12:25:12:42 | req.query.FilePath | unbzip2.js:12:5:12:43 | fs.crea ... lePath) |
|
||||
| unzipper.js:13:26:13:62 | Readabl ... e.data) | unzipper.js:16:23:16:63 | unzippe ... ath' }) |
|
||||
| unzipper.js:13:26:13:62 | Readabl ... e.data) | unzipper.js:16:23:16:63 | unzippe ... ath' }) |
|
||||
| unzipper.js:13:26:13:62 | Readabl ... e.data) | unzipper.js:19:23:19:41 | unzipper.ParseOne() |
|
||||
| unzipper.js:13:26:13:62 | Readabl ... e.data) | unzipper.js:19:23:19:41 | unzipper.ParseOne() |
|
||||
| unzipper.js:13:26:13:62 | Readabl ... e.data) | unzipper.js:24:15:24:30 | unzipper.Parse() |
|
||||
| unzipper.js:13:26:13:62 | Readabl ... e.data) | unzipper.js:24:15:24:30 | unzipper.Parse() |
|
||||
| unzipper.js:13:26:13:62 | Readabl ... e.data) | unzipper.js:34:15:34:30 | unzipper.Parse() |
|
||||
| unzipper.js:13:26:13:62 | Readabl ... e.data) | unzipper.js:34:15:34:30 | unzipper.Parse() |
|
||||
| unzipper.js:13:26:13:62 | Readabl ... e.data) | unzipper.js:41:35:41:71 | unzippe ... true }) |
|
||||
| unzipper.js:13:26:13:62 | Readabl ... e.data) | unzipper.js:41:35:41:71 | unzippe ... true }) |
|
||||
| unzipper.js:13:26:13:62 | Readabl ... e.data) | unzipper.js:51:36:51:72 | unzippe ... true }) |
|
||||
| unzipper.js:13:26:13:62 | Readabl ... e.data) | unzipper.js:51:36:51:72 | unzippe ... true }) |
|
||||
| unzipper.js:13:26:13:62 | Readabl ... e.data) | unzipper.js:60:23:60:38 | unzipper.Parse() |
|
||||
| unzipper.js:13:26:13:62 | Readabl ... e.data) | unzipper.js:60:23:60:38 | unzipper.Parse() |
|
||||
| unzipper.js:13:26:13:62 | Readabl ... e.data) | unzipper.js:73:23:73:38 | unzipper.Parse() |
|
||||
| unzipper.js:13:26:13:62 | Readabl ... e.data) | unzipper.js:73:23:73:38 | unzipper.Parse() |
|
||||
| unzipper.js:13:40:13:48 | req.files | unzipper.js:13:40:13:56 | req.files.ZipFile |
|
||||
| unzipper.js:13:40:13:48 | req.files | unzipper.js:13:40:13:56 | req.files.ZipFile |
|
||||
| unzipper.js:13:40:13:56 | req.files.ZipFile | unzipper.js:13:40:13:61 | req.fil ... le.data |
|
||||
| unzipper.js:13:40:13:61 | req.fil ... le.data | unzipper.js:13:26:13:62 | Readabl ... e.data) |
|
||||
| yauzl.js:12:18:12:26 | req.files | yauzl.js:12:18:12:34 | req.files.zipFile |
|
||||
| yauzl.js:12:18:12:26 | req.files | yauzl.js:12:18:12:34 | req.files.zipFile |
|
||||
| yauzl.js:12:18:12:34 | req.files.zipFile | yauzl.js:12:18:12:39 | req.fil ... le.data |
|
||||
| yauzl.js:12:18:12:34 | req.files.zipFile | yauzl.js:12:18:12:39 | req.fil ... le.data |
|
||||
| yauzl.js:13:22:13:30 | req.files | yauzl.js:13:22:13:38 | req.files.zipFile |
|
||||
| yauzl.js:13:22:13:30 | req.files | yauzl.js:13:22:13:38 | req.files.zipFile |
|
||||
| yauzl.js:13:22:13:38 | req.files.zipFile | yauzl.js:13:22:13:43 | req.fil ... le.data |
|
||||
| yauzl.js:13:22:13:38 | req.files.zipFile | yauzl.js:13:22:13:43 | req.fil ... le.data |
|
||||
| yauzl.js:14:34:14:42 | req.files | yauzl.js:14:34:14:50 | req.files.zipFile |
|
||||
| yauzl.js:14:34:14:42 | req.files | yauzl.js:14:34:14:50 | req.files.zipFile |
|
||||
| yauzl.js:14:34:14:50 | req.files.zipFile | yauzl.js:14:34:14:55 | req.fil ... le.data |
|
||||
| yauzl.js:14:34:14:50 | req.files.zipFile | yauzl.js:14:34:14:55 | req.fil ... le.data |
|
||||
| yauzl.js:37:16:37:33 | req.query.filePath | yauzl.js:39:9:39:27 | zipfile.readEntry() |
|
||||
| yauzl.js:37:16:37:33 | req.query.filePath | yauzl.js:39:9:39:27 | zipfile.readEntry() |
|
||||
| yauzl.js:37:16:37:33 | req.query.filePath | yauzl.js:39:9:39:27 | zipfile.readEntry() |
|
||||
| yauzl.js:37:16:37:33 | req.query.filePath | yauzl.js:39:9:39:27 | zipfile.readEntry() |
|
||||
| yauzl.js:37:16:37:33 | req.query.filePath | yauzl.js:41:64:41:73 | readStream |
|
||||
| yauzl.js:37:16:37:33 | req.query.filePath | yauzl.js:41:64:41:73 | readStream |
|
||||
| yauzl.js:37:16:37:33 | req.query.filePath | yauzl.js:41:64:41:73 | readStream |
|
||||
| yauzl.js:37:16:37:33 | req.query.filePath | yauzl.js:41:64:41:73 | readStream |
|
||||
| yauzl.js:37:16:37:33 | req.query.filePath | yauzl.js:43:21:43:39 | zipfile.readEntry() |
|
||||
| yauzl.js:37:16:37:33 | req.query.filePath | yauzl.js:43:21:43:39 | zipfile.readEntry() |
|
||||
| yauzl.js:37:16:37:33 | req.query.filePath | yauzl.js:43:21:43:39 | zipfile.readEntry() |
|
||||
| yauzl.js:37:16:37:33 | req.query.filePath | yauzl.js:43:21:43:39 | zipfile.readEntry() |
|
||||
| zlib.js:15:19:15:27 | req.files | zlib.js:15:19:15:39 | req.fil ... ombFile |
|
||||
| zlib.js:15:19:15:27 | req.files | zlib.js:15:19:15:39 | req.fil ... ombFile |
|
||||
| zlib.js:15:19:15:39 | req.fil ... ombFile | zlib.js:15:19:15:44 | req.fil ... le.data |
|
||||
| zlib.js:15:19:15:44 | req.fil ... le.data | zlib.js:27:24:27:30 | zipFile |
|
||||
| zlib.js:17:18:17:26 | req.files | zlib.js:17:18:17:38 | req.fil ... ombFile |
|
||||
| zlib.js:17:18:17:26 | req.files | zlib.js:17:18:17:38 | req.fil ... ombFile |
|
||||
| zlib.js:17:18:17:38 | req.fil ... ombFile | zlib.js:17:18:17:43 | req.fil ... le.data |
|
||||
| zlib.js:17:18:17:43 | req.fil ... le.data | zlib.js:62:23:62:29 | zipFile |
|
||||
| zlib.js:19:24:19:32 | req.files | zlib.js:19:24:19:44 | req.fil ... ombFile |
|
||||
| zlib.js:19:24:19:32 | req.files | zlib.js:19:24:19:44 | req.fil ... ombFile |
|
||||
| zlib.js:19:24:19:44 | req.fil ... ombFile | zlib.js:19:24:19:49 | req.fil ... le.data |
|
||||
| zlib.js:19:24:19:49 | req.fil ... le.data | zlib.js:74:29:74:35 | zipFile |
|
||||
| zlib.js:21:32:21:40 | req.files | zlib.js:21:32:21:52 | req.fil ... ombFile |
|
||||
| zlib.js:21:32:21:40 | req.files | zlib.js:21:32:21:52 | req.fil ... ombFile |
|
||||
| zlib.js:21:32:21:52 | req.fil ... ombFile | zlib.js:21:32:21:57 | req.fil ... le.data |
|
||||
| zlib.js:21:32:21:57 | req.fil ... le.data | zlib.js:82:43:82:49 | zipFile |
|
||||
| zlib.js:27:24:27:30 | zipFile | zlib.js:29:9:29:15 | zipFile |
|
||||
| zlib.js:27:24:27:30 | zipFile | zlib.js:33:9:33:15 | zipFile |
|
||||
| zlib.js:27:24:27:30 | zipFile | zlib.js:38:9:38:15 | zipFile |
|
||||
| zlib.js:29:9:29:15 | zipFile | zlib.js:29:9:29:20 | zipFile.data |
|
||||
| zlib.js:29:9:29:15 | zipFile | zlib.js:29:9:29:20 | zipFile.data |
|
||||
| zlib.js:33:9:33:15 | zipFile | zlib.js:33:9:33:20 | zipFile.data |
|
||||
| zlib.js:33:9:33:15 | zipFile | zlib.js:33:9:33:20 | zipFile.data |
|
||||
| zlib.js:38:9:38:15 | zipFile | zlib.js:38:9:38:20 | zipFile.data |
|
||||
| zlib.js:38:9:38:15 | zipFile | zlib.js:38:9:38:20 | zipFile.data |
|
||||
| zlib.js:62:23:62:29 | zipFile | zlib.js:63:21:63:27 | zipFile |
|
||||
| zlib.js:62:23:62:29 | zipFile | zlib.js:64:20:64:26 | zipFile |
|
||||
| zlib.js:62:23:62:29 | zipFile | zlib.js:65:31:65:37 | zipFile |
|
||||
| zlib.js:63:21:63:27 | zipFile | zlib.js:63:21:63:32 | zipFile.data |
|
||||
| zlib.js:63:21:63:27 | zipFile | zlib.js:63:21:63:32 | zipFile.data |
|
||||
| zlib.js:64:20:64:26 | zipFile | zlib.js:64:20:64:31 | zipFile.data |
|
||||
| zlib.js:64:20:64:26 | zipFile | zlib.js:64:20:64:31 | zipFile.data |
|
||||
| zlib.js:65:31:65:37 | zipFile | zlib.js:65:31:65:42 | zipFile.data |
|
||||
| zlib.js:65:31:65:37 | zipFile | zlib.js:65:31:65:42 | zipFile.data |
|
||||
| zlib.js:74:29:74:35 | zipFile | zlib.js:75:39:75:45 | zipFile |
|
||||
| zlib.js:75:25:75:51 | Readabl ... e.data) | zlib.js:77:22:77:40 | zlib.createGunzip() |
|
||||
| zlib.js:75:25:75:51 | Readabl ... e.data) | zlib.js:77:22:77:40 | zlib.createGunzip() |
|
||||
| zlib.js:75:25:75:51 | Readabl ... e.data) | zlib.js:78:22:78:39 | zlib.createUnzip() |
|
||||
| zlib.js:75:25:75:51 | Readabl ... e.data) | zlib.js:78:22:78:39 | zlib.createUnzip() |
|
||||
| zlib.js:75:25:75:51 | Readabl ... e.data) | zlib.js:79:22:79:50 | zlib.cr ... press() |
|
||||
| zlib.js:75:25:75:51 | Readabl ... e.data) | zlib.js:79:22:79:50 | zlib.cr ... press() |
|
||||
| zlib.js:75:39:75:45 | zipFile | zlib.js:75:39:75:50 | zipFile.data |
|
||||
| zlib.js:75:39:75:50 | zipFile.data | zlib.js:75:25:75:51 | Readabl ... e.data) |
|
||||
| zlib.js:82:43:82:49 | zipFile | zlib.js:83:39:83:45 | zipFile |
|
||||
| zlib.js:83:11:83:51 | inputStream | zlib.js:86:9:86:19 | inputStream |
|
||||
| zlib.js:83:25:83:51 | Readabl ... e.data) | zlib.js:83:11:83:51 | inputStream |
|
||||
| zlib.js:83:39:83:45 | zipFile | zlib.js:83:39:83:50 | zipFile.data |
|
||||
| zlib.js:83:39:83:50 | zipFile.data | zlib.js:83:25:83:51 | Readabl ... e.data) |
|
||||
| zlib.js:86:9:86:19 | inputStream | zlib.js:87:9:87:27 | zlib.createGunzip() |
|
||||
| zlib.js:86:9:86:19 | inputStream | zlib.js:87:9:87:27 | zlib.createGunzip() |
|
||||
#select
|
||||
| adm-zip.js:28:25:28:42 | zipEntry.getData() | adm-zip.js:13:13:13:21 | req.files | adm-zip.js:28:25:28:42 | zipEntry.getData() | This Decompression depends on a $@. | adm-zip.js:13:13:13:21 | req.files | potentially untrusted source |
|
||||
| adm-zip.js:32:17:32:41 | admZip. ... "10GB") | adm-zip.js:13:13:13:21 | req.files | adm-zip.js:32:17:32:41 | admZip. ... "10GB") | This Decompression depends on a $@. | adm-zip.js:13:13:13:21 | req.files | potentially untrusted source |
|
||||
| adm-zip.js:34:5:34:55 | admZip. ... , true) | adm-zip.js:13:13:13:21 | req.files | adm-zip.js:34:5:34:55 | admZip. ... , true) | This Decompression depends on a $@. | adm-zip.js:13:13:13:21 | req.files | potentially untrusted source |
|
||||
| adm-zip.js:36:5:36:38 | admZip. ... , true) | adm-zip.js:13:13:13:21 | req.files | adm-zip.js:36:5:36:38 | admZip. ... , true) | This Decompression depends on a $@. | adm-zip.js:13:13:13:21 | req.files | potentially untrusted source |
|
||||
| decompress.js:11:16:11:33 | req.query.filePath | decompress.js:11:16:11:33 | req.query.filePath | decompress.js:11:16:11:33 | req.query.filePath | This Decompression depends on a $@. | decompress.js:11:16:11:33 | req.query.filePath | potentially untrusted source |
|
||||
| jszip.js:33:22:33:33 | zipFile.data | jszip.js:12:13:12:21 | req.files | jszip.js:33:22:33:33 | zipFile.data | This Decompression depends on a $@. | jszip.js:12:13:12:21 | req.files | potentially untrusted source |
|
||||
| node-tar.js:24:9:24:15 | tar.x() | node-tar.js:15:13:15:21 | req.files | node-tar.js:24:9:24:15 | tar.x() | This Decompression depends on a $@. | node-tar.js:15:13:15:21 | req.files | potentially untrusted source |
|
||||
| node-tar.js:30:9:33:10 | tar.x({ ... }) | node-tar.js:15:13:15:21 | req.files | node-tar.js:30:9:33:10 | tar.x({ ... }) | This Decompression depends on a $@. | node-tar.js:15:13:15:21 | req.files | potentially untrusted source |
|
||||
| node-tar.js:48:9:50:10 | tar.x({ ... }) | node-tar.js:15:13:15:21 | req.files | node-tar.js:48:9:50:10 | tar.x({ ... }) | This Decompression depends on a $@. | node-tar.js:15:13:15:21 | req.files | potentially untrusted source |
|
||||
| node-tar.js:58:19:58:30 | tarFile.name | node-tar.js:15:13:15:21 | req.files | node-tar.js:58:19:58:30 | tarFile.name | This Decompression depends on a $@. | node-tar.js:15:13:15:21 | req.files | potentially untrusted source |
|
||||
| node-tar.js:59:25:59:36 | tarFile.name | node-tar.js:15:13:15:21 | req.files | node-tar.js:59:25:59:36 | tarFile.name | This Decompression depends on a $@. | node-tar.js:15:13:15:21 | req.files | potentially untrusted source |
|
||||
| pako.js:21:31:21:37 | myArray | pako.js:12:14:12:22 | req.files | pako.js:21:31:21:37 | myArray | This Decompression depends on a $@. | pako.js:12:14:12:22 | req.files | potentially untrusted source |
|
||||
| pako.js:32:31:32:37 | myArray | pako.js:13:14:13:22 | req.files | pako.js:32:31:32:37 | myArray | This Decompression depends on a $@. | pako.js:13:14:13:22 | req.files | potentially untrusted source |
|
||||
| unbzip2.js:12:50:12:54 | bz2() | unbzip2.js:12:25:12:42 | req.query.FilePath | unbzip2.js:12:50:12:54 | bz2() | This Decompression depends on a $@. | unbzip2.js:12:25:12:42 | req.query.FilePath | potentially untrusted source |
|
||||
| unzipper.js:16:23:16:63 | unzippe ... ath' }) | unzipper.js:13:40:13:48 | req.files | unzipper.js:16:23:16:63 | unzippe ... ath' }) | This Decompression depends on a $@. | unzipper.js:13:40:13:48 | req.files | potentially untrusted source |
|
||||
| unzipper.js:19:23:19:41 | unzipper.ParseOne() | unzipper.js:13:40:13:48 | req.files | unzipper.js:19:23:19:41 | unzipper.ParseOne() | This Decompression depends on a $@. | unzipper.js:13:40:13:48 | req.files | potentially untrusted source |
|
||||
| unzipper.js:24:15:24:30 | unzipper.Parse() | unzipper.js:13:40:13:48 | req.files | unzipper.js:24:15:24:30 | unzipper.Parse() | This Decompression depends on a $@. | unzipper.js:13:40:13:48 | req.files | potentially untrusted source |
|
||||
| unzipper.js:34:15:34:30 | unzipper.Parse() | unzipper.js:13:40:13:48 | req.files | unzipper.js:34:15:34:30 | unzipper.Parse() | This Decompression depends on a $@. | unzipper.js:13:40:13:48 | req.files | potentially untrusted source |
|
||||
| unzipper.js:41:35:41:71 | unzippe ... true }) | unzipper.js:13:40:13:48 | req.files | unzipper.js:41:35:41:71 | unzippe ... true }) | This Decompression depends on a $@. | unzipper.js:13:40:13:48 | req.files | potentially untrusted source |
|
||||
| unzipper.js:51:36:51:72 | unzippe ... true }) | unzipper.js:13:40:13:48 | req.files | unzipper.js:51:36:51:72 | unzippe ... true }) | This Decompression depends on a $@. | unzipper.js:13:40:13:48 | req.files | potentially untrusted source |
|
||||
| unzipper.js:60:23:60:38 | unzipper.Parse() | unzipper.js:13:40:13:48 | req.files | unzipper.js:60:23:60:38 | unzipper.Parse() | This Decompression depends on a $@. | unzipper.js:13:40:13:48 | req.files | potentially untrusted source |
|
||||
| unzipper.js:73:23:73:38 | unzipper.Parse() | unzipper.js:13:40:13:48 | req.files | unzipper.js:73:23:73:38 | unzipper.Parse() | This Decompression depends on a $@. | unzipper.js:13:40:13:48 | req.files | potentially untrusted source |
|
||||
| yauzl.js:12:18:12:39 | req.fil ... le.data | yauzl.js:12:18:12:26 | req.files | yauzl.js:12:18:12:39 | req.fil ... le.data | This Decompression depends on a $@. | yauzl.js:12:18:12:26 | req.files | potentially untrusted source |
|
||||
| yauzl.js:13:22:13:43 | req.fil ... le.data | yauzl.js:13:22:13:30 | req.files | yauzl.js:13:22:13:43 | req.fil ... le.data | This Decompression depends on a $@. | yauzl.js:13:22:13:30 | req.files | potentially untrusted source |
|
||||
| yauzl.js:14:34:14:55 | req.fil ... le.data | yauzl.js:14:34:14:42 | req.files | yauzl.js:14:34:14:55 | req.fil ... le.data | This Decompression depends on a $@. | yauzl.js:14:34:14:42 | req.files | potentially untrusted source |
|
||||
| yauzl.js:39:9:39:27 | zipfile.readEntry() | yauzl.js:37:16:37:33 | req.query.filePath | yauzl.js:39:9:39:27 | zipfile.readEntry() | This Decompression depends on a $@. | yauzl.js:37:16:37:33 | req.query.filePath | potentially untrusted source |
|
||||
| yauzl.js:41:64:41:73 | readStream | yauzl.js:37:16:37:33 | req.query.filePath | yauzl.js:41:64:41:73 | readStream | This Decompression depends on a $@. | yauzl.js:37:16:37:33 | req.query.filePath | potentially untrusted source |
|
||||
| yauzl.js:43:21:43:39 | zipfile.readEntry() | yauzl.js:37:16:37:33 | req.query.filePath | yauzl.js:43:21:43:39 | zipfile.readEntry() | This Decompression depends on a $@. | yauzl.js:37:16:37:33 | req.query.filePath | potentially untrusted source |
|
||||
| zlib.js:29:9:29:20 | zipFile.data | zlib.js:15:19:15:27 | req.files | zlib.js:29:9:29:20 | zipFile.data | This Decompression depends on a $@. | zlib.js:15:19:15:27 | req.files | potentially untrusted source |
|
||||
| zlib.js:33:9:33:20 | zipFile.data | zlib.js:15:19:15:27 | req.files | zlib.js:33:9:33:20 | zipFile.data | This Decompression depends on a $@. | zlib.js:15:19:15:27 | req.files | potentially untrusted source |
|
||||
| zlib.js:38:9:38:20 | zipFile.data | zlib.js:15:19:15:27 | req.files | zlib.js:38:9:38:20 | zipFile.data | This Decompression depends on a $@. | zlib.js:15:19:15:27 | req.files | potentially untrusted source |
|
||||
| zlib.js:63:21:63:32 | zipFile.data | zlib.js:17:18:17:26 | req.files | zlib.js:63:21:63:32 | zipFile.data | This Decompression depends on a $@. | zlib.js:17:18:17:26 | req.files | potentially untrusted source |
|
||||
| zlib.js:64:20:64:31 | zipFile.data | zlib.js:17:18:17:26 | req.files | zlib.js:64:20:64:31 | zipFile.data | This Decompression depends on a $@. | zlib.js:17:18:17:26 | req.files | potentially untrusted source |
|
||||
| zlib.js:65:31:65:42 | zipFile.data | zlib.js:17:18:17:26 | req.files | zlib.js:65:31:65:42 | zipFile.data | This Decompression depends on a $@. | zlib.js:17:18:17:26 | req.files | potentially untrusted source |
|
||||
| zlib.js:77:22:77:40 | zlib.createGunzip() | zlib.js:19:24:19:32 | req.files | zlib.js:77:22:77:40 | zlib.createGunzip() | This Decompression depends on a $@. | zlib.js:19:24:19:32 | req.files | potentially untrusted source |
|
||||
| zlib.js:78:22:78:39 | zlib.createUnzip() | zlib.js:19:24:19:32 | req.files | zlib.js:78:22:78:39 | zlib.createUnzip() | This Decompression depends on a $@. | zlib.js:19:24:19:32 | req.files | potentially untrusted source |
|
||||
| zlib.js:79:22:79:50 | zlib.cr ... press() | zlib.js:19:24:19:32 | req.files | zlib.js:79:22:79:50 | zlib.cr ... press() | This Decompression depends on a $@. | zlib.js:19:24:19:32 | req.files | potentially untrusted source |
|
||||
| zlib.js:87:9:87:27 | zlib.createGunzip() | zlib.js:21:32:21:40 | req.files | zlib.js:87:9:87:27 | zlib.createGunzip() | This Decompression depends on a $@. | zlib.js:21:32:21:40 | req.files | potentially untrusted source |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE-522-DecompressionBombs/DecompressionBombs.ql
|
||||
@@ -0,0 +1,37 @@
|
||||
const AdmZip = require("adm-zip");
|
||||
const express = require('express')
|
||||
const fileUpload = require("express-fileupload");
|
||||
const fs = require("fs");
|
||||
const app = express();
|
||||
const port = 3000;
|
||||
app.use(fileUpload());
|
||||
app.listen(port, () => {
|
||||
console.log(`Example app listening on port ${port}`)
|
||||
});
|
||||
|
||||
app.post('/upload', (req, res) => {
|
||||
zipBomb(req.files.zipBombFile)
|
||||
res.send('Hello World!')
|
||||
});
|
||||
|
||||
function zipBomb(tarFile) {
|
||||
fs.writeFileSync(tarFile.name, tarFile.data);
|
||||
// or using fs.writeFile
|
||||
|
||||
// file path is a tmp file name that can get from DB after saving to DB with remote file upload
|
||||
// so the input file name will come from a DB source
|
||||
const admZip
|
||||
= new AdmZip(tarFile.data);
|
||||
const zipEntries = admZip.getEntries();
|
||||
zipEntries.forEach(function (zipEntry) {
|
||||
if (zipEntry.entryName === "my_file.txt") {
|
||||
console.log(zipEntry.getData().toString("utf8"));
|
||||
}
|
||||
});
|
||||
// outputs the content of file named 10GB
|
||||
console.log(admZip.readAsText("10GB"));
|
||||
// extracts the specified file to the specified location
|
||||
admZip.extractEntryTo("10GB", "/tmp/", false, true);
|
||||
// extracts everything
|
||||
admZip.extractAllTo("./tmp", true);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
const decompress = require('decompress');
|
||||
const express = require('express')
|
||||
const fileUpload = require("express-fileupload");
|
||||
|
||||
const app = express();
|
||||
app.use(fileUpload());
|
||||
app.listen(3000, () => {
|
||||
});
|
||||
|
||||
app.post('/upload', async (req, res) => {
|
||||
decompress(req.query.filePath, 'dist').then(files => {
|
||||
console.log('done!');
|
||||
});
|
||||
|
||||
res.send("OK")
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user