Merge branch 'main' into js/shared-dataflow-merge-main

This commit is contained in:
Asger F
2024-08-22 13:22:05 +02:00
1277 changed files with 120219 additions and 12839 deletions

View File

@@ -305,6 +305,15 @@ private predicate isRequire(EarlyStageNode nd) {
isCreateRequire(TValueNode(call.getCallee())) and
nd = TValueNode(call)
)
or
// `$.require('underscore');`.
// NPM as supported in [XSJS files](https://www.npmjs.com/package/@sap/async-xsjs#npm-packages-support).
exists(MethodCallExpr require |
nd.getFile().getExtension() = ["xsjs", "xsjslib"] and
require.getCalleeName() = "require" and
require.getReceiver().(GlobalVarAccess).getName() = "$" and
nd = require.getCallee().flow()
)
}
/**

View File

@@ -12,7 +12,7 @@ import javascript
abstract class CredentialsNode extends DataFlow::Node {
/**
* Gets a description of the kind of credential this expression is used as,
* such as `"user name"`, `"password"`, `"key"`.
* such as `"user name"`, `"password"`, `"key"`, `"jwt key"`.
*/
abstract string getCredentialsKind();
}

View File

@@ -40,11 +40,111 @@ private module JsonWebToken {
}
/**
* The private key for a JWT as a `CredentialsNode`.
* The secret or PrivateKey for a JWT as a `CredentialsNode`.
*/
private class JwtKey extends CredentialsNode {
JwtKey() { this = DataFlow::moduleMember("jsonwebtoken", "sign").getACall().getArgument(1) }
JwtKey() {
this =
API::moduleImport("jsonwebtoken").getMember(["sign", "verify"]).getParameter(1).asSink()
}
override string getCredentialsKind() { result = "key" }
override string getCredentialsKind() { result = "jwt key" }
}
}
/**
* Provides classes and predicates modeling the `jose` library.
*/
private module Jose {
/**
* The asymmetric key or symmetric secret for verifying a JWT as a `CredentialsNode`.
*/
private class JwtVerifyKey extends CredentialsNode {
JwtVerifyKey() {
this = API::moduleImport("jose").getMember("jwtVerify").getParameter(1).asSink()
}
override string getCredentialsKind() { result = "jwt key" }
}
}
/**
* Provides classes and predicates modeling the `jwt-simple` library.
*/
private module JwtSimple {
/**
* The asymmetric key or symmetric secret for a JWT as a `CredentialsNode`.
*/
private class JwtKey extends CredentialsNode {
JwtKey() { this = API::moduleImport("jwt-simple").getMember("decode").getParameter(1).asSink() }
override string getCredentialsKind() { result = "jwt key" }
}
}
/**
* Provides classes and predicates modeling the `koa-jwt` library.
*/
private module KoaJwt {
/**
* The shared secret for a JWT as a `CredentialsNode`.
*/
private class SharedSecret extends CredentialsNode {
SharedSecret() {
this = API::moduleImport("koa-jwt").getParameter(0).getMember("secret").asSink()
}
override string getCredentialsKind() { result = "jwt key" }
}
}
/**
* Provides classes and predicates modeling the `express-jwt` library.
*/
private module ExpressJwt {
/**
* The shared secret for a JWT as a `CredentialsNode`.
*/
private class SharedSecret extends CredentialsNode {
SharedSecret() {
this =
API::moduleImport("express-jwt")
.getMember("expressjwt")
.getParameter(0)
.getMember("secret")
.asSink()
}
override string getCredentialsKind() { result = "jwt key" }
}
}
/**
* Provides classes and predicates modeling the `passport-jwt` library.
*/
private module PassportJwt {
/**
* The secret (symmetric) or PEM-encoded public key (asymmetric) for a JWT as a `CredentialsNode`.
*/
private class JwtKey extends CredentialsNode {
JwtKey() {
this =
API::moduleImport("passport-jwt")
.getMember("Strategy")
.getParameter(0)
.getMember("secretOrKey")
.asSink()
or
this =
API::moduleImport("passport-jwt")
.getMember("Strategy")
.getParameter(0)
.getMember("secretOrKeyProvider")
.getParameter(2)
.getParameter(1)
.asSink()
}
override string getCredentialsKind() { result = "jwt key" }
}
}

