Merge branch 'github:main' into main

This commit is contained in:
Mauro Baluda
2024-06-21 13:36:38 +02:00
committed by GitHub
417 changed files with 7877 additions and 4033 deletions

View File

@@ -33,6 +33,5 @@ codeql_pack(
"//javascript/extractor:tools-extractor",
"//javascript/resources",
],
visibility = ["//visibility:public"],
zips = {"//javascript/extractor/lib/typescript": "tools"},
)

View File

@@ -115,7 +115,7 @@ public class Identifiers {
// rare.
private static boolean isInAstralSet(int code, int[] set) {
int pos = 0x10000;
for (int i = 0; i < set.length; i += 2) {
for (int i = 0; i < set.length - 1; i += 2) {
pos += set[i];
if (pos > code) return false;
pos += set[i + 1];

View File

@@ -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

View File

@@ -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::SharedFlowStep {
private class ArrayFrom extends PreCallGraphStep {
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::SharedFlowStep {
private class ArrayCopySpread extends PreCallGraphStep {
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::SharedFlowStep {
private class ArrayAppendStep extends PreCallGraphStep {
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::SharedFlowStep {
private class ArrayIndexingStep extends PreCallGraphStep {
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::SharedFlowStep {
private class ArrayPopStep extends PreCallGraphStep {
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::SharedFlowStep {
private class ArraySpliceStep extends PreCallGraphStep {
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::SharedFlowStep {
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
private class ArrayConcatStep extends PreCallGraphStep {
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::SharedFlowStep {
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
private class ArraySliceStep extends PreCallGraphStep {
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::SharedFlowStep {
private class ArrayFindStep extends PreCallGraphStep {
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::SharedFlowStep {
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
private class ArrayCopyLoadStore extends PreCallGraphStep {
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
exists(DataFlow::CallNode call |
call = arrayCopyCall(pred) and
succ = call and

View File

@@ -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>

View File

@@ -0,0 +1,6 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<include src="CodeInjection.inc.qhelp" />
</qhelp>

View File

@@ -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"

View File

@@ -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)
});

View File

@@ -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>

View File

@@ -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"

View File

@@ -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>

View File

@@ -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"

View File

@@ -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!');
});

View File

@@ -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!');
});

View File

@@ -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}`)
})

View File

@@ -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}`)
})

View 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()
)
}

View File

@@ -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>

View File

@@ -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"

View File

@@ -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"

View File

@@ -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 |

View File

@@ -0,0 +1 @@
experimental/Security/CWE-094-dataURL/CodeInjection.ql

View File

@@ -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
});

View File

@@ -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 |

View File

@@ -0,0 +1 @@
experimental/Security/CWE-099/EnvValueAndKeyInjection.ql

View File

@@ -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!');
});

View File

@@ -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 |

View File

@@ -0,0 +1 @@
experimental/Security/CWE-099/EnvValueInjection.ql

View File

@@ -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!');
});

View File

@@ -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
})();

View File

@@ -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 |

View File

@@ -0,0 +1 @@
experimental/Security/CWE-347/decodeJwtWithoutVerificationLocalSource.ql

View File

@@ -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
})();

View File

@@ -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
})();

View File

@@ -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
})();

View File

@@ -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}`)
})

View File

@@ -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 |

View File

@@ -0,0 +1 @@
experimental/Security/CWE-347/decodeJwtWithoutVerification.ql

View File

@@ -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}`)
})

View File

@@ -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}`)
})

View File

@@ -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}`)
})

View File

@@ -3,20 +3,22 @@
| 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() |

View File

@@ -3,24 +3,26 @@
| 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) |

View File

@@ -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

View File

@@ -26,6 +26,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()) |

View File

@@ -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.

View File

@@ -1,5 +1,5 @@
WARNING: Unused method getMethod (query2.ql:31,14-23)
WARNING: Unused method getResponse (query2.ql:45,13-24)
WARNING: unused method 'getMethod' (query2.ql:31,14-23)
WARNING: unused method 'getResponse' (query2.ql:45,13-24)
| robonode/src/assets/raml/api.raml:8:3:72:14 | get: | /robots |
| robonode/src/assets/raml/api.raml:15:5:72:14 | uriParameters: | /robots/{robotId} |
| robonode/src/assets/raml/api.raml:24:7:54:4 | get: | /robots/{robotId}/commands |

View File

@@ -1,2 +1,2 @@
WARNING: Unused method getMethod (query3.ql:31,14-23)
WARNING: unused method 'getMethod' (query3.ql:31,14-23)
| 1 |

View File

@@ -1,5 +1,5 @@
WARNING: Unused method getMethod (query4.ql:31,14-23)
WARNING: Unused method getResponse (query4.ql:45,13-24)
WARNING: unused method 'getMethod' (query4.ql:31,14-23)
WARNING: unused method 'getResponse' (query4.ql:45,13-24)
| robonode/src/assets/raml/api.raml:9:5:14:2 | description: \| |
| robonode/src/assets/raml/api.raml:25:9:29:6 | description: \| |
| robonode/src/assets/raml/api.raml:30:9:54:4 | description: \| |