mirror of
https://github.com/github/codeql.git
synced 2026-04-28 02:05:14 +02:00
Merge branch 'main' into js/shared-dataflow-merge-main
This commit is contained in:
@@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/javascript-queries
|
||||
pack: codeql/javascript-all
|
||||
extensible: requiredHelmetSecuritySetting
|
||||
data:
|
||||
- ["frameguard"]
|
||||
|
||||
@@ -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);
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user