View File

@@ -255,4 +255,20 @@ module NextJS {
.getMember("router")
.asSource()
}
/**
* Provides classes and predicates modeling the `next-auth` library.
*/
private module NextAuth {
/**
* A random string used to hash tokens, sign cookies and generate cryptographic keys as a `CredentialsNode`.
*/
private class SecretKey extends CredentialsNode {
SecretKey() {
this = API::moduleImport("next-auth").getParameter(0).getMember("secret").asSink()
}
override string getCredentialsKind() { result = "jwt key" }
}
}
}

View File

@@ -45,3 +45,25 @@ extensible predicate typeModel(string type1, string type2, string path);
* Holds if `path` can be substituted for a token `TypeVar[name]`.
*/
extensible predicate typeVariableModel(string name, string 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 |
sourceModel(type, path, kind, madId) and
model = "Source: " + type + "; " + path + "; " + kind
)
or
exists(string type, string path, string kind |
sinkModel(type, path, kind, madId) and
model = "Sink: " + type + "; " + path + "; " + kind
)
or
exists(string type, string path, string input, string output, string kind |
summaryModel(type, path, input, output, kind, madId) and
model = "Summary: " + type + "; " + path + "; " + input + "; " + output + "; " + kind
)
}

View File

@@ -1,6 +1,6 @@
extensions:
- addsTo:
pack: codeql/javascript-queries
pack: codeql/javascript-all
extensible: requiredHelmetSecuritySetting
data:
- ["frameguard"]

View File

@@ -0,0 +1,41 @@
/**
* Provides classes for working with Helmet
*/
private import javascript
/**
* A write to a property of a route handler from the "helmet" module.
*/
class HelmetProperty extends DataFlow::Node instanceof DataFlow::PropWrite {
ExpressLibraries::HelmetRouteHandler helmet;
HelmetProperty() {
this = helmet.(DataFlow::CallNode).getAnArgument().getALocalSource().getAPropertyWrite()
}
/**
* Gets the route handler associated to this property.
*/
ExpressLibraries::HelmetRouteHandler getHelmet() { result = helmet }
/**
* Gets the boolean value of this property, if it may evaluate to a `Boolean`.
*/
predicate isFalse() { DataFlow::PropWrite.super.getRhs().mayHaveBooleanValue(false) }
/**
* Gets the name of the `HelmetProperty`.
*/
string getName() { result = DataFlow::PropWrite.super.getPropertyName() }
/**
* read from data extensions to allow enforcing custom settings
*/
predicate isImportantSecuritySetting() { requiredHelmetSecuritySetting(this.getName()) }
}
/**
* defaults are located in `javascript/ql/lib/semmle/frameworks/helmet/Helmet.Required.Setting.model.yml`
*/
extensible predicate requiredHelmetSecuritySetting(string name);

View File

@@ -247,6 +247,15 @@ module ClientSideUrlRedirect {
override predicate isXssSink() { any() }
}
/**
* A `templateUrl` member of an AngularJS directive.
*/
private class AngularJSTemplateUrlSink extends Sink {
AngularJSTemplateUrlSink() { this = any(AngularJS::CustomDirective d).getMember("templateUrl") }
override predicate isXssSink() { any() }
}
private class SinkFromModel extends Sink {
SinkFromModel() { this = ModelOutput::getASinkNode("url-redirection").asSink() }
}

View File

