Skip to content

Commit adf261c

Browse files
πŸ› fix(merge.spec.js): fix indentation and line breaks in test cases
✨ feat(merge.spec.js): add test cases for mergeQueries function πŸ› fix(merge.spec.js): fix formatting and remove unnecessary code ✨ feat(merge.spec.js): add test case for handling subqueries πŸ› fix(GraphQLQueryPurifier): handle empty requestQuery and blocked queries ✨ feat(GraphQLQueryPurifier): add support for filtering queries based on allowed paths πŸ“¦ chore(package.json): update version to 1.0.11 βœ… test(merge.spec.ts): update test case for mergeQueries πŸ› fix(index.ts): handle empty allowedQueries and blocked queries
1 parent c4cb6ca commit adf261c

7 files changed

Lines changed: 4454 additions & 4464 deletions

File tree

β€Ždist/__tests__/merge.spec.jsβ€Ž

Lines changed: 99 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -1,143 +1,121 @@
1-
'use strict';
2-
Object.defineProperty(exports, '__esModule', { value: true });
3-
const merge_1 = require('../merge');
1+
"use strict";
2+
Object.defineProperty(exports, "__esModule", { value: true });
3+
const merge_1 = require("../merge");
44
describe('mergeQueries', () => {
5-
test('should allow fields present in allowed queries', () => {
6-
const requestQuery = `{ user { id, name, email } }`;
7-
const allowedQueries = [`{ user { id, name } }`];
8-
const expected = `{
5+
test('should allow fields present in allowed queries', () => {
6+
const requestQuery = `{ user { id, name, email } }`;
7+
const allowedQueries = [`{ user { id, name } }`];
8+
const expected = `{
99
user {
1010
id
1111
name
1212
}
1313
}`;
14-
expect((0, merge_1.mergeQueries)(requestQuery, allowedQueries)).toBe(
15-
expected
16-
);
17-
});
18-
test('should exclude fields not present in allowed queries', () => {
19-
const requestQuery = `{ user { id, name, email } }`;
20-
const allowedQueries = [`{ user { id } }`];
21-
const expected = `{
14+
expect((0, merge_1.mergeQueries)(requestQuery, allowedQueries)).toBe(expected);
15+
});
16+
test('should exclude fields not present in allowed queries', () => {
17+
const requestQuery = `{ user { id, name, email } }`;
18+
const allowedQueries = [`{ user { id } }`];
19+
const expected = `{
2220
user {
2321
id
2422
}
2523
}`;
26-
expect((0, merge_1.mergeQueries)(requestQuery, allowedQueries)).toBe(
27-
expected
28-
);
29-
});
30-
test('should handle nested queries', () => {
31-
const requestQuery = `{ user { id, profile { name, age } } }`;
32-
const allowedQueries = [`{ user { profile { name } } }`];
33-
const expected = `{
24+
expect((0, merge_1.mergeQueries)(requestQuery, allowedQueries)).toBe(expected);
25+
});
26+
test('should handle nested queries', () => {
27+
const requestQuery = `{ user { id, profile { name, age } } }`;
28+
const allowedQueries = [`{ user { profile { name } } }`];
29+
const expected = `{
3430
user {
3531
profile {
3632
name
3733
}
3834
}
3935
}`;
40-
expect((0, merge_1.mergeQueries)(requestQuery, allowedQueries)).toBe(
41-
expected
42-
);
43-
});
44-
test('should handle queries with arguments', () => {
45-
const requestQuery = `{ users(age: 30) { id, name } }`;
46-
const allowedQueries = [`{ users { name } }`];
47-
const expected = `{
36+
expect((0, merge_1.mergeQueries)(requestQuery, allowedQueries)).toBe(expected);
37+
});
38+
test('should handle queries with arguments', () => {
39+
const requestQuery = `{ users(age: 30) { id, name } }`;
40+
const allowedQueries = [`{ users { name } }`];
41+
const expected = `{
4842
users(age: 30) {
4943
name
5044
}
5145
}`;
52-
expect((0, merge_1.mergeQueries)(requestQuery, allowedQueries)).toBe(
53-
expected
54-
);
55-
});
56-
test('should strictly follow allowed queries with aliases', () => {
57-
const requestQuery = `{ user: users { id, name } }`;
58-
const allowedQueries = [`{ user: users { name } }`];
59-
const expected = `{
46+
expect((0, merge_1.mergeQueries)(requestQuery, allowedQueries)).toBe(expected);
47+
});
48+
test('should strictly follow allowed queries with aliases', () => {
49+
const requestQuery = `{ user: users { id, name } }`;
50+
const allowedQueries = [`{ user: users { name } }`];
51+
const expected = `{
6052
user: users {
6153
name
6254
}
6355
}`;
64-
expect((0, merge_1.mergeQueries)(requestQuery, allowedQueries)).toBe(
65-
expected
66-
);
67-
});
68-
test('should strictly follow allowed queries without aliases', () => {
69-
const requestQuery = `{ users { id, name } }`;
70-
const allowedQueries = [`{ users { name } }`];
71-
const expected = `{
56+
expect((0, merge_1.mergeQueries)(requestQuery, allowedQueries)).toBe(expected);
57+
});
58+
test('should strictly follow allowed queries without aliases', () => {
59+
const requestQuery = `{ users { id, name } }`;
60+
const allowedQueries = [`{ users { name } }`];
61+
const expected = `{
7262
users {
7363
name
7464
}
7565
}`;
76-
expect((0, merge_1.mergeQueries)(requestQuery, allowedQueries)).toBe(
77-
expected
78-
);
79-
});
80-
test('should handle mutations', () => {
81-
const requestQuery = `mutation { updateUser(id: 1, data: { name: "New Name" }) { id, name } }`;
82-
const allowedQueries = [
83-
`mutation { updateUser(id: 1, data: { name: "New Name" }) { name } }`,
84-
];
85-
const expected = `mutation {
66+
expect((0, merge_1.mergeQueries)(requestQuery, allowedQueries)).toBe(expected);
67+
});
68+
test('should handle mutations', () => {
69+
const requestQuery = `mutation { updateUser(id: 1, data: { name: "New Name" }) { id, name } }`;
70+
const allowedQueries = [
71+
`mutation { updateUser(id: 1, data: { name: "New Name" }) { name } }`,
72+
];
73+
const expected = `mutation {
8674
updateUser(id: 1, data: {name: "New Name"}) {
8775
name
8876
}
8977
}`;
90-
expect((0, merge_1.mergeQueries)(requestQuery, allowedQueries)).toBe(
91-
expected
92-
);
93-
});
94-
test('should handle multiple allowed queries', () => {
95-
const requestQuery = `{ user { id, name, email } }`;
96-
const allowedQueries = [`{ user { id } }`, `{ user { name } }`];
97-
const expected = `{
78+
expect((0, merge_1.mergeQueries)(requestQuery, allowedQueries)).toBe(expected);
79+
});
80+
test('should handle multiple allowed queries', () => {
81+
const requestQuery = `{ user { id, name, email } }`;
82+
const allowedQueries = [`{ user { id } }`, `{ user { name } }`];
83+
const expected = `{
9884
user {
9985
id
10086
name
10187
}
10288
}`;
103-
expect((0, merge_1.mergeQueries)(requestQuery, allowedQueries)).toBe(
104-
expected
105-
);
106-
});
107-
test('should handle empty requestQuery', () => {
108-
const requestQuery = '';
109-
const allowedQueries = [`{ user { id, name } }`];
110-
const expected = '';
111-
expect((0, merge_1.mergeQueries)(requestQuery, allowedQueries)).toBe(
112-
expected
113-
);
114-
});
115-
test('should handle empty allowedQueries', () => {
116-
const requestQuery = `{ user { id, name, email } }`;
117-
const allowedQueries = [];
118-
const expected = '';
119-
expect((0, merge_1.mergeQueries)(requestQuery, allowedQueries)).toBe(
120-
expected
121-
);
122-
});
123-
test('should handle mutations', () => {
124-
const requestQuery = `mutation { updateUser(id: 1, data: { name: "New Name" }) { id, name } }`;
125-
const allowedQueries = [
126-
`mutation { updateUser(id: 1, data: { name: "New Name" }) { name } }`,
127-
];
128-
const expected = `mutation {
89+
expect((0, merge_1.mergeQueries)(requestQuery, allowedQueries)).toBe(expected);
90+
});
91+
test('should handle empty requestQuery', () => {
92+
const requestQuery = '';
93+
const allowedQueries = [`{ user { id, name } }`];
94+
const expected = '';
95+
expect((0, merge_1.mergeQueries)(requestQuery, allowedQueries)).toBe(expected);
96+
});
97+
test('should handle empty allowedQueries', () => {
98+
const requestQuery = `{ user { id, name, email } }`;
99+
const allowedQueries = [];
100+
const expected = '';
101+
expect((0, merge_1.mergeQueries)(requestQuery, allowedQueries)).toBe(expected);
102+
});
103+
test('should handle mutations', () => {
104+
const requestQuery = `mutation { updateUser(id: 1, data: { name: "New Name" }) { id, name } }`;
105+
const allowedQueries = [
106+
`mutation { updateUser(id: 1, data: { name: "New Name" }) { name } }`,
107+
];
108+
const expected = `mutation {
129109
updateUser(id: 1, data: {name: "New Name"}) {
130110
name
131111
}
132112
}`;
133-
expect((0, merge_1.mergeQueries)(requestQuery, allowedQueries)).toBe(
134-
expected
135-
);
136-
});
137-
test('should handle deeply nested queries', () => {
138-
const requestQuery = `{ user { id, profile { name, address { street, city } } } }`;
139-
const allowedQueries = [`{ user { profile { address { street } } } }`];
140-
const expected = `{
113+
expect((0, merge_1.mergeQueries)(requestQuery, allowedQueries)).toBe(expected);
114+
});
115+
test('should handle deeply nested queries', () => {
116+
const requestQuery = `{ user { id, profile { name, address { street, city } } } }`;
117+
const allowedQueries = [`{ user { profile { address { street } } } }`];
118+
const expected = `{
141119
user {
142120
profile {
143121
address {
@@ -146,36 +124,31 @@ describe('mergeQueries', () => {
146124
}
147125
}
148126
}`;
149-
expect((0, merge_1.mergeQueries)(requestQuery, allowedQueries)).toBe(
150-
expected
151-
);
152-
});
153-
test('should handle nested mutations', () => {
154-
const requestQuery = `mutation { updateUser(id: 1, data: { profile: { name: "New Name" } }) { id } }`;
155-
const allowedQueries = [
156-
`mutation { updateUser(id: 1, data: { profile: { name: "New Name" } }) { id } }`,
157-
];
158-
const expected = `mutation {
127+
expect((0, merge_1.mergeQueries)(requestQuery, allowedQueries)).toBe(expected);
128+
});
129+
test('should handle nested mutations', () => {
130+
const requestQuery = `mutation { updateUser(id: 1, data: { profile: { name: "New Name" } }) { id } }`;
131+
const allowedQueries = [
132+
`mutation { updateUser(id: 1, data: { profile: { name: "New Name" } }) { id } }`,
133+
];
134+
const expected = `mutation {
159135
updateUser(id: 1, data: {profile: {name: "New Name"}}) {
160136
id
161137
}
162138
}`;
163-
expect((0, merge_1.mergeQueries)(requestQuery, allowedQueries)).toBe(
164-
expected
165-
);
166-
});
167-
168-
test('should handle subqueries', () => {
169-
const requestQuery = `query {
139+
expect((0, merge_1.mergeQueries)(requestQuery, allowedQueries)).toBe(expected);
140+
});
141+
test('should handle subqueries', () => {
142+
const requestQuery = `query {
170143
employees {
171144
salary {
172145
amount
173146
}
174147
name
175148
}
176149
}`;
177-
const allowedQueries = [
178-
`query {
150+
const allowedQueries = [
151+
`query {
179152
departments {
180153
id
181154
name
@@ -185,14 +158,13 @@ describe('mergeQueries', () => {
185158
}
186159
}
187160
}`,
188-
];
189-
const expected = `mutation {
190-
updateUser(id: 1, data: {profile: {name: "New Name"}}) {
191-
id
161+
`query {
162+
salaries {
163+
amount
192164
}
193-
}`;
194-
expect((0, merge_1.mergeQueries)(requestQuery, allowedQueries)).toBe(
195-
expected
196-
);
197-
});
165+
}`,
166+
];
167+
const expected = ``;
168+
expect((0, merge_1.mergeQueries)(requestQuery, allowedQueries)).toBe(expected);
169+
});
198170
});

β€Ždist/index.jsβ€Ž

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,22 @@ class GraphQLQueryPurifier {
2828
}
2929
// Get all allowed queries as an array of strings
3030
const allowedQueries = Object.values(this.queryMap);
31-
if (allowedQueries.length === 0) {
31+
if (!allowedQueries.length) {
3232
console.warn('Warning: No GraphQL queries were loaded in GraphQLQueryPurifier. ' +
3333
'Ensure that your .gql files are located in the specified directory: ' +
3434
this.gqlPath);
35-
return res
36-
.status(500)
37-
.send('GraphQL queries are not loaded. Please check server logs for more details.');
3835
}
3936
if (req.body && req.body.query) {
4037
// Use mergeQueries to filter the incoming request query
41-
req.body.query = (0, merge_1.mergeQueries)(req.body.query, allowedQueries);
38+
const filteredQuery = (0, merge_1.mergeQueries)(req.body.query, allowedQueries);
39+
if (!filteredQuery.trim()) {
40+
// Log the incident for monitoring
41+
console.warn(`Query was blocked due to security rules: ${req.body.query}`);
42+
return res.status(403).send('The requested query is not allowed.');
43+
}
44+
else {
45+
req.body.query = filteredQuery;
46+
}
4247
}
4348
next();
4449
};

β€Ždist/merge.jsβ€Ž

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,35 @@
22
Object.defineProperty(exports, "__esModule", { value: true });
33
exports.mergeQueries = void 0;
44
const graphql_1 = require("graphql");
5+
function getPath(node, ancestors) {
6+
// Build the path from the ancestors and the current node
7+
const path = ancestors
8+
.map((ancestor) => (ancestor.kind === 'Field' ? ancestor.name.value : null))
9+
.filter(Boolean);
10+
path.push(node.name.value);
11+
return path.join('.');
12+
}
513
function mergeQueries(requestQuery, allowedQueries) {
614
if (!requestQuery.trim()) {
7-
return ''; // Return an empty string for empty requestQuery
15+
return '';
816
}
917
const parsedRequestQuery = (0, graphql_1.parse)(requestQuery);
1018
const allowedQueryASTs = allowedQueries.map((query) => (0, graphql_1.parse)(query));
11-
const allowedFields = new Set();
12-
// Extract allowed fields from allowedQueryASTs
19+
const allowedPaths = new Set();
20+
// Extract allowed paths from allowedQueryASTs
1321
allowedQueryASTs.forEach((ast) => {
1422
(0, graphql_1.visit)(ast, {
15-
Field(node) {
16-
allowedFields.add(node.name.value);
23+
Field(node, key, parent, path, ancestors) {
24+
allowedPaths.add(getPath(node, ancestors));
1725
},
1826
});
1927
});
20-
// Modify the AST of the request query based on allowed fields
28+
// Modify the AST of the request query based on allowed paths
2129
const modifiedAST = (0, graphql_1.visit)(parsedRequestQuery, {
22-
Field(node) {
23-
if (!allowedFields.has(node.name.value)) {
24-
return null; // This removes the field from the AST
30+
Field(node, key, parent, path, ancestors) {
31+
const currentPath = getPath(node, ancestors);
32+
if (!allowedPaths.has(currentPath)) {
33+
return null; // Remove the field from the AST
2534
}
2635
return undefined; // Continue visiting child nodes
2736
},

β€Žpackage.jsonβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "graphql-query-purifier",
3-
"version": "1.0.9",
3+
"version": "1.0.11",
44
"description": "A small library to match .gql queries vs user input. Removes fields from user requests that are not expected by your frontend code.",
55
"main": "./dist/index.js",
66
"author": "multipliedtwice",

β€Žsrc/__tests__/merge.spec.tsβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ describe('mergeQueries', () => {
171171
}
172172
}`,
173173
`query {
174-
salary {
174+
salaries {
175175
amount
176176
}
177177
}`,

0 commit comments

Comments
Β (0)