mirror of
https://github.com/github/codeql.git
synced 2026-04-27 17:55:19 +02:00
Move CORS misconfiguration query from experimental to Security
This commit is contained in:
@@ -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"
|
||||
@@ -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,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 }
|
||||
}
|
||||
Reference in New Issue
Block a user