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