Skip to content

Commit

Permalink
Backport NoSchemaIntrospectionCustomRule from graphql@15.2.0
Browse files Browse the repository at this point in the history
This rule is adapted from graphql/graphql-js#2600
and should be replaced once using graphql >=15.2.0.

Co-authored by: dzucconi <mail@damonzucconi.com>
  • Loading branch information
joeyAghion committed Sep 2, 2020
1 parent c88a896 commit 1688b5e
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 4 deletions.
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ DIFFUSION_API_BASE=https://diffusion-staging.artsy.net/api
DIFFUSION_TOKEN=REPLACE
EMBEDLY_ENDPOINT=https://i.embed.ly/1/display
EMBEDLY_KEY=REPLACE
ENABLE_APOLLO=true
ENABLE_APOLLO=false
ENABLE_CONSIGNMENTS_STITCHING=true
ENABLE_QUERY_TRACING=false
ENABLE_REQUEST_LOGGING=true
Expand All @@ -37,6 +37,7 @@ GRAVITY_SECRET=REPLACE
HMAC_SECRET=https://www.youtube.com/watch?v=F5bAa6gFvLs
IMPULSE_API_BASE=https://impulse-staging.artsy.net/api
IMPULSE_APPLICATION_ID=REPLACE
INTROSPECT_TOKEN=replaceme
KAWS_API_BASE=https://kaws-staging.artsy.net
MEMCACHED_URL=localhost:11211
NODE_ENV=development
Expand Down
2 changes: 2 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ const {
HMAC_SECRET,
IMPULSE_API_BASE,
IMPULSE_APPLICATION_ID,
INTROSPECT_TOKEN,
IP_BLACKLIST,
LOG_QUERY_DETAILS_THRESHOLD,
MEMCACHED_MAX_POOL,
Expand Down Expand Up @@ -186,6 +187,7 @@ export default {
HMAC_SECRET: HMAC_SECRET as string,
IMPULSE_API_BASE,
IMPULSE_APPLICATION_ID,
INTROSPECT_TOKEN,
IP_BLACKLIST: IP_BLACKLIST || "",
LOG_QUERY_DETAILS_THRESHOLD,
MEMCACHED_MAX_POOL: Number(MEMCACHED_MAX_POOL) || 10,
Expand Down
14 changes: 13 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { ErrorExtension } from "./extensions/errorExtension"
import { LoggingExtension } from "./extensions/loggingExtension"
import { principalFieldDirectiveExtension } from "./extensions/principalFieldDirectiveExtension"
import { principalFieldDirectiveValidation } from "validations/principalFieldDirectiveValidation"
import { NoSchemaIntrospectionCustomRule } from "validations/noSchemaIntrospectionCustomRule"
import * as Sentry from "@sentry/node"

const {
Expand All @@ -44,6 +45,7 @@ const {
RESOLVER_TIMEOUT_MS,
SENTRY_PRIVATE_DSN,
ENABLE_APOLLO,
INTROSPECT_TOKEN,
} = config

const enableSentry = !!SENTRY_PRIVATE_DSN
Expand Down Expand Up @@ -220,7 +222,17 @@ function startApp(appSchema, path: string) {
userAgent,
}

const validationRules = [principalFieldDirectiveValidation]
const validationRules = [
principalFieldDirectiveValidation,

// require Authorization header for introspection (in production if configured)
...(PRODUCTION_ENV &&
INTROSPECT_TOKEN &&
req.headers["authorization"] !== `Bearer ${INTROSPECT_TOKEN}`
? [NoSchemaIntrospectionCustomRule]
: []),
]

if (QUERY_DEPTH_LIMIT)
validationRules.push(depthLimit(QUERY_DEPTH_LIMIT))

Expand Down
41 changes: 41 additions & 0 deletions src/validations/noSchemaIntrospectionCustomRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {
ASTVisitor,
GraphQLError,
FieldNode,
ValidationContext,
getNamedType,
isIntrospectionType,
} from "graphql"

// Adapted from https://github.com/graphql/graphql-js/pull/2600.
// TODO: replace once using graphql >=15.2.0

/**
* Prohibit introspection queries
*
* A GraphQL document is only valid if all fields selected are not fields that
* return an introspection type.
*
* Note: This rule is optional and is not part of the Validation section of the
* GraphQL Specification. This rule effectively disables introspection, which
* does not reflect best practices and should only be done if absolutely necessary.
*/
export const NoSchemaIntrospectionCustomRule = (
context: ValidationContext
): ASTVisitor => {
return {
Field(node: FieldNode) {
const contextType = context.getType()
if (!contextType) return
const type = getNamedType(contextType)
if (type && isIntrospectionType(type)) {
context.reportError(
new GraphQLError(
`GraphQL introspection has been disabled, but the requested query contained the field "${node.name.value}".`,
node
)
)
}
},
}
}
4 changes: 2 additions & 2 deletions src/validations/principalFieldDirectiveValidation.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { GraphQLError, BREAK } from "graphql"
import { GraphQLError, BREAK, ASTVisitor } from "graphql"

export const principalFieldDirectiveValidation = (context) => {
export const principalFieldDirectiveValidation = (context): ASTVisitor => {
let directivesSeen = 0
return {
Directive(node) {
Expand Down

0 comments on commit 1688b5e

Please sign in to comment.