Skip to content

Instantly share code, notes, and snippets.

@andyrichardson
Created November 12, 2020 12:48
Show Gist options
  • Save andyrichardson/0f9c7580c38d0fefa904046aa893131c to your computer and use it in GitHub Desktop.
Save andyrichardson/0f9c7580c38d0fefa904046aa893131c to your computer and use it in GitHub Desktop.
A secure method for checking if an incoming query is an introspection query.
const isIntrospectionQuery = (arg: string) => {
const query = parse(arg);
const opDefs = query.definitions.filter(d => d.kind == "OperationDefinition") as OperationDefinitionNode[];
// Must only have one definition
if (opDefs.length > 1) {
return false;
}
const selections = opDefs[0].selectionSet.selections;
// Must only have one selection
if (selections.length > 1) {
return false
}
const selection = selections[0];
// Must have single field
if (selection.kind !== "Field") {
return false;
}
if (selection.name.value !== "__schema") {
return false;
}
return true;
}
@tbrannam
Copy link

This should evaluate all the selectors and return true if any are on the blacklist.

function isOperationDefinition(
  definition: DefinitionNode,
): definition is OperationDefinitionNode {
  return definition.kind === 'OperationDefinition';
}

const excludedSelectionNameValues = ['__schema', '__type'];

const isIntrospectionQuery = (query: string) => {
  const document = parse(query);

  const operationDefinitions = document.definitions.filter(
    isOperationDefinition,
  );

  return operationDefinitions
    .flatMap(operationDefinition => {
      return operationDefinition.selectionSet.selections.map(selection => {
        if (selection.kind !== 'Field') {
          return false;
        }
        return excludedSelectionNameValues.includes(selection.name.value);
      });
    })
    .reduce((prev, curr) => {
      return prev || curr;
    }, false);
};

@andyrichardson
Copy link
Author

wouldn't this fail if the first selection isn't __schema or __type - but some other valid query selector?

@tbrannam yes that is correct (and intentional). The function returns true if (and only if) it is an introspection query.

Exclusively an introspection query

This gist: true
Your example: true

query IntrospectionQuery {
  __schema {
    queryType { name }
  }
}

A query with introspection fields

This gist: false
Your example: true

query IntrospectionQuery {
  user {
    id
  }
  __schema {
    queryType { name }
  }
}

If you check out this issue you'll see there are a number of folks taking your approach in order to determine whether auth is required. This is problematic as it means auth can be bypassed by accompanying a query with introspective fields, hence why I made this gist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment