From 358617f53365db5b808bc668f0aae704e56790c2 Mon Sep 17 00:00:00 2001
From: Napalys Klicius
Date: Wed, 30 Jul 2025 09:49:04 +0000
Subject: [PATCH 01/15] Move CORS misconfiguration query from experimental to
Security
---
.../semmle/javascript/frameworks}/Apollo.qll | 0
.../semmle/javascript/frameworks}/Cors.qll | 0
.../CorsPermissiveConfigurationCustomizations.qll | 10 +++++-----
.../security}/CorsPermissiveConfigurationQuery.qll | 0
.../CWE-942/CorsPermissiveConfiguration.qhelp | 0
.../Security/CWE-942/CorsPermissiveConfiguration.ql | 11 ++++++-----
.../examples/CorsPermissiveConfigurationBad.js | 0
.../examples/CorsPermissiveConfigurationGood.js | 0
.../CWE-942/CorsPermissiveConfiguration.qlref | 1 -
.../CWE-942/CorsPermissiveConfiguration.expected | 0
.../CWE-942/CorsPermissiveConfiguration.qlref | 1 +
.../Security/CWE-942/apollo-test.js | 0
.../Security/CWE-942/express-test.js | 0
13 files changed, 12 insertions(+), 11 deletions(-)
rename javascript/ql/{src/experimental/Security/CWE-942 => lib/semmle/javascript/frameworks}/Apollo.qll (100%)
rename javascript/ql/{src/experimental/Security/CWE-942 => lib/semmle/javascript/frameworks}/Cors.qll (100%)
rename javascript/ql/{src/experimental/Security/CWE-942 => lib/semmle/javascript/security}/CorsPermissiveConfigurationCustomizations.qll (94%)
rename javascript/ql/{src/experimental/Security/CWE-942 => lib/semmle/javascript/security}/CorsPermissiveConfigurationQuery.qll (100%)
rename javascript/ql/src/{experimental => }/Security/CWE-942/CorsPermissiveConfiguration.qhelp (100%)
rename javascript/ql/src/{experimental => }/Security/CWE-942/CorsPermissiveConfiguration.ql (53%)
rename javascript/ql/src/{experimental => }/Security/CWE-942/examples/CorsPermissiveConfigurationBad.js (100%)
rename javascript/ql/src/{experimental => }/Security/CWE-942/examples/CorsPermissiveConfigurationGood.js (100%)
delete mode 100644 javascript/ql/test/experimental/Security/CWE-942/CorsPermissiveConfiguration.qlref
rename javascript/ql/test/{experimental => query-tests}/Security/CWE-942/CorsPermissiveConfiguration.expected (100%)
create mode 100644 javascript/ql/test/query-tests/Security/CWE-942/CorsPermissiveConfiguration.qlref
rename javascript/ql/test/{experimental => query-tests}/Security/CWE-942/apollo-test.js (100%)
rename javascript/ql/test/{experimental => query-tests}/Security/CWE-942/express-test.js (100%)
diff --git a/javascript/ql/src/experimental/Security/CWE-942/Apollo.qll b/javascript/ql/lib/semmle/javascript/frameworks/Apollo.qll
similarity index 100%
rename from javascript/ql/src/experimental/Security/CWE-942/Apollo.qll
rename to javascript/ql/lib/semmle/javascript/frameworks/Apollo.qll
diff --git a/javascript/ql/src/experimental/Security/CWE-942/Cors.qll b/javascript/ql/lib/semmle/javascript/frameworks/Cors.qll
similarity index 100%
rename from javascript/ql/src/experimental/Security/CWE-942/Cors.qll
rename to javascript/ql/lib/semmle/javascript/frameworks/Cors.qll
diff --git a/javascript/ql/src/experimental/Security/CWE-942/CorsPermissiveConfigurationCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationCustomizations.qll
similarity index 94%
rename from javascript/ql/src/experimental/Security/CWE-942/CorsPermissiveConfigurationCustomizations.qll
rename to javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationCustomizations.qll
index 8876373a3d24..b642b98b35ba 100644
--- a/javascript/ql/src/experimental/Security/CWE-942/CorsPermissiveConfigurationCustomizations.qll
+++ b/javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationCustomizations.qll
@@ -5,8 +5,8 @@
*/
import javascript
-import Cors::Cors
-import Apollo::Apollo
+private import semmle.javascript.frameworks.Apollo
+private import semmle.javascript.frameworks.Cors
/** Module containing sources, sinks, and sanitizers for overly permissive CORS configurations. */
module CorsPermissiveConfiguration {
@@ -105,7 +105,7 @@ module CorsPermissiveConfiguration {
*/
class CorsApolloServer extends Sink, DataFlow::ValueNode {
CorsApolloServer() {
- exists(ApolloServer agql |
+ exists(Apollo::ApolloServer agql |
this =
agql.getOptionArgument(0, "cors").getALocalSource().getAPropertyWrite("origin").getRhs()
)
@@ -125,7 +125,7 @@ module CorsPermissiveConfiguration {
* An express route setup configured with the `cors` package.
*/
class CorsConfiguration extends DataFlow::MethodCallNode {
- Cors corsConfig;
+ Cors::Cors corsConfig;
CorsConfiguration() {
exists(Express::RouteSetup setup | this = setup |
@@ -136,6 +136,6 @@ module CorsPermissiveConfiguration {
}
/** Gets the expression that configures `cors` on this route setup. */
- Cors getCorsConfiguration() { result = corsConfig }
+ Cors::Cors getCorsConfiguration() { result = corsConfig }
}
}
diff --git a/javascript/ql/src/experimental/Security/CWE-942/CorsPermissiveConfigurationQuery.qll b/javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationQuery.qll
similarity index 100%
rename from javascript/ql/src/experimental/Security/CWE-942/CorsPermissiveConfigurationQuery.qll
rename to javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationQuery.qll
diff --git a/javascript/ql/src/experimental/Security/CWE-942/CorsPermissiveConfiguration.qhelp b/javascript/ql/src/Security/CWE-942/CorsPermissiveConfiguration.qhelp
similarity index 100%
rename from javascript/ql/src/experimental/Security/CWE-942/CorsPermissiveConfiguration.qhelp
rename to javascript/ql/src/Security/CWE-942/CorsPermissiveConfiguration.qhelp
diff --git a/javascript/ql/src/experimental/Security/CWE-942/CorsPermissiveConfiguration.ql b/javascript/ql/src/Security/CWE-942/CorsPermissiveConfiguration.ql
similarity index 53%
rename from javascript/ql/src/experimental/Security/CWE-942/CorsPermissiveConfiguration.ql
rename to javascript/ql/src/Security/CWE-942/CorsPermissiveConfiguration.ql
index 87db66ad98d9..050842028585 100644
--- a/javascript/ql/src/experimental/Security/CWE-942/CorsPermissiveConfiguration.ql
+++ b/javascript/ql/src/Security/CWE-942/CorsPermissiveConfiguration.ql
@@ -1,5 +1,5 @@
/**
- * @name overly CORS configuration
+ * @name Permissive CORS configuration
* @description Misconfiguration of CORS HTTP headers allows CSRF attacks.
* @kind path-problem
* @problem.severity error
@@ -11,11 +11,12 @@
*/
import javascript
-import CorsPermissiveConfigurationQuery
-import CorsPermissiveConfigurationFlow::PathGraph
+import semmle.javascript.security.CorsPermissiveConfigurationQuery as CorsQuery
+import CorsQuery::CorsPermissiveConfigurationFlow::PathGraph
from
- CorsPermissiveConfigurationFlow::PathNode source, CorsPermissiveConfigurationFlow::PathNode sink
-where CorsPermissiveConfigurationFlow::flowPath(source, sink)
+ CorsQuery::CorsPermissiveConfigurationFlow::PathNode source,
+ CorsQuery::CorsPermissiveConfigurationFlow::PathNode sink
+where CorsQuery::CorsPermissiveConfigurationFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "CORS Origin misconfiguration due to a $@.", source.getNode(),
"too permissive or user controlled value"
diff --git a/javascript/ql/src/experimental/Security/CWE-942/examples/CorsPermissiveConfigurationBad.js b/javascript/ql/src/Security/CWE-942/examples/CorsPermissiveConfigurationBad.js
similarity index 100%
rename from javascript/ql/src/experimental/Security/CWE-942/examples/CorsPermissiveConfigurationBad.js
rename to javascript/ql/src/Security/CWE-942/examples/CorsPermissiveConfigurationBad.js
diff --git a/javascript/ql/src/experimental/Security/CWE-942/examples/CorsPermissiveConfigurationGood.js b/javascript/ql/src/Security/CWE-942/examples/CorsPermissiveConfigurationGood.js
similarity index 100%
rename from javascript/ql/src/experimental/Security/CWE-942/examples/CorsPermissiveConfigurationGood.js
rename to javascript/ql/src/Security/CWE-942/examples/CorsPermissiveConfigurationGood.js
diff --git a/javascript/ql/test/experimental/Security/CWE-942/CorsPermissiveConfiguration.qlref b/javascript/ql/test/experimental/Security/CWE-942/CorsPermissiveConfiguration.qlref
deleted file mode 100644
index 1e6a39679c0d..000000000000
--- a/javascript/ql/test/experimental/Security/CWE-942/CorsPermissiveConfiguration.qlref
+++ /dev/null
@@ -1 +0,0 @@
-./experimental/Security/CWE-942/CorsPermissiveConfiguration.ql
\ No newline at end of file
diff --git a/javascript/ql/test/experimental/Security/CWE-942/CorsPermissiveConfiguration.expected b/javascript/ql/test/query-tests/Security/CWE-942/CorsPermissiveConfiguration.expected
similarity index 100%
rename from javascript/ql/test/experimental/Security/CWE-942/CorsPermissiveConfiguration.expected
rename to javascript/ql/test/query-tests/Security/CWE-942/CorsPermissiveConfiguration.expected
diff --git a/javascript/ql/test/query-tests/Security/CWE-942/CorsPermissiveConfiguration.qlref b/javascript/ql/test/query-tests/Security/CWE-942/CorsPermissiveConfiguration.qlref
new file mode 100644
index 000000000000..4f4178905a29
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-942/CorsPermissiveConfiguration.qlref
@@ -0,0 +1 @@
+Security/CWE-942/CorsPermissiveConfiguration.ql
\ No newline at end of file
diff --git a/javascript/ql/test/experimental/Security/CWE-942/apollo-test.js b/javascript/ql/test/query-tests/Security/CWE-942/apollo-test.js
similarity index 100%
rename from javascript/ql/test/experimental/Security/CWE-942/apollo-test.js
rename to javascript/ql/test/query-tests/Security/CWE-942/apollo-test.js
diff --git a/javascript/ql/test/experimental/Security/CWE-942/express-test.js b/javascript/ql/test/query-tests/Security/CWE-942/express-test.js
similarity index 100%
rename from javascript/ql/test/experimental/Security/CWE-942/express-test.js
rename to javascript/ql/test/query-tests/Security/CWE-942/express-test.js
From 92daa7d42cd2835a00c5e17deea4cc1a44401112 Mon Sep 17 00:00:00 2001
From: Napalys Klicius
Date: Wed, 30 Jul 2025 10:27:14 +0000
Subject: [PATCH 02/15] Updated suite expectations
---
.../query-suite/javascript-code-scanning.qls.expected | 1 +
.../query-suite/javascript-security-and-quality.qls.expected | 1 +
.../query-suite/javascript-security-extended.qls.expected | 1 +
.../integration-tests/query-suite/not_included_in_qls.expected | 1 -
4 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/javascript/ql/integration-tests/query-suite/javascript-code-scanning.qls.expected b/javascript/ql/integration-tests/query-suite/javascript-code-scanning.qls.expected
index 652ac0ebc1b9..0c417e661c79 100644
--- a/javascript/ql/integration-tests/query-suite/javascript-code-scanning.qls.expected
+++ b/javascript/ql/integration-tests/query-suite/javascript-code-scanning.qls.expected
@@ -83,5 +83,6 @@ ql/javascript/ql/src/Security/CWE-915/PrototypePollutingFunction.ql
ql/javascript/ql/src/Security/CWE-915/PrototypePollutingMergeCall.ql
ql/javascript/ql/src/Security/CWE-916/InsufficientPasswordHash.ql
ql/javascript/ql/src/Security/CWE-918/RequestForgery.ql
+ql/javascript/ql/src/Security/CWE-942/CorsPermissiveConfiguration.ql
ql/javascript/ql/src/Summary/LinesOfCode.ql
ql/javascript/ql/src/Summary/LinesOfUserCode.ql
diff --git a/javascript/ql/integration-tests/query-suite/javascript-security-and-quality.qls.expected b/javascript/ql/integration-tests/query-suite/javascript-security-and-quality.qls.expected
index dd5877683082..f87cd2bf505a 100644
--- a/javascript/ql/integration-tests/query-suite/javascript-security-and-quality.qls.expected
+++ b/javascript/ql/integration-tests/query-suite/javascript-security-and-quality.qls.expected
@@ -184,6 +184,7 @@ ql/javascript/ql/src/Security/CWE-915/PrototypePollutingMergeCall.ql
ql/javascript/ql/src/Security/CWE-916/InsufficientPasswordHash.ql
ql/javascript/ql/src/Security/CWE-918/ClientSideRequestForgery.ql
ql/javascript/ql/src/Security/CWE-918/RequestForgery.ql
+ql/javascript/ql/src/Security/CWE-942/CorsPermissiveConfiguration.ql
ql/javascript/ql/src/Statements/DanglingElse.ql
ql/javascript/ql/src/Statements/IgnoreArrayResult.ql
ql/javascript/ql/src/Statements/InconsistentLoopOrientation.ql
diff --git a/javascript/ql/integration-tests/query-suite/javascript-security-extended.qls.expected b/javascript/ql/integration-tests/query-suite/javascript-security-extended.qls.expected
index 9b7cfd22ed6f..ac5e0e2c4984 100644
--- a/javascript/ql/integration-tests/query-suite/javascript-security-extended.qls.expected
+++ b/javascript/ql/integration-tests/query-suite/javascript-security-extended.qls.expected
@@ -99,5 +99,6 @@ ql/javascript/ql/src/Security/CWE-915/PrototypePollutingMergeCall.ql
ql/javascript/ql/src/Security/CWE-916/InsufficientPasswordHash.ql
ql/javascript/ql/src/Security/CWE-918/ClientSideRequestForgery.ql
ql/javascript/ql/src/Security/CWE-918/RequestForgery.ql
+ql/javascript/ql/src/Security/CWE-942/CorsPermissiveConfiguration.ql
ql/javascript/ql/src/Summary/LinesOfCode.ql
ql/javascript/ql/src/Summary/LinesOfUserCode.ql
diff --git a/javascript/ql/integration-tests/query-suite/not_included_in_qls.expected b/javascript/ql/integration-tests/query-suite/not_included_in_qls.expected
index 1b119f60c75e..fa52a97a4e4a 100644
--- a/javascript/ql/integration-tests/query-suite/not_included_in_qls.expected
+++ b/javascript/ql/integration-tests/query-suite/not_included_in_qls.expected
@@ -75,7 +75,6 @@ ql/javascript/ql/src/experimental/Security/CWE-347/decodeJwtWithoutVerificationL
ql/javascript/ql/src/experimental/Security/CWE-444/InsecureHttpParser.ql
ql/javascript/ql/src/experimental/Security/CWE-522-DecompressionBombs/DecompressionBombs.ql
ql/javascript/ql/src/experimental/Security/CWE-918/SSRF.ql
-ql/javascript/ql/src/experimental/Security/CWE-942/CorsPermissiveConfiguration.ql
ql/javascript/ql/src/experimental/StandardLibrary/MultipleArgumentsToSetConstructor.ql
ql/javascript/ql/src/experimental/heuristics/ql/src/Security/CWE-020/UntrustedDataToExternalAPI.ql
ql/javascript/ql/src/experimental/heuristics/ql/src/Security/CWE-078/CommandInjection.ql
From 95743d7109180c7076fe732c2e51ff1ee88d14ab Mon Sep 17 00:00:00 2001
From: Napalys Klicius
Date: Wed, 30 Jul 2025 10:42:55 +0000
Subject: [PATCH 03/15] Added inline test expectations for cors permissive
config
---
.../Security/CWE-942/CorsPermissiveConfiguration.qlref | 3 ++-
.../ql/test/query-tests/Security/CWE-942/apollo-test.js | 8 ++++----
.../ql/test/query-tests/Security/CWE-942/express-test.js | 6 +++---
3 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/javascript/ql/test/query-tests/Security/CWE-942/CorsPermissiveConfiguration.qlref b/javascript/ql/test/query-tests/Security/CWE-942/CorsPermissiveConfiguration.qlref
index 4f4178905a29..b38b30eb842d 100644
--- a/javascript/ql/test/query-tests/Security/CWE-942/CorsPermissiveConfiguration.qlref
+++ b/javascript/ql/test/query-tests/Security/CWE-942/CorsPermissiveConfiguration.qlref
@@ -1 +1,2 @@
-Security/CWE-942/CorsPermissiveConfiguration.ql
\ No newline at end of file
+query: Security/CWE-942/CorsPermissiveConfiguration.ql
+postprocess: utils/test/InlineExpectationsTestQuery.ql
\ No newline at end of file
diff --git a/javascript/ql/test/query-tests/Security/CWE-942/apollo-test.js b/javascript/ql/test/query-tests/Security/CWE-942/apollo-test.js
index f55d5dc2c3ec..22019a722584 100644
--- a/javascript/ql/test/query-tests/Security/CWE-942/apollo-test.js
+++ b/javascript/ql/test/query-tests/Security/CWE-942/apollo-test.js
@@ -5,10 +5,10 @@ var https = require('https'),
var server = https.createServer(function () { });
server.on('request', function (req, res) {
- let user_origin = url.parse(req.url, true).query.origin;
+ let user_origin = url.parse(req.url, true).query.origin; // $ Source
// BAD: CORS too permissive
const server_1 = new ApolloServer({
- cors: { origin: true }
+ cors: { origin: true } // $ Alert
});
// GOOD: restrictive CORS
@@ -18,11 +18,11 @@ server.on('request', function (req, res) {
// BAD: CORS too permissive
const server_3 = new ApolloServer({
- cors: { origin: null }
+ cors: { origin: null } // $ Alert
});
// BAD: CORS is controlled by user
const server_4 = new ApolloServer({
- cors: { origin: user_origin }
+ cors: { origin: user_origin } // $ Alert
});
});
\ No newline at end of file
diff --git a/javascript/ql/test/query-tests/Security/CWE-942/express-test.js b/javascript/ql/test/query-tests/Security/CWE-942/express-test.js
index 3ad31a6a31a8..9b21ed56873b 100644
--- a/javascript/ql/test/query-tests/Security/CWE-942/express-test.js
+++ b/javascript/ql/test/query-tests/Security/CWE-942/express-test.js
@@ -7,7 +7,7 @@ var https = require('https'),
var server = https.createServer(function () { });
server.on('request', function (req, res) {
- let user_origin = url.parse(req.url, true).query.origin;
+ let user_origin = url.parse(req.url, true).query.origin; // $ Source
// BAD: CORS too permissive, default value is *
var app1 = express();
@@ -23,14 +23,14 @@ server.on('request', function (req, res) {
// BAD: CORS too permissive
var app3 = express();
var corsOption3 = {
- origin: '*'
+ origin: '*' // $ Alert
};
app3.use(cors(corsOption3));
// BAD: CORS is controlled by user
var app4 = express();
var corsOption4 = {
- origin: user_origin
+ origin: user_origin // $ Alert
};
app4.use(cors(corsOption4));
});
\ No newline at end of file
From 84ffbbec33cbff31393da847b815f2c56ae076c2 Mon Sep 17 00:00:00 2001
From: Napalys Klicius
Date: Wed, 30 Jul 2025 10:51:38 +0000
Subject: [PATCH 04/15] Added missing doc strings
---
.../security/CorsPermissiveConfigurationCustomizations.qll | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationCustomizations.qll
index b642b98b35ba..4751ace2a608 100644
--- a/javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationCustomizations.qll
+++ b/javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationCustomizations.qll
@@ -26,6 +26,7 @@ module CorsPermissiveConfiguration {
this = TWildcard() and result = "wildcard"
}
+ /** DEPRECATED: Converts this flow state to a flow label. */
deprecated DataFlow::FlowLabel toFlowLabel() {
this = TTaint() and result.isTaint()
or
@@ -37,6 +38,7 @@ module CorsPermissiveConfiguration {
/** Predicates for working with flow states. */
module FlowState {
+ /** DEPRECATED: Gets a flow state from a flow label. */
deprecated FlowState fromFlowLabel(DataFlow::FlowLabel label) { result.toFlowLabel() = label }
/** A tainted value. */
@@ -81,6 +83,7 @@ module CorsPermissiveConfiguration {
TrueAndNull() { this = "TrueAndNull" }
}
+ /** DEPRECATED: Gets a flow label representing `true` and `null` values. */
deprecated TrueAndNull truenullLabel() { any() }
/** A flow label representing `*` value. */
@@ -88,6 +91,7 @@ module CorsPermissiveConfiguration {
Wildcard() { this = "Wildcard" }
}
+ /** DEPRECATED: Gets a flow label representing `*` value. */
deprecated Wildcard wildcardLabel() { any() }
/** An overly permissive value for `origin` (Apollo) */
From fd4233e30edc5b828c53f1b4b8cfe76becf154b3 Mon Sep 17 00:00:00 2001
From: Napalys Klicius
Date: Thu, 31 Jul 2025 10:53:03 +0200
Subject: [PATCH 05/15] Moved apollo modeling to MaD
---
javascript/ql/lib/ext/apollo-server.model.yml | 12 +++++++
.../semmle/javascript/frameworks/Apollo.qll | 36 -------------------
...sPermissiveConfigurationCustomizations.qll | 4 +--
3 files changed, 14 insertions(+), 38 deletions(-)
delete mode 100644 javascript/ql/lib/semmle/javascript/frameworks/Apollo.qll
diff --git a/javascript/ql/lib/ext/apollo-server.model.yml b/javascript/ql/lib/ext/apollo-server.model.yml
index ffceb6a6d5af..5962b8ee7d08 100644
--- a/javascript/ql/lib/ext/apollo-server.model.yml
+++ b/javascript/ql/lib/ext/apollo-server.model.yml
@@ -5,6 +5,12 @@ extensions:
data:
- ["@apollo/server", "Member[ApolloServer,ApolloServerBase].Argument[0].AnyMember.AnyMember.AnyMember.Parameter[1]", "remote"]
+ - addsTo:
+ pack: codeql/javascript-all
+ extensible: sinkModel
+ data:
+ - ["@apollo/server", "Member[gql].Argument[0]", "sql-injection"]
+
- addsTo:
pack: codeql/javascript-all
extensible: typeModel
@@ -13,3 +19,9 @@ extensions:
- ["@apollo/server", "apollo-server-express", ""]
- ["@apollo/server", "apollo-server-core", ""]
- ["@apollo/server", "apollo-server", ""]
+ - ["@apollo/server", "@apollo/apollo-server-express", ""]
+ - ["@apollo/server", "apollo-server-express", ""]
+ - ["@apollo/server", "@apollo/server", ""]
+ - ["@apollo/server", "@apollo/apollo-server-core", ""]
+ - ["ApolloServer", "@apollo/server", "Member[ApolloServer]"]
+ - ["GraphQLApollo", "@apollo/server", "Member[gql]"]
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Apollo.qll b/javascript/ql/lib/semmle/javascript/frameworks/Apollo.qll
deleted file mode 100644
index 983c0a8ac89c..000000000000
--- a/javascript/ql/lib/semmle/javascript/frameworks/Apollo.qll
+++ /dev/null
@@ -1,36 +0,0 @@
-/**
- * Provides classes for working with Apollo GraphQL connectors.
- */
-
-import javascript
-
-/** Provides classes modeling the apollo packages [@apollo/server](https://npmjs.com/package/@apollo/server`) */
-module Apollo {
- /** Get a reference to the `ApolloServer` class. */
- private API::Node apollo() {
- result =
- API::moduleImport([
- "@apollo/server", "@apollo/apollo-server-express", "@apollo/apollo-server-core",
- "apollo-server", "apollo-server-express"
- ]).getMember("ApolloServer")
- }
-
- /** Gets a reference to the `gql` function that parses GraphQL strings. */
- private API::Node gql() {
- result =
- API::moduleImport([
- "@apollo/server", "@apollo/apollo-server-express", "@apollo/apollo-server-core",
- "apollo-server", "apollo-server-express"
- ]).getMember("gql")
- }
-
- /** An instantiation of an `ApolloServer`. */
- class ApolloServer extends API::NewNode {
- ApolloServer() { this = apollo().getAnInstantiation() }
- }
-
- /** A string that is interpreted as a GraphQL query by a `apollo` package. */
- private class ApolloGraphQLString extends GraphQL::GraphQLString {
- ApolloGraphQLString() { this = gql().getACall().getArgument(0) }
- }
-}
diff --git a/javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationCustomizations.qll
index 4751ace2a608..a504f66ba22e 100644
--- a/javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationCustomizations.qll
+++ b/javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationCustomizations.qll
@@ -5,7 +5,6 @@
*/
import javascript
-private import semmle.javascript.frameworks.Apollo
private import semmle.javascript.frameworks.Cors
/** Module containing sources, sinks, and sanitizers for overly permissive CORS configurations. */
@@ -109,7 +108,8 @@ module CorsPermissiveConfiguration {
*/
class CorsApolloServer extends Sink, DataFlow::ValueNode {
CorsApolloServer() {
- exists(Apollo::ApolloServer agql |
+ exists(API::NewNode agql |
+ agql = ModelOutput::getATypeNode("ApolloServer").getAnInstantiation() and
this =
agql.getOptionArgument(0, "cors").getALocalSource().getAPropertyWrite("origin").getRhs()
)
From 2baca58b278827703fd803889555c71b5bd05a8e Mon Sep 17 00:00:00 2001
From: Napalys Klicius
Date: Thu, 31 Jul 2025 11:08:22 +0200
Subject: [PATCH 06/15] Removed deprecations from cors as it was moved out
experimental
---
...sPermissiveConfigurationCustomizations.qll | 33 -------------------
.../CorsPermissiveConfigurationQuery.qll | 28 ----------------
2 files changed, 61 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationCustomizations.qll
index a504f66ba22e..583847ab0d98 100644
--- a/javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationCustomizations.qll
+++ b/javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationCustomizations.qll
@@ -24,22 +24,10 @@ module CorsPermissiveConfiguration {
or
this = TWildcard() and result = "wildcard"
}
-
- /** DEPRECATED: Converts this flow state to a flow label. */
- deprecated DataFlow::FlowLabel toFlowLabel() {
- this = TTaint() and result.isTaint()
- or
- this = TTrueOrNull() and result instanceof TrueAndNull
- or
- this = TWildcard() and result instanceof Wildcard
- }
}
/** Predicates for working with flow states. */
module FlowState {
- /** DEPRECATED: Gets a flow state from a flow label. */
- deprecated FlowState fromFlowLabel(DataFlow::FlowLabel label) { result.toFlowLabel() = label }
-
/** A tainted value. */
FlowState taint() { result = TTaint() }
@@ -65,11 +53,6 @@ module CorsPermissiveConfiguration {
*/
abstract class Sanitizer extends DataFlow::Node { }
- /**
- * DEPRECATED: Use `ActiveThreatModelSource` from Concepts instead!
- */
- deprecated class RemoteFlowSourceAsSource = ActiveThreatModelSourceAsSource;
-
/**
* An active threat-model source, considered as a flow source.
*/
@@ -77,22 +60,6 @@ module CorsPermissiveConfiguration {
ActiveThreatModelSourceAsSource() { not this instanceof ClientSideRemoteFlowSource }
}
- /** A flow label representing `true` and `null` values. */
- abstract deprecated class TrueAndNull extends DataFlow::FlowLabel {
- TrueAndNull() { this = "TrueAndNull" }
- }
-
- /** DEPRECATED: Gets a flow label representing `true` and `null` values. */
- deprecated TrueAndNull truenullLabel() { any() }
-
- /** A flow label representing `*` value. */
- abstract deprecated class Wildcard extends DataFlow::FlowLabel {
- Wildcard() { this = "Wildcard" }
- }
-
- /** DEPRECATED: Gets a flow label representing `*` value. */
- deprecated Wildcard wildcardLabel() { any() }
-
/** An overly permissive value for `origin` (Apollo) */
class TrueNullValue extends Source {
TrueNullValue() { this.mayHaveBooleanValue(true) or this.asExpr() instanceof NullLiteral }
diff --git a/javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationQuery.qll b/javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationQuery.qll
index 3605a1adaa93..0db678e43afd 100644
--- a/javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationQuery.qll
+++ b/javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationQuery.qll
@@ -39,31 +39,3 @@ module CorsPermissiveConfigurationConfig implements DataFlow::StateConfigSig {
module CorsPermissiveConfigurationFlow =
TaintTracking::GlobalWithState;
-
-/**
- * DEPRECATED. Use the `CorsPermissiveConfigurationFlow` module instead.
- */
-deprecated class Configuration extends TaintTracking::Configuration {
- Configuration() { this = "CorsPermissiveConfiguration" }
-
- override predicate isSource(DataFlow::Node source, DataFlow::FlowLabel label) {
- CorsPermissiveConfigurationConfig::isSource(source, FlowState::fromFlowLabel(label))
- }
-
- override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel label) {
- CorsPermissiveConfigurationConfig::isSink(sink, FlowState::fromFlowLabel(label))
- }
-
- override predicate isSanitizer(DataFlow::Node node) {
- super.isSanitizer(node) or
- CorsPermissiveConfigurationConfig::isBarrier(node)
- }
-}
-
-deprecated private class WildcardActivated extends DataFlow::FlowLabel, Wildcard {
- WildcardActivated() { this = this }
-}
-
-deprecated private class TrueAndNullActivated extends DataFlow::FlowLabel, TrueAndNull {
- TrueAndNullActivated() { this = this }
-}
From 791a7e242e5ca1e9ad68c21a49cd76730bdb0370 Mon Sep 17 00:00:00 2001
From: Napalys Klicius
Date: Thu, 31 Jul 2025 11:31:10 +0200
Subject: [PATCH 07/15] Updated qhelp for cors permissive configuration
---
.../CWE-942/CorsPermissiveConfiguration.qhelp | 92 ++++++++++---------
1 file changed, 47 insertions(+), 45 deletions(-)
diff --git a/javascript/ql/src/Security/CWE-942/CorsPermissiveConfiguration.qhelp b/javascript/ql/src/Security/CWE-942/CorsPermissiveConfiguration.qhelp
index fc79eee743bf..04796dfbc189 100644
--- a/javascript/ql/src/Security/CWE-942/CorsPermissiveConfiguration.qhelp
+++ b/javascript/ql/src/Security/CWE-942/CorsPermissiveConfiguration.qhelp
@@ -3,69 +3,71 @@
"qhelp.dtd">
-
-
+
+
- A server can use CORS
(Cross-Origin Resource Sharing) to relax the
- restrictions imposed by the SOP
(Same-Origin Policy), allowing controlled, secure
- cross-origin requests when necessary.
+ A server can use CORS (Cross-Origin Resource Sharing) to relax the
+ restrictions imposed by the Same-Origin Policy, allowing controlled, secure
+ cross-origin requests when necessary.
- A server with an overly permissive CORS
configuration may inadvertently
- expose sensitive data or lead to CSRF
which is an attack that allows attackers to trick
- users into performing unwanted operations in websites they're authenticated to.
+
+
-
+ A server with an overly permissive CORS configuration may inadvertently
+ expose sensitive data or enable CSRF attacks, which allow attackers to trick
+ users into performing unwanted operations on websites they're authenticated to.
-
+
+
-
-
+
+
- When the origin
is set to true
, it signifies that the server
- is accepting requests from any
origin, potentially exposing the system to
- CSRF attacks. This can be fixed using false
as origin value or using a whitelist.
+ When the origin
is set to true
, the server
+ accepts requests from any origin, potentially exposing the system to
+ CSRF attacks. Use false
as the origin value or implement a whitelist
+ of allowed origins instead.
-
-
+
+
- On the other hand, if the origin
is
- set to null
, it can be exploited by an attacker to deceive a user into making
- requests from a null
origin form, often hosted within a sandboxed iframe.
+ When the origin
is set to null
, it can be
+ exploited by an attacker who can deceive a user into making
+ requests from a null
origin, often hosted within a sandboxed iframe.
-
+
+
-
+ If the origin
value is user-controlled, ensure that the data
+ is properly sanitized and validated against a whitelist of allowed origins.
- If the origin
value is user controlled, make sure that the data
- is properly sanitized.
+
+
-
-
+
+
-
-
+ In the following example, server_1
accepts requests from any origin
+ because the value of origin
is set to true
.
+ server_2
uses user-controlled data for the origin without validation.
- In the example below, the server_1
accepts requests from any origin
- since the value of origin
is set to true
.
- And server_2
's origin is user-controlled.
+
-
+
-
+
-
+ To fix these issues, server_1
uses a restrictive CORS configuration
+ that is not vulnerable to CSRF attacks. server_2
properly validates
+ user-controlled data against a whitelist before using it.
- In the example below, the server_1
CORS is restrictive so it's not
- vulnerable to CSRF attacks. And server_2
's is using properly sanitized
- user-controlled data.
+
-
+
+
-
-
-
-
- Mozilla Developer Network: CORS, Access-Control-Allow-Origin.
- W3C: CORS for developers, Advice for Resource Owners
-
+
+ Mozilla Developer Network: CORS, Access-Control-Allow-Origin.
+ W3C: CORS for developers, Advice for Resource Owners.
+
From 021aa13ee2d544de36bcb26f16857c9c085401d2 Mon Sep 17 00:00:00 2001
From: Napalys Klicius
Date: Thu, 31 Jul 2025 12:45:34 +0200
Subject: [PATCH 08/15] Added change note
---
.../change-notes/2025-07-31-cors-move-out-of-experimental.md | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 javascript/ql/src/change-notes/2025-07-31-cors-move-out-of-experimental.md
diff --git a/javascript/ql/src/change-notes/2025-07-31-cors-move-out-of-experimental.md b/javascript/ql/src/change-notes/2025-07-31-cors-move-out-of-experimental.md
new file mode 100644
index 000000000000..112fb0c628ff
--- /dev/null
+++ b/javascript/ql/src/change-notes/2025-07-31-cors-move-out-of-experimental.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* The query "CORS misconfiguration" (`js/cors-misconfiguration`) has been promoted from experimental and is now part of the default security suite.
From 3a4a57f66ea456a6493c7066fb1dc44685938f57 Mon Sep 17 00:00:00 2001
From: Napalys Klicius
Date: Tue, 19 Aug 2025 09:32:24 +0200
Subject: [PATCH 09/15] Merged test cases from CorsPermissiveConfig to
CorsMisconfigurationForCredentials
---
.../{CWE-942 => CWE-346}/apollo-test.js | 10 +++---
.../{CWE-942 => CWE-346}/express-test.js | 8 ++---
.../CorsPermissiveConfiguration.expected | 34 -------------------
.../CWE-942/CorsPermissiveConfiguration.qlref | 2 --
4 files changed, 9 insertions(+), 45 deletions(-)
rename javascript/ql/test/query-tests/Security/{CWE-942 => CWE-346}/apollo-test.js (77%)
rename javascript/ql/test/query-tests/Security/{CWE-942 => CWE-346}/express-test.js (88%)
delete mode 100644 javascript/ql/test/query-tests/Security/CWE-942/CorsPermissiveConfiguration.expected
delete mode 100644 javascript/ql/test/query-tests/Security/CWE-942/CorsPermissiveConfiguration.qlref
diff --git a/javascript/ql/test/query-tests/Security/CWE-942/apollo-test.js b/javascript/ql/test/query-tests/Security/CWE-346/apollo-test.js
similarity index 77%
rename from javascript/ql/test/query-tests/Security/CWE-942/apollo-test.js
rename to javascript/ql/test/query-tests/Security/CWE-346/apollo-test.js
index 22019a722584..5331158085ab 100644
--- a/javascript/ql/test/query-tests/Security/CWE-942/apollo-test.js
+++ b/javascript/ql/test/query-tests/Security/CWE-346/apollo-test.js
@@ -5,10 +5,10 @@ var https = require('https'),
var server = https.createServer(function () { });
server.on('request', function (req, res) {
- let user_origin = url.parse(req.url, true).query.origin; // $ Source
+ let user_origin = url.parse(req.url, true).query.origin; // $ MISSING: Source
// BAD: CORS too permissive
const server_1 = new ApolloServer({
- cors: { origin: true } // $ Alert
+ cors: { origin: true } // $ MISSING: Alert
});
// GOOD: restrictive CORS
@@ -18,11 +18,11 @@ server.on('request', function (req, res) {
// BAD: CORS too permissive
const server_3 = new ApolloServer({
- cors: { origin: null } // $ Alert
+ cors: { origin: null } // $ MISSING: Alert
});
// BAD: CORS is controlled by user
const server_4 = new ApolloServer({
- cors: { origin: user_origin } // $ Alert
+ cors: { origin: user_origin } // $ MISSING: Alert
});
-});
\ No newline at end of file
+});
diff --git a/javascript/ql/test/query-tests/Security/CWE-942/express-test.js b/javascript/ql/test/query-tests/Security/CWE-346/express-test.js
similarity index 88%
rename from javascript/ql/test/query-tests/Security/CWE-942/express-test.js
rename to javascript/ql/test/query-tests/Security/CWE-346/express-test.js
index 9b21ed56873b..420768bc4b85 100644
--- a/javascript/ql/test/query-tests/Security/CWE-942/express-test.js
+++ b/javascript/ql/test/query-tests/Security/CWE-346/express-test.js
@@ -7,7 +7,7 @@ var https = require('https'),
var server = https.createServer(function () { });
server.on('request', function (req, res) {
- let user_origin = url.parse(req.url, true).query.origin; // $ Source
+ let user_origin = url.parse(req.url, true).query.origin; // $ MISSING: Source
// BAD: CORS too permissive, default value is *
var app1 = express();
@@ -23,14 +23,14 @@ server.on('request', function (req, res) {
// BAD: CORS too permissive
var app3 = express();
var corsOption3 = {
- origin: '*' // $ Alert
+ origin: '*' // $ MISSING: Alert
};
app3.use(cors(corsOption3));
// BAD: CORS is controlled by user
var app4 = express();
var corsOption4 = {
- origin: user_origin // $ Alert
+ origin: user_origin // $ MISSING: Alert
};
app4.use(cors(corsOption4));
-});
\ No newline at end of file
+});
diff --git a/javascript/ql/test/query-tests/Security/CWE-942/CorsPermissiveConfiguration.expected b/javascript/ql/test/query-tests/Security/CWE-942/CorsPermissiveConfiguration.expected
deleted file mode 100644
index 6c28b7105a18..000000000000
--- a/javascript/ql/test/query-tests/Security/CWE-942/CorsPermissiveConfiguration.expected
+++ /dev/null
@@ -1,34 +0,0 @@
-edges
-| apollo-test.js:8:9:8:59 | user_origin | apollo-test.js:26:25:26:35 | user_origin | provenance | |
-| apollo-test.js:8:9:8:59 | user_origin | apollo-test.js:26:25:26:35 | user_origin | provenance | |
-| apollo-test.js:8:23:8:46 | url.par ... , true) | apollo-test.js:8:9:8:59 | user_origin | provenance | |
-| apollo-test.js:8:23:8:46 | url.par ... , true) | apollo-test.js:8:9:8:59 | user_origin | provenance | |
-| apollo-test.js:8:33:8:39 | req.url | apollo-test.js:8:23:8:46 | url.par ... , true) | provenance | |
-| apollo-test.js:8:42:8:45 | true | apollo-test.js:8:23:8:46 | url.par ... , true) | provenance | |
-| express-test.js:10:9:10:59 | user_origin | express-test.js:33:17:33:27 | user_origin | provenance | |
-| express-test.js:10:23:10:46 | url.par ... , true) | express-test.js:10:9:10:59 | user_origin | provenance | |
-| express-test.js:10:33:10:39 | req.url | express-test.js:10:23:10:46 | url.par ... , true) | provenance | |
-nodes
-| apollo-test.js:8:9:8:59 | user_origin | semmle.label | user_origin |
-| apollo-test.js:8:9:8:59 | user_origin | semmle.label | user_origin |
-| apollo-test.js:8:23:8:46 | url.par ... , true) | semmle.label | url.par ... , true) |
-| apollo-test.js:8:23:8:46 | url.par ... , true) | semmle.label | url.par ... , true) |
-| apollo-test.js:8:33:8:39 | req.url | semmle.label | req.url |
-| apollo-test.js:8:42:8:45 | true | semmle.label | true |
-| apollo-test.js:11:25:11:28 | true | semmle.label | true |
-| apollo-test.js:21:25:21:28 | null | semmle.label | null |
-| apollo-test.js:26:25:26:35 | user_origin | semmle.label | user_origin |
-| apollo-test.js:26:25:26:35 | user_origin | semmle.label | user_origin |
-| express-test.js:10:9:10:59 | user_origin | semmle.label | user_origin |
-| express-test.js:10:23:10:46 | url.par ... , true) | semmle.label | url.par ... , true) |
-| express-test.js:10:33:10:39 | req.url | semmle.label | req.url |
-| express-test.js:26:17:26:19 | '*' | semmle.label | '*' |
-| express-test.js:33:17:33:27 | user_origin | semmle.label | user_origin |
-subpaths
-#select
-| apollo-test.js:11:25:11:28 | true | apollo-test.js:11:25:11:28 | true | apollo-test.js:11:25:11:28 | true | CORS Origin misconfiguration due to a $@. | apollo-test.js:11:25:11:28 | true | too permissive or user controlled value |
-| apollo-test.js:21:25:21:28 | null | apollo-test.js:21:25:21:28 | null | apollo-test.js:21:25:21:28 | null | CORS Origin misconfiguration due to a $@. | apollo-test.js:21:25:21:28 | null | too permissive or user controlled value |
-| apollo-test.js:26:25:26:35 | user_origin | apollo-test.js:8:33:8:39 | req.url | apollo-test.js:26:25:26:35 | user_origin | CORS Origin misconfiguration due to a $@. | apollo-test.js:8:33:8:39 | req.url | too permissive or user controlled value |
-| apollo-test.js:26:25:26:35 | user_origin | apollo-test.js:8:42:8:45 | true | apollo-test.js:26:25:26:35 | user_origin | CORS Origin misconfiguration due to a $@. | apollo-test.js:8:42:8:45 | true | too permissive or user controlled value |
-| express-test.js:26:17:26:19 | '*' | express-test.js:26:17:26:19 | '*' | express-test.js:26:17:26:19 | '*' | CORS Origin misconfiguration due to a $@. | express-test.js:26:17:26:19 | '*' | too permissive or user controlled value |
-| express-test.js:33:17:33:27 | user_origin | express-test.js:10:33:10:39 | req.url | express-test.js:33:17:33:27 | user_origin | CORS Origin misconfiguration due to a $@. | express-test.js:10:33:10:39 | req.url | too permissive or user controlled value |
diff --git a/javascript/ql/test/query-tests/Security/CWE-942/CorsPermissiveConfiguration.qlref b/javascript/ql/test/query-tests/Security/CWE-942/CorsPermissiveConfiguration.qlref
deleted file mode 100644
index b38b30eb842d..000000000000
--- a/javascript/ql/test/query-tests/Security/CWE-942/CorsPermissiveConfiguration.qlref
+++ /dev/null
@@ -1,2 +0,0 @@
-query: Security/CWE-942/CorsPermissiveConfiguration.ql
-postprocess: utils/test/InlineExpectationsTestQuery.ql
\ No newline at end of file
From d758433b1005d94bea49eabb1a3e0016d60288c3 Mon Sep 17 00:00:00 2001
From: Napalys Klicius
Date: Tue, 19 Aug 2025 09:55:26 +0200
Subject: [PATCH 10/15] Merged CorsPermissiveConfig to
CorsMisconfigurationForCredentials
---
...figurationForCredentialsCustomizations.qll | 61 ++++++++++++++++++-
.../CorsMisconfigurationForCredentials.ql | 7 +--
...orsMisconfigurationForCredentials.expected | 37 ++++++++++-
.../Security/CWE-346/apollo-test.js | 8 +--
.../Security/CWE-346/express-test.js | 6 +-
5 files changed, 102 insertions(+), 17 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/CorsMisconfigurationForCredentialsCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/CorsMisconfigurationForCredentialsCustomizations.qll
index eaf78fd4c4c9..36f8badcad70 100644
--- a/javascript/ql/lib/semmle/javascript/security/dataflow/CorsMisconfigurationForCredentialsCustomizations.qll
+++ b/javascript/ql/lib/semmle/javascript/security/dataflow/CorsMisconfigurationForCredentialsCustomizations.qll
@@ -1,14 +1,15 @@
/**
* Provides default sources, sinks and sanitizers for reasoning about
- * CORS misconfiguration for credentials transfer, as well as
- * extension points for adding your own.
+ * CORS misconfiguration for credentials transfer and overly permissive CORS configurations,
+ * as well as extension points for adding your own.
*/
import javascript
+private import semmle.javascript.frameworks.Cors
module CorsMisconfigurationForCredentials {
/**
- * A data flow source for CORS misconfiguration for credentials transfer.
+ * A data flow source for CORS misconfiguration for credentials transfer and overly permissive CORS configurations.
*/
abstract class Source extends DataFlow::Node { }
@@ -75,4 +76,58 @@ module CorsMisconfigurationForCredentials {
this.asExpr().mayHaveStringValue("null")
}
}
+
+ /** An overly permissive value for `origin` (Apollo) */
+ class TrueNullValue extends Source {
+ TrueNullValue() { this.mayHaveBooleanValue(true) or this.asExpr() instanceof NullLiteral }
+ }
+
+ /** An overly permissive value for `origin` (Express) */
+ class WildcardValue extends Source {
+ WildcardValue() { this.mayHaveStringValue("*") }
+ }
+
+ /**
+ * The value of cors origin when initializing the application.
+ */
+ class CorsApolloServer extends Sink, DataFlow::ValueNode {
+ CorsApolloServer() {
+ exists(API::NewNode agql |
+ agql = ModelOutput::getATypeNode("ApolloServer").getAnInstantiation() and
+ this =
+ agql.getOptionArgument(0, "cors").getALocalSource().getAPropertyWrite("origin").getRhs()
+ )
+ }
+
+ override Http::HeaderDefinition getCredentialsHeader() { none() }
+ }
+
+ /**
+ * The value of cors origin when initializing the application.
+ */
+ class ExpressCors extends Sink, DataFlow::ValueNode {
+ ExpressCors() {
+ exists(CorsConfiguration config | this = config.getCorsConfiguration().getOrigin())
+ }
+
+ override Http::HeaderDefinition getCredentialsHeader() { none() }
+ }
+
+ /**
+ * An express route setup configured with the `cors` package.
+ */
+ class CorsConfiguration extends DataFlow::MethodCallNode {
+ Cors::Cors corsConfig;
+
+ CorsConfiguration() {
+ exists(Express::RouteSetup setup | this = setup |
+ if setup.isUseCall()
+ then corsConfig = setup.getArgument(0)
+ else corsConfig = setup.getArgument(any(int i | i > 0))
+ )
+ }
+
+ /** Gets the expression that configures `cors` on this route setup. */
+ Cors::Cors getCorsConfiguration() { result = corsConfig }
+ }
}
diff --git a/javascript/ql/src/Security/CWE-346/CorsMisconfigurationForCredentials.ql b/javascript/ql/src/Security/CWE-346/CorsMisconfigurationForCredentials.ql
index ac8acac4742d..dcc4b2d5e087 100644
--- a/javascript/ql/src/Security/CWE-346/CorsMisconfigurationForCredentials.ql
+++ b/javascript/ql/src/Security/CWE-346/CorsMisconfigurationForCredentials.ql
@@ -1,6 +1,6 @@
/**
* @name CORS misconfiguration for credentials transfer
- * @description Misconfiguration of CORS HTTP headers allows for leaks of secret credentials.
+ * @description Misconfiguration of CORS HTTP headers allows for leaks of secret credentials and CSRF attacks.
* @kind path-problem
* @problem.severity error
* @security-severity 7.5
@@ -18,6 +18,5 @@ import CorsMisconfigurationFlow::PathGraph
from CorsMisconfigurationFlow::PathNode source, CorsMisconfigurationFlow::PathNode sink
where CorsMisconfigurationFlow::flowPath(source, sink)
-select sink.getNode(), source, sink, "$@ leak vulnerability due to a $@.",
- sink.getNode().(Sink).getCredentialsHeader(), "Credential", source.getNode(),
- "misconfigured CORS header value"
+select sink.getNode(), source, sink, "CORS misconfiguration due to a $@.", source.getNode(),
+ "permissive or user controlled value"
diff --git a/javascript/ql/test/query-tests/Security/CWE-346/CorsMisconfigurationForCredentials.expected b/javascript/ql/test/query-tests/Security/CWE-346/CorsMisconfigurationForCredentials.expected
index 34b731a12d18..bbc1072cb85b 100644
--- a/javascript/ql/test/query-tests/Security/CWE-346/CorsMisconfigurationForCredentials.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-346/CorsMisconfigurationForCredentials.expected
@@ -1,15 +1,46 @@
#select
-| tst.js:13:50:13:55 | origin | tst.js:12:28:12:34 | req.url | tst.js:13:50:13:55 | origin | $@ leak vulnerability due to a $@. | tst.js:14:5:14:59 | res.set ... , true) | Credential | tst.js:12:28:12:34 | req.url | misconfigured CORS header value |
-| tst.js:18:50:18:53 | null | tst.js:18:50:18:53 | null | tst.js:18:50:18:53 | null | $@ leak vulnerability due to a $@. | tst.js:19:5:19:59 | res.set ... , true) | Credential | tst.js:18:50:18:53 | null | misconfigured CORS header value |
-| tst.js:23:50:23:55 | "null" | tst.js:23:50:23:55 | "null" | tst.js:23:50:23:55 | "null" | $@ leak vulnerability due to a $@. | tst.js:24:5:24:59 | res.set ... , true) | Credential | tst.js:23:50:23:55 | "null" | misconfigured CORS header value |
+| apollo-test.js:11:25:11:28 | true | apollo-test.js:11:25:11:28 | true | apollo-test.js:11:25:11:28 | true | CORS misconfiguration due to a $@. | apollo-test.js:11:25:11:28 | true | permissive or user controlled value |
+| apollo-test.js:21:25:21:28 | null | apollo-test.js:21:25:21:28 | null | apollo-test.js:21:25:21:28 | null | CORS misconfiguration due to a $@. | apollo-test.js:21:25:21:28 | null | permissive or user controlled value |
+| apollo-test.js:26:25:26:35 | user_origin | apollo-test.js:8:33:8:39 | req.url | apollo-test.js:26:25:26:35 | user_origin | CORS misconfiguration due to a $@. | apollo-test.js:8:33:8:39 | req.url | permissive or user controlled value |
+| apollo-test.js:26:25:26:35 | user_origin | apollo-test.js:8:42:8:45 | true | apollo-test.js:26:25:26:35 | user_origin | CORS misconfiguration due to a $@. | apollo-test.js:8:42:8:45 | true | permissive or user controlled value |
+| express-test.js:26:17:26:19 | '*' | express-test.js:26:17:26:19 | '*' | express-test.js:26:17:26:19 | '*' | CORS misconfiguration due to a $@. | express-test.js:26:17:26:19 | '*' | permissive or user controlled value |
+| express-test.js:33:17:33:27 | user_origin | express-test.js:10:33:10:39 | req.url | express-test.js:33:17:33:27 | user_origin | CORS misconfiguration due to a $@. | express-test.js:10:33:10:39 | req.url | permissive or user controlled value |
+| express-test.js:33:17:33:27 | user_origin | express-test.js:10:42:10:45 | true | express-test.js:33:17:33:27 | user_origin | CORS misconfiguration due to a $@. | express-test.js:10:42:10:45 | true | permissive or user controlled value |
+| tst.js:13:50:13:55 | origin | tst.js:12:28:12:34 | req.url | tst.js:13:50:13:55 | origin | CORS misconfiguration due to a $@. | tst.js:12:28:12:34 | req.url | permissive or user controlled value |
+| tst.js:13:50:13:55 | origin | tst.js:12:37:12:40 | true | tst.js:13:50:13:55 | origin | CORS misconfiguration due to a $@. | tst.js:12:37:12:40 | true | permissive or user controlled value |
+| tst.js:18:50:18:53 | null | tst.js:18:50:18:53 | null | tst.js:18:50:18:53 | null | CORS misconfiguration due to a $@. | tst.js:18:50:18:53 | null | permissive or user controlled value |
+| tst.js:23:50:23:55 | "null" | tst.js:23:50:23:55 | "null" | tst.js:23:50:23:55 | "null" | CORS misconfiguration due to a $@. | tst.js:23:50:23:55 | "null" | permissive or user controlled value |
edges
+| apollo-test.js:8:9:8:59 | user_origin | apollo-test.js:26:25:26:35 | user_origin | provenance | |
+| apollo-test.js:8:23:8:46 | url.par ... , true) | apollo-test.js:8:9:8:59 | user_origin | provenance | |
+| apollo-test.js:8:33:8:39 | req.url | apollo-test.js:8:23:8:46 | url.par ... , true) | provenance | |
+| apollo-test.js:8:42:8:45 | true | apollo-test.js:8:23:8:46 | url.par ... , true) | provenance | |
+| express-test.js:10:9:10:59 | user_origin | express-test.js:33:17:33:27 | user_origin | provenance | |
+| express-test.js:10:23:10:46 | url.par ... , true) | express-test.js:10:9:10:59 | user_origin | provenance | |
+| express-test.js:10:33:10:39 | req.url | express-test.js:10:23:10:46 | url.par ... , true) | provenance | |
+| express-test.js:10:42:10:45 | true | express-test.js:10:23:10:46 | url.par ... , true) | provenance | |
| tst.js:12:9:12:54 | origin | tst.js:13:50:13:55 | origin | provenance | |
| tst.js:12:18:12:41 | url.par ... , true) | tst.js:12:9:12:54 | origin | provenance | |
| tst.js:12:28:12:34 | req.url | tst.js:12:18:12:41 | url.par ... , true) | provenance | |
+| tst.js:12:37:12:40 | true | tst.js:12:18:12:41 | url.par ... , true) | provenance | |
nodes
+| apollo-test.js:8:9:8:59 | user_origin | semmle.label | user_origin |
+| apollo-test.js:8:23:8:46 | url.par ... , true) | semmle.label | url.par ... , true) |
+| apollo-test.js:8:33:8:39 | req.url | semmle.label | req.url |
+| apollo-test.js:8:42:8:45 | true | semmle.label | true |
+| apollo-test.js:11:25:11:28 | true | semmle.label | true |
+| apollo-test.js:21:25:21:28 | null | semmle.label | null |
+| apollo-test.js:26:25:26:35 | user_origin | semmle.label | user_origin |
+| express-test.js:10:9:10:59 | user_origin | semmle.label | user_origin |
+| express-test.js:10:23:10:46 | url.par ... , true) | semmle.label | url.par ... , true) |
+| express-test.js:10:33:10:39 | req.url | semmle.label | req.url |
+| express-test.js:10:42:10:45 | true | semmle.label | true |
+| express-test.js:26:17:26:19 | '*' | semmle.label | '*' |
+| express-test.js:33:17:33:27 | user_origin | semmle.label | user_origin |
| tst.js:12:9:12:54 | origin | semmle.label | origin |
| tst.js:12:18:12:41 | url.par ... , true) | semmle.label | url.par ... , true) |
| tst.js:12:28:12:34 | req.url | semmle.label | req.url |
+| tst.js:12:37:12:40 | true | semmle.label | true |
| tst.js:13:50:13:55 | origin | semmle.label | origin |
| tst.js:18:50:18:53 | null | semmle.label | null |
| tst.js:23:50:23:55 | "null" | semmle.label | "null" |
diff --git a/javascript/ql/test/query-tests/Security/CWE-346/apollo-test.js b/javascript/ql/test/query-tests/Security/CWE-346/apollo-test.js
index 5331158085ab..1306f9038e3a 100644
--- a/javascript/ql/test/query-tests/Security/CWE-346/apollo-test.js
+++ b/javascript/ql/test/query-tests/Security/CWE-346/apollo-test.js
@@ -5,10 +5,10 @@ var https = require('https'),
var server = https.createServer(function () { });
server.on('request', function (req, res) {
- let user_origin = url.parse(req.url, true).query.origin; // $ MISSING: Source
+ let user_origin = url.parse(req.url, true).query.origin; // $ Source
// BAD: CORS too permissive
const server_1 = new ApolloServer({
- cors: { origin: true } // $ MISSING: Alert
+ cors: { origin: true } // $ Alert
});
// GOOD: restrictive CORS
@@ -18,11 +18,11 @@ server.on('request', function (req, res) {
// BAD: CORS too permissive
const server_3 = new ApolloServer({
- cors: { origin: null } // $ MISSING: Alert
+ cors: { origin: null } // $ Alert
});
// BAD: CORS is controlled by user
const server_4 = new ApolloServer({
- cors: { origin: user_origin } // $ MISSING: Alert
+ cors: { origin: user_origin } // $ Alert
});
});
diff --git a/javascript/ql/test/query-tests/Security/CWE-346/express-test.js b/javascript/ql/test/query-tests/Security/CWE-346/express-test.js
index 420768bc4b85..b95e78f6b861 100644
--- a/javascript/ql/test/query-tests/Security/CWE-346/express-test.js
+++ b/javascript/ql/test/query-tests/Security/CWE-346/express-test.js
@@ -7,7 +7,7 @@ var https = require('https'),
var server = https.createServer(function () { });
server.on('request', function (req, res) {
- let user_origin = url.parse(req.url, true).query.origin; // $ MISSING: Source
+ let user_origin = url.parse(req.url, true).query.origin; // $ Source
// BAD: CORS too permissive, default value is *
var app1 = express();
@@ -23,14 +23,14 @@ server.on('request', function (req, res) {
// BAD: CORS too permissive
var app3 = express();
var corsOption3 = {
- origin: '*' // $ MISSING: Alert
+ origin: '*' // $ Alert
};
app3.use(cors(corsOption3));
// BAD: CORS is controlled by user
var app4 = express();
var corsOption4 = {
- origin: user_origin // $ MISSING: Alert
+ origin: user_origin // $ Alert
};
app4.use(cors(corsOption4));
});
From c0f78b21e73d707c8f61fe01fdaf8ea252c1d661 Mon Sep 17 00:00:00 2001
From: Napalys Klicius
Date: Tue, 19 Aug 2025 09:56:02 +0200
Subject: [PATCH 11/15] Removed now redundant js/cors-misconfiguration
---
.../javascript-code-scanning.qls.expected | 1 -
...vascript-security-and-quality.qls.expected | 1 -
.../javascript-security-extended.qls.expected | 1 -
...sPermissiveConfigurationCustomizations.qll | 112 ------------------
.../CorsPermissiveConfigurationQuery.qll | 41 -------
.../CWE-942/CorsPermissiveConfiguration.qhelp | 73 ------------
.../CWE-942/CorsPermissiveConfiguration.ql | 22 ----
.../CorsPermissiveConfigurationBad.js | 18 ---
.../CorsPermissiveConfigurationGood.js | 18 ---
9 files changed, 287 deletions(-)
delete mode 100644 javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationCustomizations.qll
delete mode 100644 javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationQuery.qll
delete mode 100644 javascript/ql/src/Security/CWE-942/CorsPermissiveConfiguration.qhelp
delete mode 100644 javascript/ql/src/Security/CWE-942/CorsPermissiveConfiguration.ql
delete mode 100644 javascript/ql/src/Security/CWE-942/examples/CorsPermissiveConfigurationBad.js
delete mode 100644 javascript/ql/src/Security/CWE-942/examples/CorsPermissiveConfigurationGood.js
diff --git a/javascript/ql/integration-tests/query-suite/javascript-code-scanning.qls.expected b/javascript/ql/integration-tests/query-suite/javascript-code-scanning.qls.expected
index 0c417e661c79..652ac0ebc1b9 100644
--- a/javascript/ql/integration-tests/query-suite/javascript-code-scanning.qls.expected
+++ b/javascript/ql/integration-tests/query-suite/javascript-code-scanning.qls.expected
@@ -83,6 +83,5 @@ ql/javascript/ql/src/Security/CWE-915/PrototypePollutingFunction.ql
ql/javascript/ql/src/Security/CWE-915/PrototypePollutingMergeCall.ql
ql/javascript/ql/src/Security/CWE-916/InsufficientPasswordHash.ql
ql/javascript/ql/src/Security/CWE-918/RequestForgery.ql
-ql/javascript/ql/src/Security/CWE-942/CorsPermissiveConfiguration.ql
ql/javascript/ql/src/Summary/LinesOfCode.ql
ql/javascript/ql/src/Summary/LinesOfUserCode.ql
diff --git a/javascript/ql/integration-tests/query-suite/javascript-security-and-quality.qls.expected b/javascript/ql/integration-tests/query-suite/javascript-security-and-quality.qls.expected
index f87cd2bf505a..dd5877683082 100644
--- a/javascript/ql/integration-tests/query-suite/javascript-security-and-quality.qls.expected
+++ b/javascript/ql/integration-tests/query-suite/javascript-security-and-quality.qls.expected
@@ -184,7 +184,6 @@ ql/javascript/ql/src/Security/CWE-915/PrototypePollutingMergeCall.ql
ql/javascript/ql/src/Security/CWE-916/InsufficientPasswordHash.ql
ql/javascript/ql/src/Security/CWE-918/ClientSideRequestForgery.ql
ql/javascript/ql/src/Security/CWE-918/RequestForgery.ql
-ql/javascript/ql/src/Security/CWE-942/CorsPermissiveConfiguration.ql
ql/javascript/ql/src/Statements/DanglingElse.ql
ql/javascript/ql/src/Statements/IgnoreArrayResult.ql
ql/javascript/ql/src/Statements/InconsistentLoopOrientation.ql
diff --git a/javascript/ql/integration-tests/query-suite/javascript-security-extended.qls.expected b/javascript/ql/integration-tests/query-suite/javascript-security-extended.qls.expected
index ac5e0e2c4984..9b7cfd22ed6f 100644
--- a/javascript/ql/integration-tests/query-suite/javascript-security-extended.qls.expected
+++ b/javascript/ql/integration-tests/query-suite/javascript-security-extended.qls.expected
@@ -99,6 +99,5 @@ ql/javascript/ql/src/Security/CWE-915/PrototypePollutingMergeCall.ql
ql/javascript/ql/src/Security/CWE-916/InsufficientPasswordHash.ql
ql/javascript/ql/src/Security/CWE-918/ClientSideRequestForgery.ql
ql/javascript/ql/src/Security/CWE-918/RequestForgery.ql
-ql/javascript/ql/src/Security/CWE-942/CorsPermissiveConfiguration.ql
ql/javascript/ql/src/Summary/LinesOfCode.ql
ql/javascript/ql/src/Summary/LinesOfUserCode.ql
diff --git a/javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationCustomizations.qll
deleted file mode 100644
index 583847ab0d98..000000000000
--- a/javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationCustomizations.qll
+++ /dev/null
@@ -1,112 +0,0 @@
-/**
- * Provides default sources, sinks and sanitizers for reasoning about
- * overly permissive CORS configurations, as well as
- * extension points for adding your own.
- */
-
-import javascript
-private import semmle.javascript.frameworks.Cors
-
-/** Module containing sources, sinks, and sanitizers for overly permissive CORS configurations. */
-module CorsPermissiveConfiguration {
- private newtype TFlowState =
- TTaint() or
- TTrueOrNull() or
- TWildcard()
-
- /** A flow state to asociate with a tracked value. */
- class FlowState extends TFlowState {
- /** Gets a string representation of this flow state. */
- string toString() {
- this = TTaint() and result = "taint"
- or
- this = TTrueOrNull() and result = "true-or-null"
- or
- this = TWildcard() and result = "wildcard"
- }
- }
-
- /** Predicates for working with flow states. */
- module FlowState {
- /** A tainted value. */
- FlowState taint() { result = TTaint() }
-
- /** A `true` or `null` value. */
- FlowState trueOrNull() { result = TTrueOrNull() }
-
- /** A `"*"` value. */
- FlowState wildcard() { result = TWildcard() }
- }
-
- /**
- * A data flow source for permissive CORS configuration.
- */
- abstract class Source extends DataFlow::Node { }
-
- /**
- * A data flow sink for permissive CORS configuration.
- */
- abstract class Sink extends DataFlow::Node { }
-
- /**
- * A sanitizer for permissive CORS configuration.
- */
- abstract class Sanitizer extends DataFlow::Node { }
-
- /**
- * An active threat-model source, considered as a flow source.
- */
- private class ActiveThreatModelSourceAsSource extends Source instanceof ActiveThreatModelSource {
- ActiveThreatModelSourceAsSource() { not this instanceof ClientSideRemoteFlowSource }
- }
-
- /** An overly permissive value for `origin` (Apollo) */
- class TrueNullValue extends Source {
- TrueNullValue() { this.mayHaveBooleanValue(true) or this.asExpr() instanceof NullLiteral }
- }
-
- /** An overly permissive value for `origin` (Express) */
- class WildcardValue extends Source {
- WildcardValue() { this.mayHaveStringValue("*") }
- }
-
- /**
- * The value of cors origin when initializing the application.
- */
- class CorsApolloServer extends Sink, DataFlow::ValueNode {
- CorsApolloServer() {
- exists(API::NewNode agql |
- agql = ModelOutput::getATypeNode("ApolloServer").getAnInstantiation() and
- this =
- agql.getOptionArgument(0, "cors").getALocalSource().getAPropertyWrite("origin").getRhs()
- )
- }
- }
-
- /**
- * The value of cors origin when initializing the application.
- */
- class ExpressCors extends Sink, DataFlow::ValueNode {
- ExpressCors() {
- exists(CorsConfiguration config | this = config.getCorsConfiguration().getOrigin())
- }
- }
-
- /**
- * An express route setup configured with the `cors` package.
- */
- class CorsConfiguration extends DataFlow::MethodCallNode {
- Cors::Cors corsConfig;
-
- CorsConfiguration() {
- exists(Express::RouteSetup setup | this = setup |
- if setup.isUseCall()
- then corsConfig = setup.getArgument(0)
- else corsConfig = setup.getArgument(any(int i | i > 0))
- )
- }
-
- /** Gets the expression that configures `cors` on this route setup. */
- Cors::Cors getCorsConfiguration() { result = corsConfig }
- }
-}
diff --git a/javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationQuery.qll b/javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationQuery.qll
deleted file mode 100644
index 0db678e43afd..000000000000
--- a/javascript/ql/lib/semmle/javascript/security/CorsPermissiveConfigurationQuery.qll
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
- * Provides a dataflow taint tracking configuration for reasoning
- * about overly permissive CORS configurations.
- *
- * Note, for performance reasons: only import this file if
- * `CorsPermissiveConfiguration::Configuration` is needed,
- * otherwise `CorsPermissiveConfigurationCustomizations` should
- * be imported instead.
- */
-
-import javascript
-import CorsPermissiveConfigurationCustomizations::CorsPermissiveConfiguration
-private import CorsPermissiveConfigurationCustomizations::CorsPermissiveConfiguration as CorsPermissiveConfiguration
-
-/**
- * A data flow configuration for overly permissive CORS configuration.
- */
-module CorsPermissiveConfigurationConfig implements DataFlow::StateConfigSig {
- class FlowState = CorsPermissiveConfiguration::FlowState;
-
- predicate isSource(DataFlow::Node source, FlowState state) {
- source instanceof TrueNullValue and state = FlowState::trueOrNull()
- or
- source instanceof WildcardValue and state = FlowState::wildcard()
- or
- source instanceof RemoteFlowSource and state = FlowState::taint()
- }
-
- predicate isSink(DataFlow::Node sink, FlowState state) {
- sink instanceof CorsApolloServer and state = [FlowState::taint(), FlowState::trueOrNull()]
- or
- sink instanceof ExpressCors and state = [FlowState::taint(), FlowState::wildcard()]
- }
-
- predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
-
- predicate observeDiffInformedIncrementalMode() { any() }
-}
-
-module CorsPermissiveConfigurationFlow =
- TaintTracking::GlobalWithState;
diff --git a/javascript/ql/src/Security/CWE-942/CorsPermissiveConfiguration.qhelp b/javascript/ql/src/Security/CWE-942/CorsPermissiveConfiguration.qhelp
deleted file mode 100644
index 04796dfbc189..000000000000
--- a/javascript/ql/src/Security/CWE-942/CorsPermissiveConfiguration.qhelp
+++ /dev/null
@@ -1,73 +0,0 @@
-
-
-
-
-
-
- A server can use CORS (Cross-Origin Resource Sharing) to relax the
- restrictions imposed by the Same-Origin Policy, allowing controlled, secure
- cross-origin requests when necessary.
-
-
-
-
- A server with an overly permissive CORS configuration may inadvertently
- expose sensitive data or enable CSRF attacks, which allow attackers to trick
- users into performing unwanted operations on websites they're authenticated to.
-
-
-
-
-
-
-
- When the origin
is set to true
, the server
- accepts requests from any origin, potentially exposing the system to
- CSRF attacks. Use false
as the origin value or implement a whitelist
- of allowed origins instead.
-
-
-
-
- When the origin
is set to null
, it can be
- exploited by an attacker who can deceive a user into making
- requests from a null
origin, often hosted within a sandboxed iframe.
-
-
-
-
- If the origin
value is user-controlled, ensure that the data
- is properly sanitized and validated against a whitelist of allowed origins.
-
-
-
-
-
-
-
- In the following example, server_1
accepts requests from any origin
- because the value of origin
is set to true
.
- server_2
uses user-controlled data for the origin without validation.
-
-
-
-
-
-
-
- To fix these issues, server_1
uses a restrictive CORS configuration
- that is not vulnerable to CSRF attacks. server_2
properly validates
- user-controlled data against a whitelist before using it.
-
-
-
-
-
-
-
- Mozilla Developer Network: CORS, Access-Control-Allow-Origin.
- W3C: CORS for developers, Advice for Resource Owners.
-
-
diff --git a/javascript/ql/src/Security/CWE-942/CorsPermissiveConfiguration.ql b/javascript/ql/src/Security/CWE-942/CorsPermissiveConfiguration.ql
deleted file mode 100644
index 050842028585..000000000000
--- a/javascript/ql/src/Security/CWE-942/CorsPermissiveConfiguration.ql
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * @name Permissive CORS configuration
- * @description Misconfiguration of CORS HTTP headers allows CSRF attacks.
- * @kind path-problem
- * @problem.severity error
- * @security-severity 7.5
- * @precision high
- * @id js/cors-misconfiguration
- * @tags security
- * external/cwe/cwe-942
- */
-
-import javascript
-import semmle.javascript.security.CorsPermissiveConfigurationQuery as CorsQuery
-import CorsQuery::CorsPermissiveConfigurationFlow::PathGraph
-
-from
- CorsQuery::CorsPermissiveConfigurationFlow::PathNode source,
- CorsQuery::CorsPermissiveConfigurationFlow::PathNode sink
-where CorsQuery::CorsPermissiveConfigurationFlow::flowPath(source, sink)
-select sink.getNode(), source, sink, "CORS Origin misconfiguration due to a $@.", source.getNode(),
- "too permissive or user controlled value"
diff --git a/javascript/ql/src/Security/CWE-942/examples/CorsPermissiveConfigurationBad.js b/javascript/ql/src/Security/CWE-942/examples/CorsPermissiveConfigurationBad.js
deleted file mode 100644
index 68317486a99d..000000000000
--- a/javascript/ql/src/Security/CWE-942/examples/CorsPermissiveConfigurationBad.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { ApolloServer } from 'apollo-server';
-var https = require('https'),
- url = require('url');
-
-var server = https.createServer(function () { });
-
-server.on('request', function (req, res) {
- // BAD: origin is too permissive
- const server_1 = new ApolloServer({
- cors: { origin: true }
- });
-
- let user_origin = url.parse(req.url, true).query.origin;
- // BAD: CORS is controlled by user
- const server_2 = new ApolloServer({
- cors: { origin: user_origin }
- });
-});
\ No newline at end of file
diff --git a/javascript/ql/src/Security/CWE-942/examples/CorsPermissiveConfigurationGood.js b/javascript/ql/src/Security/CWE-942/examples/CorsPermissiveConfigurationGood.js
deleted file mode 100644
index 5e084d089b75..000000000000
--- a/javascript/ql/src/Security/CWE-942/examples/CorsPermissiveConfigurationGood.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { ApolloServer } from 'apollo-server';
-var https = require('https'),
- url = require('url');
-
-var server = https.createServer(function () { });
-
-server.on('request', function (req, res) {
- // GOOD: origin is restrictive
- const server_1 = new ApolloServer({
- cors: { origin: false }
- });
-
- let user_origin = url.parse(req.url, true).query.origin;
- // GOOD: user data is properly sanitized
- const server_2 = new ApolloServer({
- cors: { origin: (user_origin === "https://allowed1.com" || user_origin === "https://allowed2.com") ? user_origin : false }
- });
-});
\ No newline at end of file
From 1c33a0a5231dcb3f1c1487035bb3ef5d23b1e844 Mon Sep 17 00:00:00 2001
From: Napalys Klicius
Date: Tue, 19 Aug 2025 09:57:29 +0200
Subject: [PATCH 12/15] Updated change note to reflect changes
---
.../change-notes/2025-07-31-cors-move-out-of-experimental.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/src/change-notes/2025-07-31-cors-move-out-of-experimental.md b/javascript/ql/src/change-notes/2025-07-31-cors-move-out-of-experimental.md
index 112fb0c628ff..0bf37da88f00 100644
--- a/javascript/ql/src/change-notes/2025-07-31-cors-move-out-of-experimental.md
+++ b/javascript/ql/src/change-notes/2025-07-31-cors-move-out-of-experimental.md
@@ -1,4 +1,4 @@
---
category: minorAnalysis
---
-* The query "CORS misconfiguration" (`js/cors-misconfiguration`) has been promoted from experimental and is now part of the default security suite.
+* The query "CORS misconfiguration for credentials transfer" (`js/cors-misconfiguration-for-credentials`) has been expanded to also detect overly permissive CORS configurations.
From 83cb7882d07023e0524709ae873e3f7b813f7972 Mon Sep 17 00:00:00 2001
From: Napalys Klicius
Date: Thu, 21 Aug 2025 11:09:05 +0000
Subject: [PATCH 13/15] Replace complex wrapper classes with MaD
---
javascript/ql/lib/ext/apollo-server.model.yml | 1 +
javascript/ql/lib/ext/cors.model.yml | 6 +++
.../lib/semmle/javascript/frameworks/Cors.qll | 24 -----------
...figurationForCredentialsCustomizations.qll | 42 ++-----------------
4 files changed, 10 insertions(+), 63 deletions(-)
create mode 100644 javascript/ql/lib/ext/cors.model.yml
delete mode 100644 javascript/ql/lib/semmle/javascript/frameworks/Cors.qll
diff --git a/javascript/ql/lib/ext/apollo-server.model.yml b/javascript/ql/lib/ext/apollo-server.model.yml
index 5962b8ee7d08..35c6929ce264 100644
--- a/javascript/ql/lib/ext/apollo-server.model.yml
+++ b/javascript/ql/lib/ext/apollo-server.model.yml
@@ -10,6 +10,7 @@ extensions:
extensible: sinkModel
data:
- ["@apollo/server", "Member[gql].Argument[0]", "sql-injection"]
+ - ["@apollo/server", "Member[ApolloServer,ApolloServerBase].Argument[0].Member[cors].Member[origin]", "cors-misconfiguration"]
- addsTo:
pack: codeql/javascript-all
diff --git a/javascript/ql/lib/ext/cors.model.yml b/javascript/ql/lib/ext/cors.model.yml
new file mode 100644
index 000000000000..b1fd68a68e71
--- /dev/null
+++ b/javascript/ql/lib/ext/cors.model.yml
@@ -0,0 +1,6 @@
+extensions:
+ - addsTo:
+ pack: codeql/javascript-all
+ extensible: sinkModel
+ data:
+ - ["cors", "Argument[0].Member[origin]", "cors-misconfiguration"]
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Cors.qll b/javascript/ql/lib/semmle/javascript/frameworks/Cors.qll
deleted file mode 100644
index cc190e6f4294..000000000000
--- a/javascript/ql/lib/semmle/javascript/frameworks/Cors.qll
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * Provides classes for working with Cors connectors.
- */
-
-import javascript
-
-/** Provides classes modeling the [cors](https://npmjs.com/package/cors) library. */
-module Cors {
- /**
- * An expression that creates a new CORS configuration.
- */
- class Cors extends DataFlow::CallNode {
- Cors() { this = DataFlow::moduleImport("cors").getAnInvocation() }
-
- /** Get the options used to configure Cors */
- DataFlow::Node getOptionsArgument() { result = this.getArgument(0) }
-
- /** Holds if cors is using default configuration */
- predicate isDefault() { this.getNumArgument() = 0 }
-
- /** Gets the value of the `origin` option used to configure this Cors instance. */
- DataFlow::Node getOrigin() { result = this.getOptionArgument(0, "origin") }
- }
-}
diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/CorsMisconfigurationForCredentialsCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/CorsMisconfigurationForCredentialsCustomizations.qll
index 36f8badcad70..d9bd4e119fb3 100644
--- a/javascript/ql/lib/semmle/javascript/security/dataflow/CorsMisconfigurationForCredentialsCustomizations.qll
+++ b/javascript/ql/lib/semmle/javascript/security/dataflow/CorsMisconfigurationForCredentialsCustomizations.qll
@@ -5,7 +5,6 @@
*/
import javascript
-private import semmle.javascript.frameworks.Cors
module CorsMisconfigurationForCredentials {
/**
@@ -88,46 +87,11 @@ module CorsMisconfigurationForCredentials {
}
/**
- * The value of cors origin when initializing the application.
+ * The value of cors origin configuration.
*/
- class CorsApolloServer extends Sink, DataFlow::ValueNode {
- CorsApolloServer() {
- exists(API::NewNode agql |
- agql = ModelOutput::getATypeNode("ApolloServer").getAnInstantiation() and
- this =
- agql.getOptionArgument(0, "cors").getALocalSource().getAPropertyWrite("origin").getRhs()
- )
- }
-
- override Http::HeaderDefinition getCredentialsHeader() { none() }
- }
-
- /**
- * The value of cors origin when initializing the application.
- */
- class ExpressCors extends Sink, DataFlow::ValueNode {
- ExpressCors() {
- exists(CorsConfiguration config | this = config.getCorsConfiguration().getOrigin())
- }
+ class CorsOriginSink extends Sink, DataFlow::ValueNode {
+ CorsOriginSink() { this = ModelOutput::getASinkNode("cors-misconfiguration").asSink() }
override Http::HeaderDefinition getCredentialsHeader() { none() }
}
-
- /**
- * An express route setup configured with the `cors` package.
- */
- class CorsConfiguration extends DataFlow::MethodCallNode {
- Cors::Cors corsConfig;
-
- CorsConfiguration() {
- exists(Express::RouteSetup setup | this = setup |
- if setup.isUseCall()
- then corsConfig = setup.getArgument(0)
- else corsConfig = setup.getArgument(any(int i | i > 0))
- )
- }
-
- /** Gets the expression that configures `cors` on this route setup. */
- Cors::Cors getCorsConfiguration() { result = corsConfig }
- }
}
From 6e2432e66d3cf5548c92c722b145331fd7ed986d Mon Sep 17 00:00:00 2001
From: Napalys Klicius
Date: Thu, 21 Aug 2025 11:16:24 +0000
Subject: [PATCH 14/15] Merge CORS source classes
---
...figurationForCredentialsCustomizations.qll | 19 ++++++-------------
1 file changed, 6 insertions(+), 13 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/CorsMisconfigurationForCredentialsCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/CorsMisconfigurationForCredentialsCustomizations.qll
index d9bd4e119fb3..0387919fae1b 100644
--- a/javascript/ql/lib/semmle/javascript/security/dataflow/CorsMisconfigurationForCredentialsCustomizations.qll
+++ b/javascript/ql/lib/semmle/javascript/security/dataflow/CorsMisconfigurationForCredentialsCustomizations.qll
@@ -68,24 +68,17 @@ module CorsMisconfigurationForCredentials {
/**
* A value that is or coerces to the string "null".
* This is considered a source because the "null" origin is easy to obtain for an attacker.
+ * An overly permissive value for `origin`
*/
- class NullToStringValue extends Source {
- NullToStringValue() {
+ class PermissiveCorsOriginValue extends Source {
+ PermissiveCorsOriginValue() {
+ this.mayHaveStringValue("*") or
+ this.mayHaveBooleanValue(true) or
this.asExpr() instanceof NullLiteral or
- this.asExpr().mayHaveStringValue("null")
+ this.asExpr().getStringValue() = "null"
}
}
- /** An overly permissive value for `origin` (Apollo) */
- class TrueNullValue extends Source {
- TrueNullValue() { this.mayHaveBooleanValue(true) or this.asExpr() instanceof NullLiteral }
- }
-
- /** An overly permissive value for `origin` (Express) */
- class WildcardValue extends Source {
- WildcardValue() { this.mayHaveStringValue("*") }
- }
-
/**
* The value of cors origin configuration.
*/
From 58f1371263fed1efe039384deb8008f6be0280d0 Mon Sep 17 00:00:00 2001
From: Napalys Klicius
Date: Thu, 21 Aug 2025 12:09:46 +0000
Subject: [PATCH 15/15] Added `cors-misconfiguration` as valid sink kind
---
shared/mad/codeql/mad/ModelValidation.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/shared/mad/codeql/mad/ModelValidation.qll b/shared/mad/codeql/mad/ModelValidation.qll
index 018c1797ddcd..a0062df6b9a6 100644
--- a/shared/mad/codeql/mad/ModelValidation.qll
+++ b/shared/mad/codeql/mad/ModelValidation.qll
@@ -39,7 +39,7 @@ module KindValidation {
"response-splitting", "trust-boundary-violation", "template-injection", "url-forward",
"xslt-injection",
// JavaScript-only currently, but may be shared in the future
- "mongodb.sink",
+ "cors-misconfiguration", "mongodb.sink",
// Swift-only currently, but may be shared in the future
"database-store", "format-string", "hash-iteration-count", "predicate-injection",
"preferences-store", "tls-protocol-version", "transmission", "webview-fetch", "xxe",