Created
November 12, 2020 12:48
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} |
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);
};
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
wouldn't this fail if the first selection isn't __schema or __type - but some other valid query selector?