@@ -207,12 +207,14 @@ class PostMessageEventHandler extends Function {
* An event parameter for a `postMessage` event handler, considered as an untrusted
* source of data.
*/
private class PostMessageEventParameter extends RemoteFlowSource {
private class PostMessageEventParameter extends ClientSideRemoteFlowSource {
PostMessageEventParameter() {
this = DataFlow::parameterNode(any(PostMessageEventHandler pmeh).getEventParameter())
}
override string getSourceType() { result = "postMessage event" }
override ClientSideRemoteFlowKind getKind() { result.isMessageEvent() }
}
/**

View File

@@ -4,6 +4,7 @@
* own.
*/
import semmle.javascript.filters.ClassifyFiles
import javascript
private import semmle.javascript.security.SensitiveActions
@@ -38,5 +39,9 @@ module HardcodedCredentials {
*/
class DefaultCredentialsSink extends Sink instanceof CredentialsNode {
override string getKind() { result = super.getCredentialsKind() }
DefaultCredentialsSink() {
not (super.getCredentialsKind() = "jwt key" and isTestFile(this.getFile()))
}
}
}

View File

@@ -30,6 +30,44 @@ module HardcodedCredentialsConfig implements DataFlow::ConfigSig {
trg = bufferFrom and
src = bufferFrom.getArgument(0)
)
or
exists(API::Node n |
n = API::moduleImport("jose").getMember(["importSPKI", "importPKCS8", "importX509"])
|
src = n.getACall().getArgument(0) and
trg = n.getReturn().getPromised().asSource()
)
or
exists(API::Node n |
n = API::moduleImport("jose").getMember(["importSPKI", "importPKCS8", "importX509"])
|
src = n.getACall().getArgument(0) and
trg = n.getReturn().getPromised().asSource()
)
or
exists(API::Node n | n = API::moduleImport("jose").getMember("importJWK") |
src = n.getParameter(0).getMember(["x", "y", "n"]).asSink() and
trg = n.getReturn().getPromised().asSource()
)
or
exists(DataFlow::CallNode n |
n = DataFlow::globalVarRef("TextEncoder").getAnInstantiation().getAMemberCall("encode")
|
src = n.getArgument(0) and
trg = n
)
or
exists(DataFlow::CallNode n | n = DataFlow::globalVarRef("Buffer").getAMemberCall("from") |
src = n.getArgument(0) and
trg = [n, n.getAChainedMethodCall(["toString", "toJSON"])]
)
or
exists(API::Node n |
n = API::moduleImport("jose").getMember("base64url").getMember(["decode", "encode"])
|
src = n.getACall().getArgument(0) and
trg = n.getACall()
)
}
}

View File

@@ -40,7 +40,9 @@ import Cached
* A type of remote flow source that is specific to the browser environment.
*/
class ClientSideRemoteFlowKind extends string {
ClientSideRemoteFlowKind() { this = ["query", "fragment", "path", "url", "name"] }
ClientSideRemoteFlowKind() {
this = ["query", "fragment", "path", "url", "name", "message-event"]
}
/**
* Holds if this is the `query` kind, describing sources derived from the query parameters of the browser URL,
@@ -77,6 +79,12 @@ class ClientSideRemoteFlowKind extends string {
/** Holds if this is the `name` kind, describing sources derived from the window name, such as `window.name`. */
predicate isWindowName() { this = "name" }
/**
* Holds if this is the `message-event` kind, describing sources derived from cross-window message passing,
* such as `event` in `window.onmessage = event => {...}`.
*/
predicate isMessageEvent() { this = "message-event" }
}
/**

View File

@@ -674,10 +674,11 @@ module TaintedPath {
}
/**
* A `templateUrl` member of an AngularJS directive.
* DEPRECATED. This is no longer seen as a path-injection sink. It is tentatively handled
* by the client-side URL redirection query for now.
*/
class AngularJSTemplateUrlSink extends Sink, DataFlow::ValueNode {
AngularJSTemplateUrlSink() { this = any(AngularJS::CustomDirective d).getMember("templateUrl") }
deprecated class AngularJSTemplateUrlSink extends DataFlow::ValueNode instanceof Sink {
AngularJSTemplateUrlSink() { none() }
}
/**