Move CORS misconfiguration query from experimental to Security

This commit is contained in:
Napalys Klicius
2025-07-30 09:49:04 +00:00
parent 6c00ceaec9
commit 358617f533
13 changed files with 12 additions and 11 deletions

View File

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

View File

@@ -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) }
}
}

View File

@@ -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") }
}
}

View File

@@ -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 }
}
}

View File

@@ -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 }
}