trailofbits.javascript.apollo-graphql.v3-cors-audit.v3-potentially-bad-cors

profile photo of trailofbitstrailofbits
Author
unknown
Download Count*

The Apollo GraphQL server is setup with a CORS policy that does not deny all origins. Carefully review the origins to see if any of them are incorrectly setup (third-party websites, bad regexes, functions that reflect every origin, etc.).

Run Locally

Run in CI

Defintion

rules:
  - id: v3-potentially-bad-cors
    languages:
      - js
      - ts
    message: The Apollo GraphQL server is setup with a CORS policy that does not
      deny all origins. Carefully review the origins to see if any of them are
      incorrectly setup (third-party websites, bad regexes, functions that
      reflect every origin, etc.).
    severity: WARNING
    metadata:
      category: security
      cwe: "CWE-942: Permissive Cross-domain Policy with Untrusted Domains"
      subcategory:
        - audit
      confidence: LOW
      likelihood: MEDIUM
      impact: MEDIUM
      technology:
        - graphql
        - apollo-graphql-server
        - apollo-graphql-server-v3
      description: Potentially bad CORS policy
      references:
        - https://www.apollographql.com/docs/apollo-server/v3/security/cors#configuring-cors-options-for-apollo-server
      license: AGPL-3.0 license
      vulnerability_class:
        - Improper Validation
    mode: taint
    pattern-sources:
      - pattern: |
          { origin: function(...) {...} }
      - patterns:
          - pattern-inside: |
              { origin: $NOT_KNOWN_GOOD_CORS_ORIGIN }
          - metavariable-pattern:
              metavariable: $NOT_KNOWN_GOOD_CORS_ORIGIN
              patterns:
                - pattern-not: |
                    false
                - pattern-not: |
                    []
    pattern-sinks:
      - patterns:
          - pattern: |
              {..., cors: $CORS_ORIGIN, ...}
          - focus-metavariable: $CORS_ORIGIN

Examples

v3-cors-audit.js

import 'apollo-server';

// GOOD: CORS is defined to deny all
const apollo_graphql_potentially_bad_cors_good_1 = new ApolloServer({
    typeDefs,
    resolvers,
    //ok: v3-potentially-bad-cors
    cors: { origin: false }
});

// GOOD: CORS is defined to deny all with []
const apollo_graphql_potentially_bad_cors_good_2 = new ApolloServer({
    typeDefs,
    resolvers,
    //ok: v3-potentially-bad-cors
    cors: { origin: [] }
});

// GOOD: CORS is defined to deny all with [] from var
const apollo_graphql_potentially_bad_cors_good_3_var = { origin: [] }
const apollo_graphql_potentially_bad_cors_good_3 = new ApolloServer({
    typeDefs,
    resolvers,
    //ok: v3-potentially-bad-cors
    cors: apollo_graphql_potentially_bad_cors_good_3_var
});

// ====================
// BAD: Has a very permissive 'cors'
const apollo_server_bad_cors_bad_1 = new ApolloServer({
    typeDefs,
    resolvers,
    //ruleid: v3-potentially-bad-cors
    cors: { origin: true }
});

// BAD: Has a very permissive 'cors' from a variable
const apollo_server_bad_cors_bad_2_var = { origin: true }
const apollo_server_bad_cors_bad_2 = new ApolloServer({
    typeDefs,
    resolvers,
    //ruleid: v3-potentially-bad-cors
    cors: apollo_server_bad_cors_bad_2_var
});

// ====================
// BAD: string
const apollo_graphql_potentially_bad_cors_bad_3 = new ApolloServer({
    typeDefs,
    resolvers,
    //ruleid: v3-potentially-bad-cors
    cors: { origin: "attacker.com"}
});

// BAD: regex
const apollo_graphql_potentially_bad_cors_bad_4 = new ApolloServer({
    typeDefs,
    resolvers,
    //ruleid: v3-potentially-bad-cors
    cors: { origin: /\.attacker\.com$/ }
});

// BAD: array of strings
const apollo_graphql_potentially_bad_cors_bad_5 = new ApolloServer({
    typeDefs,
    resolvers,
    //ruleid: v3-potentially-bad-cors
    cors: { origin: ["attacker.com", "attacker2.com", "attacker3.com"]}
});


// BAD: array of strings and regex
const apollo_graphql_potentially_bad_cors_bad_6 = new ApolloServer({
    typeDefs,
    resolvers,
    //ruleid: v3-potentially-bad-cors
    cors: { origin: ["attacker.com", /attacker2\.com/, "attacker3.com"]}
});

// BAD: function
const apollo_graphql_potentially_bad_cors_bad_7 = new ApolloServer({
    typeDefs,
    resolvers,
    //ruleid: v3-potentially-bad-cors
    cors: { 
        origin: function (origin, callback) {
            callback(null, true)
      }
    }
});

// BAD: function from var
const apollo_graphql_potentially_bad_cors_bad_8_var = { 
    origin: function (origin, callback) {
        callback(null, true)
  }
}
const apollo_graphql_potentially_bad_cors_bad_8 = new ApolloServer({
    typeDefs,
    resolvers,
    //ruleid: v3-potentially-bad-cors
    cors: apollo_graphql_potentially_bad_cors_bad_8_var
});