From 5c3574c2a4facce6995f51e8c6ea081d69cb7cf1 Mon Sep 17 00:00:00 2001 From: Hans Ott Date: Mon, 17 Jun 2024 10:16:48 +0200 Subject: [PATCH 1/2] Use visit function to collect all StringValue's --- .../graphql/extractInputsFromDocument.test.ts | 23 +++++ .../graphql/extractInputsFromDocument.ts | 97 ++----------------- 2 files changed, 33 insertions(+), 87 deletions(-) diff --git a/library/sources/graphql/extractInputsFromDocument.test.ts b/library/sources/graphql/extractInputsFromDocument.test.ts index 325ec80b7..0c68cfc6e 100644 --- a/library/sources/graphql/extractInputsFromDocument.test.ts +++ b/library/sources/graphql/extractInputsFromDocument.test.ts @@ -319,3 +319,26 @@ t.test("it handles nested query with argument", (t) => { t.same(inputs, ["NestedCity"]); t.end(); }); + +t.test("it parses default values for string arguments", (t) => { + const source = { + query: `query($id: ID = "default") { + user(id: $id) { + id + name + } + }`, + }; + + const document = parse(source.query); + const validationErrors = validate(schema, document); + if (validationErrors.length > 0) { + t.fail(validationErrors[0].message); + return; + } + + const inputs = extractInputsFromDocument(document); + + t.same(inputs, ["default"]); + t.end(); +}); diff --git a/library/sources/graphql/extractInputsFromDocument.ts b/library/sources/graphql/extractInputsFromDocument.ts index 4da351358..bb7227bfd 100644 --- a/library/sources/graphql/extractInputsFromDocument.ts +++ b/library/sources/graphql/extractInputsFromDocument.ts @@ -1,97 +1,20 @@ -import type { - DocumentNode, - ValueNode, - SelectionSetNode, - FieldNode, - ArgumentNode, - FragmentDefinitionNode, -} from "graphql"; - -/** - * Extract user inputs from a value node - * @param value A value node - * @param inputs An array that will be filled with the user inputs - */ -function extractInputsFromValue(value: ValueNode, inputs: string[]) { - switch (value.kind) { - case "StringValue": - inputs.push(value.value); - break; - case "ListValue": - for (const item of value.values) { - extractInputsFromValue(item, inputs); - } - break; - case "ObjectValue": - for (const field of value.fields) { - extractInputsFromValue(field.value, inputs); - } - break; - default: - break; - } -} - -/** - * Extract user inputs from a list of arguments - * @param args A array of argument nodes - * @param inputs An array that will be filled with the user inputs - */ -function extractInputsFromArguments( - args: ReadonlyArray, - inputs: string[] -) { - for (const argument of args) { - extractInputsFromValue(argument.value, inputs); - } -} - -/** - * A recursive function that traverses a selection set and extracts user inputs. - * @param selectionSet A graphql selection set node - * @param inputs An array that will be filled with the user inputs - */ -function traverseSelectionSet( - selectionSet: SelectionSetNode, - inputs: string[] -) { - for (const selection of selectionSet.selections) { - switch (selection.kind) { - case "Field": - const field = selection as FieldNode; - if (field.arguments) { - extractInputsFromArguments(field.arguments, inputs); - } - if (field.selectionSet) { - traverseSelectionSet(field.selectionSet, inputs); - } - break; - case "InlineFragment": - traverseSelectionSet(selection.selectionSet, inputs); - break; - default: - break; - } - } -} +import type { DocumentNode, StringValueNode } from "graphql"; /** * This function extracts user inputs (that could be harmful) from a GraphQL document. * @returns An array of user inputs. */ export function extractInputsFromDocument(document: DocumentNode): string[] { + // Assuming graphql is installed when this function is called + // Don't use normal import for graphql + const { visit } = require("graphql"); + const inputs: string[] = []; + visit(document, { + StringValue(node: StringValueNode) { + inputs.push(node.value); + }, + }); - for (const definition of document.definitions) { - switch (definition.kind) { - case "OperationDefinition": - traverseSelectionSet(definition.selectionSet, inputs); - break; - case "FragmentDefinition": - const fragment = definition as FragmentDefinitionNode; - traverseSelectionSet(fragment.selectionSet, inputs); - break; - } - } return inputs; } From d43526fde955d8347d8497db2c1bf35aafba395c Mon Sep 17 00:00:00 2001 From: Hans Ott Date: Mon, 17 Jun 2024 10:21:28 +0200 Subject: [PATCH 2/2] Add try catch around require --- .../sources/graphql/extractInputsFromDocument.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/library/sources/graphql/extractInputsFromDocument.ts b/library/sources/graphql/extractInputsFromDocument.ts index bb7227bfd..aec11d959 100644 --- a/library/sources/graphql/extractInputsFromDocument.ts +++ b/library/sources/graphql/extractInputsFromDocument.ts @@ -5,12 +5,17 @@ import type { DocumentNode, StringValueNode } from "graphql"; * @returns An array of user inputs. */ export function extractInputsFromDocument(document: DocumentNode): string[] { - // Assuming graphql is installed when this function is called - // Don't use normal import for graphql - const { visit } = require("graphql"); + let graphql; + try { + // Assuming graphql is installed when this function is called + // Don't use normal import for graphql + graphql = require("graphql"); + } catch (e) { + return []; + } const inputs: string[] = []; - visit(document, { + graphql.visit(document, { StringValue(node: StringValueNode) { inputs.push(node.value); },