|
1 | 1 | import { NextFunction, Request, Response } from 'express'; |
2 | 2 | import fs from 'fs'; |
3 | | -import { OperationDefinitionNode, parse } from 'graphql'; |
| 3 | +import { GraphQLError, OperationDefinitionNode, parse } from 'graphql'; |
4 | 4 | import path from 'path'; |
5 | 5 | // @ts-ignore |
6 | 6 | import glob from 'glob'; |
@@ -33,27 +33,43 @@ export class GraphQLQueryPurifier { |
33 | 33 |
|
34 | 34 | files.forEach((file: string) => { |
35 | 35 | if (path.extname(file) === '.gql') { |
36 | | - const content = fs.readFileSync(file, 'utf8'); |
37 | | - const parsedQuery = parse(content); |
| 36 | + const content = fs.readFileSync(file, 'utf8').trim(); |
38 | 37 |
|
39 | | - parsedQuery.definitions.forEach((definition) => { |
40 | | - if (definition.kind === 'OperationDefinition') { |
41 | | - const operationDefinition = definition as OperationDefinitionNode; |
| 38 | + if (!content) { |
| 39 | + console.warn(`Warning: Empty or invalid GraphQL file found: ${file}`); |
| 40 | + return; |
| 41 | + } |
42 | 42 |
|
43 | | - let queryName = operationDefinition.name?.value; |
44 | | - if (!queryName) { |
45 | | - // Extract the name from the first field of the selection set |
46 | | - const firstField = operationDefinition.selectionSet.selections[0]; |
47 | | - if (firstField && firstField.kind === 'Field') { |
48 | | - queryName = firstField.name.value; |
| 43 | + try { |
| 44 | + const parsedQuery = parse(content); |
| 45 | + parsedQuery.definitions.forEach((definition) => { |
| 46 | + if (definition.kind === 'OperationDefinition') { |
| 47 | + const operationDefinition = definition as OperationDefinitionNode; |
| 48 | + |
| 49 | + let queryName = operationDefinition.name?.value; |
| 50 | + if (!queryName) { |
| 51 | + // Extract the name from the first field of the selection set |
| 52 | + const firstField = |
| 53 | + operationDefinition.selectionSet.selections[0]; |
| 54 | + if (firstField && firstField.kind === 'Field') { |
| 55 | + queryName = firstField.name.value; |
| 56 | + } |
49 | 57 | } |
50 | | - } |
51 | 58 |
|
52 | | - if (queryName) { |
53 | | - this.queryMap[queryName] = content; |
| 59 | + if (queryName) { |
| 60 | + this.queryMap[queryName] = content; |
| 61 | + } |
54 | 62 | } |
| 63 | + }); |
| 64 | + } catch (error) { |
| 65 | + if (error instanceof GraphQLError) { |
| 66 | + console.error( |
| 67 | + `Error parsing GraphQL file ${file}: ${error.message}` |
| 68 | + ); |
| 69 | + } else { |
| 70 | + console.error(`Unexpected error processing file ${file}: ${error}`); |
55 | 71 | } |
56 | | - }); |
| 72 | + } |
57 | 73 | } |
58 | 74 | }); |
59 | 75 | } |
@@ -90,12 +106,11 @@ export class GraphQLQueryPurifier { |
90 | 106 | const filteredQuery = mergeQueries(req.body.query, allowedQueries); |
91 | 107 |
|
92 | 108 | if (!filteredQuery.trim()) { |
93 | | - // Log the incident for monitoring |
94 | 109 | console.warn( |
95 | 110 | `Query was blocked due to security rules: ${req.body.query}` |
96 | 111 | ); |
97 | | - |
98 | | - return res.status(403).send('The requested query is not allowed.'); |
| 112 | + req.body.query = '{ __typename }'; |
| 113 | + delete req.body.operationName; // Remove the operation name |
99 | 114 | } else { |
100 | 115 | req.body.query = filteredQuery; |
101 | 116 | } |
|
0 commit comments