Skip to content

JS: Move cors-misconfiguration query from experimental to Security #20146

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions javascript/ql/lib/ext/apollo-server.model.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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]"]
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
*/

import javascript
import Cors::Cors
import Apollo::Apollo
private import semmle.javascript.frameworks.Cors

/** Module containing sources, sinks, and sanitizers for overly permissive CORS configurations. */
module CorsPermissiveConfiguration {
Expand All @@ -25,20 +24,10 @@ module CorsPermissiveConfiguration {
or
this = TWildcard() and result = "wildcard"
}

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 FlowState fromFlowLabel(DataFlow::FlowLabel label) { result.toFlowLabel() = label }

/** A tainted value. */
FlowState taint() { result = TTaint() }

Expand All @@ -64,32 +53,13 @@ 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.
*/
private class ActiveThreatModelSourceAsSource extends Source instanceof ActiveThreatModelSource {
ActiveThreatModelSourceAsSource() { not this instanceof ClientSideRemoteFlowSource }
}

/** A flow label representing `true` and `null` values. */
abstract deprecated class TrueAndNull extends DataFlow::FlowLabel {
TrueAndNull() { this = "TrueAndNull" }
}

deprecated TrueAndNull truenullLabel() { any() }

/** A flow label representing `*` value. */
abstract deprecated class Wildcard extends DataFlow::FlowLabel {
Wildcard() { this = "Wildcard" }
}

deprecated Wildcard wildcardLabel() { any() }

/** An overly permissive value for `origin` (Apollo) */
class TrueNullValue extends Source {
TrueNullValue() { this.mayHaveBooleanValue(true) or this.asExpr() instanceof NullLiteral }
Expand All @@ -105,7 +75,8 @@ module CorsPermissiveConfiguration {
*/
class CorsApolloServer extends Sink, DataFlow::ValueNode {
CorsApolloServer() {
exists(ApolloServer agql |
exists(API::NewNode agql |
agql = ModelOutput::getATypeNode("ApolloServer").getAnInstantiation() and
this =
agql.getOptionArgument(0, "cors").getALocalSource().getAPropertyWrite("origin").getRhs()
)
Expand All @@ -125,7 +96,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 |
Expand All @@ -136,6 +107,6 @@ module CorsPermissiveConfiguration {
}

/** Gets the expression that configures `cors` on this route setup. */
Cors getCorsConfiguration() { result = corsConfig }
Cors::Cors getCorsConfiguration() { result = corsConfig }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,31 +39,3 @@ module CorsPermissiveConfigurationConfig implements DataFlow::StateConfigSig {

module CorsPermissiveConfigurationFlow =
TaintTracking::GlobalWithState<CorsPermissiveConfigurationConfig>;

/**
* 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 }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>

<overview>
<p>

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.

</p>
<p>

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.

</p>
</overview>

<recommendation>
<p>

When the <code>origin</code> is set to <code>true</code>, the server
accepts requests from any origin, potentially exposing the system to
CSRF attacks. Use <code>false</code> as the origin value or implement a whitelist
of allowed origins instead.

</p>
<p>

When the <code>origin</code> is set to <code>null</code>, it can be
exploited by an attacker who can deceive a user into making
requests from a <code>null</code> origin, often hosted within a sandboxed iframe.

</p>
<p>

If the <code>origin</code> value is user-controlled, ensure that the data
is properly sanitized and validated against a whitelist of allowed origins.

</p>
</recommendation>

<example>
<p>

In the following example, <code>server_1</code> accepts requests from any origin
because the value of <code>origin</code> is set to <code>true</code>.
<code>server_2</code> uses user-controlled data for the origin without validation.

</p>

<sample src="examples/CorsPermissiveConfigurationBad.js"/>

<p>

To fix these issues, <code>server_1</code> uses a restrictive CORS configuration
that is not vulnerable to CSRF attacks. <code>server_2</code> properly validates
user-controlled data against a whitelist before using it.

</p>

<sample src="examples/CorsPermissiveConfigurationGood.js"/>
</example>

<references>
<li>Mozilla Developer Network: <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin">CORS, Access-Control-Allow-Origin</a>.</li>
<li>W3C: <a href="https://w3c.github.io/webappsec-cors-for-developers/#resources">CORS for developers, Advice for Resource Owners</a>.</li>
</references>
</qhelp>
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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"
Original file line number Diff line number Diff line change
@@ -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.
36 changes: 0 additions & 36 deletions javascript/ql/src/experimental/Security/CWE-942/Apollo.qll

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
query: Security/CWE-942/CorsPermissiveConfiguration.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql
Loading