Simple and customizable security middleware for GraphQL servers in Deno.

Overview


guardenoql-logo

GuarDenoQL

Simple and customizable security middleware for GraphQL servers in Deno

Features

  • Integrates with an Opine server in a Deno runtime.
  • Enables users to customize both a maximum depth and a cost limit for all GraphQL queries and mutations sent to the server.
  • Validates queries and mutations against the depth limiter and/or cost limiter before they are executed by the server.

Why?

Depth Limiting

Because GraphQL schemas can be cyclic graphs, it is possible that a client could construct a query such as this one:

Therefore, if nested deep enough, a malicious actor could potentially bring your server down with an abusive query.

However, using a Depth Limiter, you can validate the depth of incoming queries against a user-defined limit and prevent these queries from going through.

Cost Limiting

Queries can still be very expensive even if they aren't nested deeply. Using a Cost Limiter, your server will calculate the total cost of the query based on its types before execution.

Getting Started

A set up with gql and Opine out-of-the-box:

import { opine, OpineRequest } from "https://deno.land/x/[email protected]/mod.ts";
import { GraphQLHTTP } from "https://deno.land/x/[email protected]/mod.ts";
import { makeExecutableSchema } from "https://deno.land/x/[email protected]/mod.ts";
import { gql } from "https://deno.land/x/[email protected]/mod.ts";
import { readAll } from "https://deno.land/[email protected]/streams/conversion.ts";

import { guarDenoQL } from "https://deno.land/x/[email protected]/mod.ts";
// update GuarDenoQL import URL with most recent version 

type Request = OpineRequest & { json: () => Promise<any> };

const typeDefs = gql`
  type Query {
    hello: String
  }
`;

const resolvers = { Query: { hello: () => `Hello World!` } };
const dec = new TextDecoder();
const schema = makeExecutableSchema({ resolvers, typeDefs });
const app = opine();

app
  .use("/graphql", async (req, res) => {
    const request = req as Request;

    request.json = async () => {
      const rawBody = await readAll(req.raw);
      const body = JSON.parse(dec.decode(rawBody));
      const query = body.query;

      const error = guarDenoQL(schema, query, {
        depthLimitOptions: {
          maxDepth: 4, // maximum depth allowed before a request is rejected
          callback: (args) => console.log("query depth is:", args), // optional
        },
        costLimitOptions: {
          maxCost: 5000, // maximum cost allowed before a request is rejected
          mutationCost: 5, // cost of a mutation
          objectCost: 2, // cost of retrieving an object
          scalarCost: 1, // cost of retrieving a scalar
          depthCostFactor: 1.5, // multiplicative cost of each depth level
          callback: (args) => console.log("query cost is:", args), // optional
        },
      });

      if (error !== undefined && !error.length) {
        return body;
      } else {
        const errorMessage = { error };
        return res.send(JSON.stringify(errorMessage));
      }
    };

    const resp = await GraphQLHTTP<Request>({
      schema,
      context: (request) => ({ request }),
      graphiql: true,
    })(request);

    for (const [k, v] of resp.headers.entries()) res.headers?.append(k, v);
    res.status = resp.status;
    res.send(await resp.text());
  })
  .listen(3000, () => console.log(`☁  Started on http://localhost:3000`));

GuarDenoQL is fully customizable.

Users can use either the depth limiter, cost limiter or both.

The first argument is the schema, the second argument is the query, and the third argument is an Object with up to two properties: depthLimitOptions and/or costLimitOptions.

Depth Limit Configuration

This feature limits the depth of a document.

const error = guarDenoQL(schema, query, {
  depthLimitOptions: {
    maxDepth: 4, // maximum depth allowed before a request is rejected
    callback: (args) => console.log("query depth is:", args), // optional
  },
});

The depthLimitOptions object has two properties to configure:

  1. maxDepth: the depth limiter will throw a validation error if the document has a greater depth than the user-supplied maxDepth

  2. optional callback function: receives an Object that maps the name of the operation to its corresponding query depth

Cost Limit Configuration

This feature applies a cost analysis algorithm to block queries that are too expensive.

const error = guarDenoQL(schema, query, {
  costLimitOptions: {
    maxCost: 5000, // maximum cost allowed before a request is rejected
    mutationCost: 5, // cost of a mutation
    objectCost: 2, // cost of retrieving an object
    scalarCost: 1, // cost of retrieving a scalar
    depthCostFactor: 1.5, // multiplicative cost of each depth level
    callback: (args) => console.log("query cost is:", args), // optional
  },
});

The costLimitOptions object has six properties to configure:

  1. maxCost: the cost limiter will throw a validation error if the document has a greater cost than the user-supplied maxCost

  2. mutationCost: represents the cost of a mutation (some popular cost analysis algorithms make mutations more expensive than queries)

  3. objectCost: represents the cost of an object that has subfields

  4. scalarCost: represents the cost of a scalar

  5. depthCostFactor: the multiplicative cost of each depth level

  6. optional callback function: receives an Object that maps the name of the operation to its corresponding query cost

Functionality

Depth Limiter

Cost Limiter

How to Contribute

If you would like to contribute, please see CONTRIBUTING.md for more information.

Authors

Finley Decker: GitHub | LinkedIn

Hannah McDowell: GitHub | LinkedIn

Jane You: GitHub | LinkedIn

Lucien Hsu: GitHub | LinkedIn

License

Distributed under the MIT License. See LICENSE for more information.

You might also like...

A small, but powerful HTTP library for Deno & Deno Deploy, built for convenience and simplicity

A small, but powerful HTTP library for Deno & Deno Deploy, built for convenience and simplicity

Wren Wren is a small, but powerful HTTP library for Deno & Deno Deploy, built for convenience and simplicity. convenient aliases for HTTP responses au

Dec 12, 2022

TypeSafe MongoDB Atlas Data API SDK for Deno & Deno Deploy

Atlas SDK atlas_sdk is a TypeSafe MongoDB Atlas Data API SDK for Deno & Deno Deploy Links Docs Import Replace LATEST_VERSION with current latest versi

Dec 26, 2022

Deno bindings for yoga, using Deno FFI.

deno_yoga Deno bindings for yoga, using Deno FFI. Usage flags: --allow-ffi: Requires ffi access to "yogacore.dll", "libyogacore.so", "libyogacore.dyli

Feb 11, 2022

deno-ja (Deno Japanese community) showcase

Showcase Deno本家よりも気軽に作ったものを公開できるようなShowcaseです。 スクリーンショットの撮影方法 短めのidを決めていただいて、下記のようにスクリプトを実行してください。 deno task screenshot [url] [id] ※エラーが出る場合は、下記を実行してみ

Oct 28, 2022

A command-line tool to manage Deno scripts installed via deno install

🏞️ nublar nublar is a command-line tool to manage your scripts installed via deno install. 🛳️ Installation deno install --allow-read --allow-write -

Dec 26, 2022

GraphErr is an open-source error handling library for GraphQL implementations in Deno. It's a lightweight solution that provides developers with descriptive error messages, reducing ambiguity and improving debugging.

GraphErr is an open-source error handling library for GraphQL implementations in Deno. It's a lightweight solution that provides developers with descriptive error messages, reducing ambiguity and improving debugging.

GraphErr Descriptive GraphQL error handling for Deno/Oak servers. Features Provides additional context to GraphQL's native error messaging for faster

Nov 1, 2022

NextJS with GraphQL using type-graphl and graphql-codegen adoptable dogs example code

This is a Next.js project bootstrapped with create-next-app. Getting Started First, run the development server: npm run dev # or yarn dev Open http://

Mar 31, 2022

Ping.js is a small and simple Javascript library for the browser to "ping" response times to web servers in Javascript

Ping.js Ping.js is a small and simple Javascript library for the browser to "ping" response times to web servers in Javascript! This is useful for whe

Dec 27, 2022

GraphQL-first boilerplate that scales with TypeScript + Node Express + Apollo GraphQL APIs.

graphql-typescript-boilerplate A boilerplate project for quickly building Graphql APIs and with typescript 🚀 Installation Install the dependencies: y

May 15, 2022
Comments
Releases(v1.0.2)
next-graphql-server is a library for building production-grade GraphQL servers using Next.js with API Routes

next-graphql-server next-graphql-server is an easy to use Next.js library for creating performant GraphQL endpoints on top of Next.js API Routes. Star

Jakub Neander 82 Nov 21, 2022
LunaSec - Open Source Security Software built by Security Engineers. Scan your dependencies for Log4Shell, or add Data Tokenization to prevent data leaks. Try our live Tokenizer demo: https://app.lunasec.dev

Our Software We're a team of Security Engineers on a mission to make awesome Open Source Application Security tooling. It all lives in this repo. Here

LunaSec 1.2k Jan 7, 2023
Opinionated collection of TypeScript definitions and utilities for Deno and Deno Deploy. With complete types for Deno/NPM/TS config files, constructed from official JSON schemas.

Schemas Note: You can also import any type from the default module, ./mod.ts deno.json import { type DenoJson } from "https://deno.land/x/[email protected]

deno911 2 Oct 12, 2022
Grupprojekt för kurserna 'Javascript med Ramverk' och 'Agil Utveckling'

JavaScript-med-Ramverk-Laboration-3 Grupprojektet för kurserna Javascript med Ramverk och Agil Utveckling. Utvecklingsguide För information om hur utv

Svante Jonsson IT-Högskolan 3 May 18, 2022
Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Kurs-repo för kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023
Refresh - Simple browser reload on file change middleware for your Deno web applications.

refresh Simple browser reload on file change middleware for your Deno web applications. Usage To use refresh middleware, just add a few extra lines to

Craig Morten 13 Dec 19, 2022
A fast and optimized middleware server with an absurdly small amount of code (300 lines) built on top of Deno's native HTTP APIs

A fast and optimized middleware server with an absurdly small amount of code (300 lines) built on top of Deno's native HTTP APIs with no dependencies. It also has a collection of useful middlewares: log file, serve static, CORS, session, rate limit, token, body parsers, redirect, proxy and handle upload. In "README" there are examples of all the resources. Faster's ideology is: all you need is an optimized middleware manager, all other functionality is middleware.

Henrique Emanoel Viana 19 Dec 28, 2022
This is a simple boilerplate for a Deno website, deployed with Deno Deploy.

Simple Deno Website Boilerplate This is a simple website boilerplate built using Deno and deployed using Deno Deploy. Demo at simple-deno-website-boil

Bruno Bernardino 15 Dec 3, 2022
🛣️ A tiny and fast http request router designed for use with deno and deno deploy

Rutt Rutt is a tiny http router designed for use with deno and deno deploy. It is written in about 200 lines of code and is pretty fast, using an exte

Denosaurs 26 Dec 10, 2022