Skip to content

Commit 9989861

Browse files
authored
fix: max-depth bypass using fragment cache (#824)
* fix: max-depth bypass using fragment cache * Create bright-seals-relate.md Signed-off-by: M0ngi <[email protected]> * chore: rebase --------- Signed-off-by: M0ngi <[email protected]>
1 parent c99bf89 commit 9989861

File tree

3 files changed

+49
-9
lines changed

3 files changed

+49
-9
lines changed

.changeset/bright-seals-relate.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@escape.tech/graphql-armor-max-depth": patch
3+
---
4+
5+
fix: max-depth bypass using fragment cache

packages/plugins/max-depth/src/index.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -98,20 +98,20 @@ class MaxDepthVisitor {
9898
}
9999
}
100100
} else if (node.kind == Kind.FRAGMENT_SPREAD) {
101+
if (!this.config.flattenFragments) {
102+
parentDepth += 1;
103+
}
104+
101105
if (this.visitedFragments.has(node.name.value)) {
102-
return this.visitedFragments.get(node.name.value) ?? 0;
106+
return parentDepth + (this.visitedFragments.get(node.name.value) ?? 0);
103107
} else {
104108
this.visitedFragments.set(node.name.value, -1);
105109
}
106110
const fragment = this.context.getFragment(node.name.value);
107111
if (fragment) {
108-
let fragmentDepth;
109-
if (this.config.flattenFragments) {
110-
fragmentDepth = this.countDepth(fragment, parentDepth);
111-
} else {
112-
fragmentDepth = this.countDepth(fragment, parentDepth + 1);
113-
}
114-
depth = Math.max(depth, fragmentDepth);
112+
let fragmentDepth = this.countDepth(fragment, 0);
113+
114+
depth = Math.max(depth, parentDepth + fragmentDepth);
115115
if (this.visitedFragments.get(node.name.value) === -1) {
116116
this.visitedFragments.set(node.name.value, fragmentDepth);
117117
}

packages/plugins/max-depth/test/index.spec.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ describe('maxDepthPlugin', () => {
191191
assertSingleExecutionValue(result);
192192
expect(result.errors).toBeDefined();
193193
expect(result.errors?.map((error) => error.message)).toContain(
194-
'Syntax Error: Query depth limit of 3 exceeded, found 4.',
194+
'Syntax Error: Query depth limit of 3 exceeded, found 5.',
195195
);
196196
});
197197

@@ -269,4 +269,39 @@ describe('maxDepthPlugin', () => {
269269
`Syntax Error: Query depth limit of ${maxDepth} exceeded, found ${maxDepth + 2}.`,
270270
]);
271271
});
272+
273+
it('rejects for exceeding max depth by reusing a cached Fragment', async () => {
274+
const bypass_query = `
275+
query {
276+
books {
277+
author {
278+
...Test
279+
}
280+
}
281+
books {
282+
author {
283+
books {
284+
author {
285+
...Test
286+
}
287+
}
288+
}
289+
}
290+
}
291+
fragment Test on Author {
292+
books {
293+
title
294+
}
295+
}
296+
`;
297+
const maxDepth = 6;
298+
const testkit = createTestkit([maxDepthPlugin({ n: maxDepth, exposeLimits: true })], schema);
299+
const result = await testkit.execute(bypass_query);
300+
301+
assertSingleExecutionValue(result);
302+
expect(result.errors).toBeDefined();
303+
expect(result.errors?.map((error) => error.message)).toEqual([
304+
`Syntax Error: Query depth limit of ${maxDepth} exceeded, found ${maxDepth + 2}.`,
305+
]);
306+
});
272307
});

0 commit comments

Comments
 (0)