Skip to content

Commit

Permalink
Merge pull request #27 from trailofbits/dev-javascript-apollo-graphql…
Browse files Browse the repository at this point in the history
…-rules

Add apollo-graphql semgrep rules
  • Loading branch information
GrosQuildu authored Jun 13, 2023
2 parents e3016e6 + fa59793 commit 75a768d
Show file tree
Hide file tree
Showing 16 changed files with 1,012 additions and 20 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,21 @@ $ semgrep --config /path/to/semgrep-rules/hanging-goroutine.yml -o leaks.txt'
| [panic-in-function-returning-result](rs/panic-in-function-returning-result.yaml) | [🛝🔗](https://semgrep.dev/playground/r/trailofbits.rs.panic-in-function-returning-result.panic-in-function-returning-result) | 🟩 | 🌘 | Calling `unwrap` or `expect` in a function returning a `Result` |
### javascript
| ID | Playground | Impact | Confidence | Description |
| -- | :--------: | :----: | :--------: | ----------- |
| [schema-directives](javascript/apollo-graphql/schema-directives.yaml) | [🛝🔗](https://semgrep.dev/playground/r/trailofbits.javascript.apollo-graphql.schema-directives.schema-directives) | 🟥 | 🌗 | Use of outdated ApolloServer option 'schemaDirectives' |
| [use-of-graphql-upload](javascript/apollo-graphql/use-of-graphql-upload.yaml) | [🛝🔗](https://semgrep.dev/playground/r/trailofbits.javascript.apollo-graphql.use-of-graphql-upload.use-of-graphql-upload) | 🟧 | 🌕 | Use of the graphql-upload library |
| [v3-potentially-bad-cors](javascript/apollo-graphql/v3-cors-audit.yaml) | [🛝🔗](https://semgrep.dev/playground/r/trailofbits.javascript.apollo-graphql.v3-cors-audit.v3-potentially-bad-cors) | 🟧 | 🌕 | Potentially bad CORS policy |
| [v3-express-bad-cors](javascript/apollo-graphql/v3-cors-express.yaml) | [🛝🔗](https://semgrep.dev/playground/r/trailofbits.javascript.apollo-graphql.v3-cors-express.v3-express-bad-cors) | 🟥 | 🌗 | Bad CORS policy |
| [v3-express-no-cors](javascript/apollo-graphql/v3-cors-express.yaml) | [🛝🔗](https://semgrep.dev/playground/r/trailofbits.javascript.apollo-graphql.v3-cors-express.v3-express-no-cors) | 🟩 | 🌘 | Lack of CORS policy |
| [v3-bad-cors](javascript/apollo-graphql/v3-cors.yaml) | [🛝🔗](https://semgrep.dev/playground/r/trailofbits.javascript.apollo-graphql.v3-cors.v3-bad-cors) | 🟥 | 🌗 | Bad CORS policy |
| [v3-no-cors](javascript/apollo-graphql/v3-cors.yaml) | [🛝🔗](https://semgrep.dev/playground/r/trailofbits.javascript.apollo-graphql.v3-cors.v3-no-cors) | 🟩 | 🌘 | Lack of CORS policy |
| [v3-csrf-prevention](javascript/apollo-graphql/v3-csrf-prevention.yaml) | [🛝🔗](https://semgrep.dev/playground/r/trailofbits.javascript.apollo-graphql.v3-csrf-prevention.v3-csrf-prevention) | 🟧 | 🌘 | Lack of CSRF prevention |
| [v4-csrf-prevention](javascript/apollo-graphql/v4-csrf-prevention.yaml) | [🛝🔗](https://semgrep.dev/playground/r/trailofbits.javascript.apollo-graphql.v4-csrf-prevention.v4-csrf-prevention) | 🟧 | 🌘 | CSRF protection disabled |
## Contributing
Pull Requests and issues are welcomed!
Expand Down
16 changes: 16 additions & 0 deletions javascript/apollo-graphql/schema-directives.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// BAD: Has 'schemaDirectives'
//ruleid: schema-directives
const apollo_server_1 = new ApolloServer({
typeDefs,
resolvers,
schemaDirectives: {
rateLimit: rateLimitDirective
},
});

// Good: Does not have 'schemaDirectives'
//ok: schema-directives
const apollo_server_3 = new ApolloServer({
typeDefs,
resolvers,
});
23 changes: 23 additions & 0 deletions javascript/apollo-graphql/schema-directives.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
rules:
- id: schema-directives
message: >-
The Apollo GraphQL uses the 'schemaDirectives' option. This works in ApolloServer v2, but does nothing in version >=3. Depending on what the directives are used for, this can expose authenticated endpoints, disable rate limiting, and more. See the references on how to create custom directives in v3 and v4.
languages: [js, ts]
severity: ERROR
metadata:
category: security
cwe: "CWE-686: Function Call With Incorrect Argument Type"
subcategory: [vuln]
confidence: MEDIUM
likelihood: MEDIUM
impact: HIGH
technology:
- graphql
- apollo-graphql-server
description: "Use of outdated ApolloServer option 'schemaDirectives'"
references:
- https://www.apollographql.com/docs/apollo-server/schema/directives/#custom-directives

pattern-either:
- pattern: |
new ApolloServer({..., schemaDirectives: ..., ...})
2 changes: 2 additions & 0 deletions javascript/apollo-graphql/use-of-graphql-upload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
//ruleid: use-of-graphql-upload
app.use(graphqlUploadExpress());
22 changes: 22 additions & 0 deletions javascript/apollo-graphql/use-of-graphql-upload.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
rules:
- id: use-of-graphql-upload
languages: [js, ts]
message: >-
The Apollo GraphQL server is using the graphql-upload library. This library allows file uploads using POSTs with content-type: multipart/form-data, which can enable to CSRF attacks. Ensure that you are enabling CSRF protection if you really need to use graphql-upload .
severity: WARNING
metadata:
category: security
cwe: "CWE-352: Cross-Site Request Forgery (CSRF)"
subcategory: [vuln]
confidence: LOW
likelihood: MEDIUM
impact: MEDIUM
technology:
- graphql
- apollo-graphql-server
description: "Use of the graphql-upload library"
references:
- https://github.com/apollographql/apollo-server/security/advisories/GHSA-2p3c-p3qw-69r4

patterns:
- pattern: app.use(graphqlUploadExpress());
104 changes: 104 additions & 0 deletions javascript/apollo-graphql/v3-cors-audit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
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
});

44 changes: 44 additions & 0 deletions javascript/apollo-graphql/v3-cors-audit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
rules:
# Rule for potentially bad CORS policies in the batteries-included apollo-server
- 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
mode: taint

pattern-sources:
# Remove this once the above matches functions (https://github.com/returntocorp/semgrep/issues/6748)
- pattern: |
{ origin: function(...) {...} }
- patterns:
- pattern-inside: |
{ origin: $NOT_KNOWN_GOOD_CORS_ORIGIN }
- metavariable-pattern:
metavariable: $NOT_KNOWN_GOOD_CORS_ORIGIN
patterns:
# 'false' denies every origin
- pattern-not: |
false
# An empty array denies every origin.
- pattern-not: |
[]
pattern-sinks:
- patterns:
- pattern: |
{..., cors: $CORS_ORIGIN, ...}
- focus-metavariable: $CORS_ORIGIN
Loading

0 comments on commit 75a768d

Please sign in to comment.