Socket
Socket
Sign inDemoInstall

graphql-depth-limit

Package Overview
Dependencies
Maintainers
1
Versions
2
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

graphql-depth-limit - npm Package Compare versions

Comparing version 1.0.0 to 1.1.0

62

index.js

@@ -5,4 +5,13 @@ const {

} = require('graphql')
const arrify = require('arrify')
module.exports = (maxDepth, callback) => validationContext => {
/**
* Creates a validator for the GraphQL query depth
* @param {Number} maxDepth - The maximum allowed depth for any operation in a GraphQL document.
* @param {Object} [options]
* @param {String|RegExp|Function} options.ignore - Stops recursive depth checking based on a field name. Either a string or regexp to match the name, or a function that reaturns a boolean.
* @param {Function} [callback] - Called each time validation runs. Receives an Object which is a map of the depths for each operation.
* @returns {Function} The validator function for GraphQL validation phase.
*/
const depthLimit = (maxDepth, options = {}, callback = () => {}) => validationContext => {
try {

@@ -14,5 +23,5 @@ const { definitions } = validationContext.getDocument()

for (let name in queries) {
queryDepths[name] = determineDepth(queries[name], fragments, 0, maxDepth, validationContext, name)
queryDepths[name] = determineDepth(queries[name], fragments, 0, maxDepth, validationContext, name, options)
}
callback && callback(queryDepths)
callback(queryDepths)
return validationContext

@@ -25,2 +34,4 @@ } catch (err) {

module.exports = depthLimit
function getFragments(definitions) {

@@ -45,19 +56,28 @@ return definitions.reduce((map, definition) => {

function determineDepth(node, fragments, depthSoFar, maxDepth, context, operationName) {
function determineDepth(node, fragments, depthSoFar, maxDepth, context, operationName, options) {
if (depthSoFar > maxDepth) {
return context.reportError(new GraphQLError(`'${operationName}' exceeds maximum operation depth of ${maxDepth}`, [ node ]))
return context.reportError(
new GraphQLError(`'${operationName}' exceeds maximum operation depth of ${maxDepth}`, [ node ])
)
}
switch (node.kind) {
case Kind.FIELD:
if (!node.selectionSet) {
// by default, ignore the introspection fields which begin with double underscores
const shouldIgnore = /^__/.test(node.name.value) || seeIfIgnored(node, options.ignore)
if (shouldIgnore || !node.selectionSet) {
return 0
} else {
return 1 + Math.max(...node.selectionSet.selections.map(selection => determineDepth(selection, fragments, depthSoFar + 1, maxDepth, context, operationName)))
}
return 1 + Math.max(...node.selectionSet.selections.map(selection =>
determineDepth(selection, fragments, depthSoFar + 1, maxDepth, context, operationName, options)
))
case Kind.FRAGMENT_SPREAD:
return determineDepth(fragments[node.name.value], fragments, depthSoFar, maxDepth, context, operationName)
return determineDepth(fragments[node.name.value], fragments, depthSoFar, maxDepth, context, operationName, options)
case Kind.INLINE_FRAGMENT:
case Kind.FRAGMENT_DEFINITION:
case Kind.OPERATION_DEFINITION:
return Math.max(...node.selectionSet.selections.map(selection => determineDepth(selection, fragments, depthSoFar, maxDepth, context, operationName)))
return Math.max(...node.selectionSet.selections.map(selection =>
determineDepth(selection, fragments, depthSoFar, maxDepth, context, operationName, options)
))
default:

@@ -68,1 +88,23 @@ throw new Error('uh oh! depth crawler cannot handle: ' + node.kind)

function seeIfIgnored(node, ignore) {
for (let rule of arrify(ignore)) {
const fieldName = node.name.value
switch (rule.constructor) {
case Function:
if (rule(fieldName)) {
return true
}
break
case String:
case RegExp:
if (fieldName.match(rule)) {
return true
}
break
default:
throw new Error(`Invalid ignore option: ${rule}`)
}
}
return false
}
{
"name": "graphql-depth-limit",
"version": "1.0.0",
"version": "1.1.0",
"description": "Limit the complexity of your GraphQL queries based on depth.",

@@ -10,4 +10,12 @@ "engines": {

"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"docs": "sed -n '/Documentation/q;p' README.md > README.bak.md; echo '## Documentation' >> README.bak.md; jsdoc2md --heading-depth 3 index.js >> README.bak.md && mv -f README.bak.md README.md",
"lint": "eslint index.js",
"test": "ava test.js"
},
"ava": {
"verbose": true
},
"files": [
"index.js"
],
"repository": {

@@ -32,3 +40,13 @@ "type": "git",

"graphql": "*"
},
"devDependencies": {
"ava": "^0.21.0",
"eslint": "^4.4.1",
"eslint-config-airbnb-base": "^11.3.1",
"graphql": "^0.10.5",
"jsdoc-to-markdown": "^3.0.0"
},
"dependencies": {
"arrify": "^1.0.1"
}
}

@@ -48,3 +48,11 @@ GraphQL Depth Limit

album {
# and so on...
songs {
album {
songs {
album {
# and so on...
}
}
}
}
}

@@ -175,9 +183,18 @@ }

schema,
validationRules: [ depthLimit(10, depths => console.log(depths)) ]
validationRules: [ depthLimit(10) ]
})))
```
The first argument is the total depth limit. This will throw a validation error for queries (or mutations) with a depth of 11 or more.
The second argument is a callback which receives an `Object` which is a map of the depths for each operation.
The first argument is the total depth limit. This will throw a validation error for queries (or mutations) with a depth of 11 or more.<br/>
The second argument is an options object, where you can do things like specify ignored fields. Introspection fields are ignored by default.<br/>
The third argument is a callback which receives an `Object` which is a map of the depths for each operation.<br/>
```js
depthLimit(
10,
{ ignore: [ /_trusted$/, 'idontcare' ] },
depths => console.log(depths)
)
```
Now the evil query from before will tell the client this:

@@ -201,1 +218,21 @@

## Future Work
- [ ] Type-specific sub-depth limits, e.g. you can only descend 3 levels from an `Album` type, 5 levels from the `User` type, etc.
- [ ] More customization options, like custom errors.
## Documentation
<a name="depthLimit"></a>
### depthLimit(maxDepth, [options], [callback]) ⇒ <code>function</code>
Creates a validator for the GraphQL query depth
**Kind**: global function
**Returns**: <code>function</code> - The validator function for GraphQL validation phase.
| Param | Type | Description |
| --- | --- | --- |
| maxDepth | <code>Number</code> | The maximum allowed depth for any operation in a GraphQL document. |
| [options] | <code>Object</code> | |
| options.ignore | <code>String</code> \| <code>RegExp</code> \| <code>function</code> | Stops recursive depth checking based on a field name. Either a string or regexp to match the name, or a function that reaturns a boolean. |
| [callback] | <code>function</code> | Called each time validation runs. Receives an Object which is a map of the depths for each operation. |
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc