mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Merge pull request #20146 from Napalys/js/move-cors-query-from-experimental
JS: Move cors-misconfiguration query from experimental to Security
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -5,6 +5,13 @@ 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"]
|
||||
- ["@apollo/server", "Member[ApolloServer,ApolloServerBase].Argument[0].Member[cors].Member[origin]", "cors-origin"]
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/javascript-all
|
||||
extensible: typeModel
|
||||
@@ -13,3 +20,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]"]
|
||||
|
||||
6
javascript/ql/lib/ext/cors.model.yml
Normal file
6
javascript/ql/lib/ext/cors.model.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/javascript-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["cors", "Argument[0].Member[origin]", "cors-origin"]
|
||||
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for reasoning about
|
||||
* overly permissive CORS configurations, as well as
|
||||
* extension points for adding your own.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/** Module containing sources, sinks, and sanitizers for overly permissive CORS configurations. */
|
||||
module CorsPermissiveConfiguration {
|
||||
private newtype TFlowState =
|
||||
TTaint() or
|
||||
TPermissive()
|
||||
|
||||
/** A flow state to associate 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 = TPermissive() and result = "permissive"
|
||||
}
|
||||
}
|
||||
|
||||
/** Predicates for working with flow states. */
|
||||
module FlowState {
|
||||
/** A tainted value. */
|
||||
FlowState taint() { result = TTaint() }
|
||||
|
||||
/** A permissive value (true, null, or "*"). */
|
||||
FlowState permissive() { result = TPermissive() }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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` configuration. */
|
||||
class PermissiveValue extends Source {
|
||||
PermissiveValue() {
|
||||
this.mayHaveBooleanValue(true) or
|
||||
this.asExpr() instanceof NullLiteral or
|
||||
this.mayHaveStringValue("*")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The value of cors origin when initializing the application.
|
||||
*/
|
||||
class CorsOriginSink extends Sink, DataFlow::ValueNode {
|
||||
CorsOriginSink() { this = ModelOutput::getASinkNode("cors-origin").asSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer for CORS configurations where credentials are explicitly disabled.
|
||||
* When credentials are false, using "*" for origin is a legitimate pattern.
|
||||
*/
|
||||
private class CredentialsDisabledSanitizer extends Sanitizer {
|
||||
CredentialsDisabledSanitizer() {
|
||||
exists(DataFlow::SourceNode config, DataFlow::CallNode call |
|
||||
call.getArgument(0).getALocalSource() = config and
|
||||
this = config.getAPropertyWrite("origin").getRhs() and
|
||||
config.getAPropertyWrite("credentials").getRhs().mayHaveBooleanValue(false)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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 PermissiveValue and state = FlowState::permissive()
|
||||
or
|
||||
source instanceof RemoteFlowSource and state = FlowState::taint()
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink, FlowState state) {
|
||||
sink instanceof CorsOriginSink and
|
||||
state = [FlowState::taint(), FlowState::permissive()]
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
predicate observeDiffInformedIncrementalMode() { any() }
|
||||
}
|
||||
|
||||
module CorsPermissiveConfigurationFlow =
|
||||
TaintTracking::GlobalWithState<CorsPermissiveConfigurationConfig>;
|
||||
@@ -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>
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @name Permissive CORS configuration
|
||||
* @description Cross-origin resource sharing (CORS) policy allows overly broad access.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 6.0
|
||||
* @precision high
|
||||
* @id js/cors-permissive-configuration
|
||||
* @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 allows broad access due to $@.", source.getNode(),
|
||||
"permissive or user controlled value"
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The query "Permissive CORS configuration" (`js/cors-permissive-configuration`) has been promoted from experimental and is now part of the default security suite.
|
||||
@@ -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) }
|
||||
}
|
||||
}
|
||||
@@ -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") }
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
|
||||
A server can use <code>CORS</code> (Cross-Origin Resource Sharing) to relax the
|
||||
restrictions imposed by the <code>SOP</code> (Same-Origin Policy), allowing controlled, secure
|
||||
cross-origin requests when necessary.
|
||||
|
||||
A server with an overly permissive <code>CORS</code> configuration may inadvertently
|
||||
expose sensitive data or lead to <code>CSRF</code> which is an attack that allows attackers to trick
|
||||
users into performing unwanted operations in websites they're authenticated to.
|
||||
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
|
||||
When the <code>origin</code> is set to <code>true</code>, it signifies that the server
|
||||
is accepting requests from <code>any</code> origin, potentially exposing the system to
|
||||
CSRF attacks. This can be fixed using <code>false</code> as origin value or using a whitelist.
|
||||
|
||||
</p>
|
||||
<p>
|
||||
|
||||
On the other hand, if the <code>origin</code> is
|
||||
set to <code>null</code>, it can be exploited by an attacker to deceive a user into making
|
||||
requests from a <code>null</code> origin form, often hosted within a sandboxed iframe.
|
||||
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
If the <code>origin</code> value is user controlled, make sure that the data
|
||||
is properly sanitized.
|
||||
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
|
||||
In the example below, the <code>server_1</code> accepts requests from any origin
|
||||
since the value of <code>origin</code> is set to <code>true</code>.
|
||||
And <code>server_2</code>'s origin is user-controlled.
|
||||
|
||||
</p>
|
||||
|
||||
<sample src="examples/CorsPermissiveConfigurationBad.js"/>
|
||||
|
||||
<p>
|
||||
|
||||
In the example below, the <code>server_1</code> CORS is restrictive so it's not
|
||||
vulnerable to CSRF attacks. And <code>server_2</code>'s is using properly sanitized
|
||||
user-controlled data.
|
||||
|
||||
</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>
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* @name overly 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 CorsPermissiveConfigurationQuery
|
||||
import CorsPermissiveConfigurationFlow::PathGraph
|
||||
|
||||
from
|
||||
CorsPermissiveConfigurationFlow::PathNode source, CorsPermissiveConfigurationFlow::PathNode sink
|
||||
where CorsPermissiveConfigurationFlow::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "CORS Origin misconfiguration due to a $@.", source.getNode(),
|
||||
"too permissive or user controlled value"
|
||||
@@ -1,141 +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
|
||||
import Cors::Cors
|
||||
import Apollo::Apollo
|
||||
|
||||
/** 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"
|
||||
}
|
||||
|
||||
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() }
|
||||
|
||||
/** 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 { }
|
||||
|
||||
/**
|
||||
* 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 }
|
||||
}
|
||||
|
||||
/** 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(ApolloServer agql |
|
||||
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 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 getCorsConfiguration() { result = corsConfig }
|
||||
}
|
||||
}
|
||||
@@ -1,69 +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<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 }
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
./experimental/Security/CWE-942/CorsPermissiveConfiguration.ql
|
||||
@@ -1,3 +1,12 @@
|
||||
#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 allows broad access due to $@. | 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 Origin allows broad access due to $@. | 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 Origin allows broad access due to $@. | 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 Origin allows broad access due to $@. | 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 Origin allows broad access due to $@. | 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 Origin allows broad access due to $@. | 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 Origin allows broad access due to $@. | express-test.js:10:42:10:45 | true | permissive or user controlled value |
|
||||
| express-test.js:48:17:48:19 | '*' | express-test.js:48:17:48:19 | '*' | express-test.js:48:17:48:19 | '*' | CORS Origin allows broad access due to $@. | express-test.js:48:17:48:19 | '*' | permissive or user controlled value |
|
||||
edges
|
||||
| apollo-test.js:8:9:8:19 | user_origin | apollo-test.js:26:25:26:35 | user_origin | provenance | |
|
||||
| apollo-test.js:8:9:8:19 | user_origin | apollo-test.js:26:25:26:35 | user_origin | provenance | |
|
||||
@@ -6,8 +15,11 @@ edges
|
||||
| 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:19 | user_origin | express-test.js:33:17:33:27 | user_origin | provenance | |
|
||||
| express-test.js:10:9:10:19 | 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:19 | user_origin | provenance | |
|
||||
| express-test.js:10:23:10:46 | url.par ... , true) | express-test.js:10:9:10:19 | 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 | |
|
||||
nodes
|
||||
| apollo-test.js:8:9:8:19 | user_origin | semmle.label | user_origin |
|
||||
| apollo-test.js:8:9:8:19 | user_origin | semmle.label | user_origin |
|
||||
@@ -20,15 +32,13 @@ nodes
|
||||
| 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:19 | user_origin | semmle.label | user_origin |
|
||||
| express-test.js:10:9:10:19 | 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: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 |
|
||||
| express-test.js:33:17:33:27 | user_origin | semmle.label | user_origin |
|
||||
| express-test.js:48:17:48:19 | '*' | semmle.label | '*' |
|
||||
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 |
|
||||
@@ -0,0 +1,2 @@
|
||||
query: Security/CWE-942/CorsPermissiveConfiguration.ql
|
||||
postprocess: utils/test/InlineExpectationsTestQuery.ql
|
||||
@@ -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
|
||||
});
|
||||
});
|
||||
@@ -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,30 @@ 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));
|
||||
|
||||
// GOOD: CORS allows any origin but credentials are disabled (safe pattern)
|
||||
var app5 = express();
|
||||
var corsOption5 = {
|
||||
origin: '*',
|
||||
credentials: false
|
||||
};
|
||||
app5.use(cors(corsOption5));
|
||||
|
||||
// BAD: CORS allows any origin with credentials enabled
|
||||
var app6 = express();
|
||||
var corsOption6 = {
|
||||
origin: '*', // $ Alert
|
||||
credentials: true
|
||||
};
|
||||
app6.use(cors(corsOption6));
|
||||
});
|
||||
Reference in New Issue
Block a user