From bc18622760d745c3f2e8fc5806a1c276a814e924 Mon Sep 17 00:00:00 2001 From: maticzav Date: Mon, 26 Aug 2019 10:08:14 +0200 Subject: [PATCH] fix: Security improvements, typing fixes Fixes potential security hole in no_cache option. --- README.md | 53 ++++-- examples/advanced/src/permissions/rules.ts | 14 +- examples/basic/index.js | 24 ++- .../lib/permissions/rules/is-authenticated.js | 12 +- jest.config.js | 5 + package.json | 16 +- src/constructors.ts | 2 +- src/generator.ts | 17 +- src/rules.ts | 166 +++++++++++------- src/types.ts | 29 ++- src/utils.ts | 19 +- src/validation.ts | 15 +- tests/logic.test.ts | 6 +- tsconfig.json | 3 +- tsconfig.test.json | 15 ++ yarn.lock | 32 ++-- 16 files changed, 267 insertions(+), 161 deletions(-) create mode 100644 tsconfig.test.json diff --git a/README.md b/README.md index 182dd19e6..573b63cf3 100644 --- a/README.md +++ b/README.md @@ -103,17 +103,25 @@ function getUser(req) { // Rules -const isAuthenticated = rule()(async (parent, args, ctx, info) => { - return ctx.user !== null -}) +/* Read more about cache options down in the `rules/cache` section. */ -const isAdmin = rule()(async (parent, args, ctx, info) => { - return ctx.user.role === 'admin' -}) +const isAuthenticated = rule({ cache: 'contextual' })( + async (parent, args, ctx, info) => { + return ctx.user !== null + }, +) -const isEditor = rule()(async (parent, args, ctx, info) => { - return ctx.user.role === 'editor' -}) +const isAdmin = rule({ cache: 'contextual' })( + async (parent, args, ctx, info) => { + return ctx.user.role === 'admin' + }, +) + +const isEditor = rule({ cache: 'contextual' })( + async (parent, args, ctx, info) => { + return ctx.user.role === 'editor' + }, +) // Permissions @@ -274,25 +282,34 @@ const admin = bool => ) ``` -- Cache is enabled by default across all rules. To prevent `cache` generation, set `{ cache: 'no_cache' }` or `{ cache: false }` when generating a rule. -- By default, no rule is executed more than once in complete query execution. This accounts for significantly better load times and quick responses. +- Cache is disabled by default. To enable `cache` generation, set cache option when generating a rule. ##### Cache You can choose from three different cache options. 1. `no_cache` - prevents rules from being cached. -1. `contextual` - use when rule only relies on `ctx` parameter. -1. `strict` - use when rule relies on `parent` or `args` parameter as well. +1. `contextual` - use when rule only relies on `context` parameter (useful for authentication). +1. `strict` - use when rule relies on `parent` or `args` parameter as well (field specific modifications). ```ts // Contextual -const admin = rule({ cache: 'contextual' })(async (parent, args, ctx, info) => { - return ctx.user.isAdmin -}) +const isAdmin = rule({ cache: 'contextual' })( + async (parent, args, ctx, info) => { + return ctx.user.isAdmin + }, +) // Strict -const admin = rule({ cache: 'strict' })(async (parent, args, ctx, info) => { +const canSeeUserSensitiveData = rule({ cache: 'strict' })( + async (parent, args, ctx, info) => { + /* The id of observed User matches the id of authenticated viewer. */ + return ctx.viewer.id === parent.id + }, +) + +// No-cache (defuault) +const admin = rule({ cache: 'no_cache' })(async (parent, args, ctx, info) => { return ctx.user.isAdmin || args.code === 'secret' || parent.id === 'theone' }) ``` @@ -629,7 +646,7 @@ See [#126](https://github.com/maticzav/graphql-shield/issues/126#issuecomment-41 #### A rule is executed only once even though the dataset contains multiple values (and thus should execute the rule multiple times) -This occurs because of caching. When the cache is set to "contextual" only the contextual variable of the rule is expected to be evaluated. Setting the cache to "strict" allows the rule to rely on parent and args parameters as well. +This occurs because of caching. When the cache is set to `contextual` only the contextual variable of the rule is expected to be evaluated. Setting the cache to `strict` allows the rule to rely on parent and args parameters as well, while setting the cache to `no_cache` won't cache result at all. ## Contributors diff --git a/examples/advanced/src/permissions/rules.ts b/examples/advanced/src/permissions/rules.ts index 375a23c8d..8ac9ad54f 100644 --- a/examples/advanced/src/permissions/rules.ts +++ b/examples/advanced/src/permissions/rules.ts @@ -6,14 +6,16 @@ import { Context, getUserEmail } from '../utils' // To see the effect with no cache, set { cache: false } in isCustomer rule. -export const isGrocer = rule()(async (parent, args, ctx: Context, info) => { - // console.log('SHIELD: IsGrocer?') +export const isGrocer = rule({ cache: 'contextual' })( + async (parent, args, ctx: Context, info) => { + // console.log('SHIELD: IsGrocer?') - const email = getUserEmail(ctx) - return ctx.db.exists.Grocer({ email }) -}) + const email = getUserEmail(ctx) + return ctx.db.exists.Grocer({ email }) + }, +) -export const isCustomer = rule({ cache: true })( +export const isCustomer = rule({ cache: 'contextual' })( async (parent, args, ctx: Context, info) => { // console.log('SHIELD: IsCustomer?') diff --git a/examples/basic/index.js b/examples/basic/index.js index d1766f60f..a5a234072 100644 --- a/examples/basic/index.js +++ b/examples/basic/index.js @@ -75,17 +75,23 @@ function getUser(req) { // Rules -const isAuthenticated = rule()(async (parent, args, ctx, info) => { - return ctx.user !== null -}) +const isAuthenticated = rule({ cache: 'contextual' })( + async (parent, args, ctx, info) => { + return ctx.user !== null + }, +) -const isAdmin = rule()(async (parent, args, ctx, info) => { - return ctx.user.role === 'admin' -}) +const isAdmin = rule({ cache: 'contextual' })( + async (parent, args, ctx, info) => { + return ctx.user.role === 'admin' + }, +) -const isEditor = rule()(async (parent, args, ctx, info) => { - return ctx.user.role === 'editor' -}) +const isEditor = rule({ cache: 'contextual' })( + async (parent, args, ctx, info) => { + return ctx.user.role === 'editor' + }, +) // Permissions diff --git a/examples/with-graphql-nexus/src/lib/permissions/rules/is-authenticated.js b/examples/with-graphql-nexus/src/lib/permissions/rules/is-authenticated.js index 51611c23b..c59041b19 100644 --- a/examples/with-graphql-nexus/src/lib/permissions/rules/is-authenticated.js +++ b/examples/with-graphql-nexus/src/lib/permissions/rules/is-authenticated.js @@ -1,9 +1,9 @@ -const {rule} = require('graphql-shield'); +const { rule } = require('graphql-shield') -const isAuthenticated = rule()((parent, args, ctx) => { - return Boolean(ctx.request.userId); -}); +const isAuthenticated = rule({ cache: 'contextual' })((parent, args, ctx) => { + return Boolean(ctx.request.userId) +}) module.exports = { - isAuthenticated -}; + isAuthenticated, +} diff --git a/jest.config.js b/jest.config.js index 7f3d43350..d6ba633ba 100644 --- a/jest.config.js +++ b/jest.config.js @@ -17,4 +17,9 @@ module.exports = { verbose: true, coverageDirectory: './coverage', coverageReporters: ['json', 'lcov', 'text', 'clover', 'html'], + globals: { + 'ts-jest': { + tsConfig: 'tsconfig.test.json', + }, + }, } diff --git a/package.json b/package.json index cbec05715..d02515f81 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "author": "Matic Zavadlal ", "scripts": { "compile": "tsc -d", - "coverage": "yarn codecov", + "coverage": "codecov", "clean": "rimraf dist", "docz:dev": "docz dev", "docz:build": "docz build", @@ -15,10 +15,10 @@ "postinstall": "lightercollective postinstall", "posttest": "npm-run-all lint", "prepublishOnly": "npm-run-all compile", - "prerelease": "npm-run-all test", - "pretest": "npm-run-all clean compile", "release": "semantic-release", - "test": "NODE_ENV=test jest" + "test": "npm-run-all --parallel test:*", + "test:jest": "NODE_ENV=test jest", + "test:types": "tsc --noEmit" }, "dependencies": { "@types/yup": "0.26.23", @@ -38,9 +38,9 @@ "docz": "0.13.7", "docz-theme-default": "0.13.7", "graphql": "14.3.1", - "graphql-middleware": "3.0.2", + "graphql-middleware": "4.0.1", "graphql-tools": "4.0.5", - "graphql-yoga": "1.18.1", + "graphql-yoga": "1.18.3", "husky": "3.0.4", "jest": "24.9.0", "npm-run-all": "4.1.5", @@ -48,7 +48,7 @@ "prettier-check": "2.0.0", "pretty-quick": "1.11.1", "remark-emoji": "2.0.2", - "rimraf": "2.7.1", + "rimraf": "3.0.0", "semantic-release": "15.13.24", "ts-jest": "24.0.2", "ts-node": "8.3.0", @@ -59,7 +59,7 @@ }, "peerDependencies": { "graphql": "^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0", - "graphql-middleware": "^2.0.0 || ^3.0.0" + "graphql-middleware": "^2.0.0 || ^3.0.0 || ^4.0.0" }, "resolutions": { "graphql-yoga/graphql": "14.3.1" diff --git a/src/constructors.ts b/src/constructors.ts index 79bd0dd38..92d13cba5 100644 --- a/src/constructors.ts +++ b/src/constructors.ts @@ -70,7 +70,7 @@ export const inputRule = ( schema?: (yup: typeof Yup) => Yup.Schema, ) => { if (typeof name === 'string') { - return new InputRule(name, schema(Yup)) + return new InputRule(name, schema!(Yup)) } else { return new InputRule(Math.random().toString(), name(Yup)) } diff --git a/src/generator.ts b/src/generator.ts index a512c2f43..df02835cd 100644 --- a/src/generator.ts +++ b/src/generator.ts @@ -25,6 +25,7 @@ import { withDefault, } from './utils' import { ValidationError } from './validation' +import { IMiddlewareWithOptions } from 'graphql-middleware/dist/types' /** * @@ -37,9 +38,14 @@ import { ValidationError } from './validation' function generateFieldMiddlewareFromRule( rule: ShieldRule, options: IOptions, -): IMiddlewareFunction { +): IMiddlewareFunction { async function middleware( - resolve: (parent, args, ctx, info) => any, + resolve: ( + parent: object, + args: object, + ctx: IShieldContext, + info: GraphQLResolveInfo, + ) => Promise, parent: { [key: string]: any }, args: { [key: string]: any }, ctx: IShieldContext, @@ -53,7 +59,6 @@ function generateFieldMiddlewareFromRule( if (!ctx._shield) { ctx._shield = { cache: {}, - hashFunction: options.hashFunction, } } @@ -83,17 +88,17 @@ function generateFieldMiddlewareFromRule( return { fragment: rule.extractFragment(), resolve: middleware, - } + } as IMiddlewareWithOptions } if (isLogicRule(rule)) { return { fragments: rule.extractFragments(), resolve: middleware, - } + } as IMiddlewareWithOptions } - return middleware + return middleware as IMiddlewareFunction } /** diff --git a/src/rules.ts b/src/rules.ts index 342fb6fa0..ce0e60924 100644 --- a/src/rules.ts +++ b/src/rules.ts @@ -14,15 +14,20 @@ import { IShieldContext, } from './types' import { isLogicRule } from './utils' +import { GraphQLResolveInfo } from 'graphql' export class Rule implements IRule { readonly name: string private cache: ICache - private fragment: IFragment + private fragment?: IFragment private func: IRuleFunction - constructor(name: string, func, constructorOptions: IRuleConstructorOptions) { + constructor( + name: string, + func: IRuleFunction, + constructorOptions: IRuleConstructorOptions, + ) { const options = this.normalizeOptions(constructorOptions) this.name = name @@ -42,22 +47,15 @@ export class Rule implements IRule { * */ async resolve( - parent, - args, + parent: object, + args: object, ctx: IShieldContext, - info, + info: GraphQLResolveInfo, options: IOptions, ): Promise { try { - // Cache - const cacheKey = this.generateCacheKey(parent, args, ctx, info) - - if (!ctx._shield.cache[cacheKey]) { - ctx._shield.cache[cacheKey] = this.func(parent, args, ctx, info) - } - - // Resolve - const res = await ctx._shield.cache[cacheKey] + /* Resolve */ + const res = await this.executeRule(parent, args, ctx, info, options) if (res instanceof Error) { return res @@ -94,7 +92,7 @@ export class Rule implements IRule { * Extracts fragment from the rule. * */ - extractFragment(): IFragment { + extractFragment(): IFragment | undefined { return this.fragment } @@ -110,7 +108,7 @@ export class Rule implements IRule { cache: options.cache !== undefined ? this.normalizeCacheOption(options.cache) - : 'strict', + : 'no_cache', fragment: options.fragment !== undefined ? options.fragment : undefined, } } @@ -137,39 +135,80 @@ export class Rule implements IRule { } /** + * Executes a rule and writes to cache if needed. * * @param parent * @param args * @param ctx * @param info - * - * Generates cache key based on cache option. - * */ - private generateCacheKey(parent, args, ctx: IShieldContext, info): string { - if (typeof this.cache === 'function') { - return `${this.name}-${this.cache(parent, args, ctx, info)}` + private executeRule( + parent: object, + args: object, + ctx: IShieldContext, + info: GraphQLResolveInfo, + options: IOptions, + ): string | boolean | Error | Promise { + switch (typeof this.cache) { + case 'function': { + /* User defined cache function. */ + const key = `${this.name}-${this.cache(parent, args, ctx, info)}` + return this.writeToCache(key)(parent, args, ctx, info) + } + case 'string': { + /* Standard cache option. */ + switch (this.cache) { + case 'strict': { + const key = options.hashFunction({ parent, args }) + + return this.writeToCache(`${this.name}-${key}`)( + parent, + args, + ctx, + info, + ) + } + case 'contextual': { + return this.writeToCache(this.name)(parent, args, ctx, info) + } + case 'no_cache': { + return this.func(parent, args, ctx, info) + } + } + } + /* istanbul ignore next */ + default: { + throw new Error(`Unsupported cache format: ${typeof this.cache}`) + } } + } - switch (this.cache) { - case 'strict': { - const key = ctx._shield.hashFunction({ parent, args }) + /** + * Writes or reads result from cache. + * + * @param key + */ - return `${this.name}-${key}` - } - case 'contextual': { - return this.name - } - case 'no_cache': { - return `${this.name}-${Math.random()}` + private writeToCache( + key: string, + ): ( + parent: object, + args: object, + ctx: IShieldContext, + info: GraphQLResolveInfo, + ) => string | boolean | Error | Promise { + return (parent, args, ctx, info) => { + if (!ctx._shield.cache[key]) { + return (ctx._shield.cache[key] = this.func(parent, args, ctx, info)) } + return ctx._shield.cache[key] } } } export class InputRule extends Rule { constructor(name: string, schema: Yup.Schema) { - const validationFunction = (parent, args) => + const validationFunction = (parent: object, args: object) => schema .validate(args) .then(() => true) @@ -197,10 +236,10 @@ export class LogicRule implements ILogicRule { * */ async resolve( - parent, - args, - ctx, - info, + parent: object, + args: object, + ctx: IShieldContext, + info: GraphQLResolveInfo, options: IOptions, ): Promise { return false @@ -217,10 +256,10 @@ export class LogicRule implements ILogicRule { * */ async evaluate( - parent, - args, - ctx, - info, + parent: object, + args: object, + ctx: IShieldContext, + info: GraphQLResolveInfo, options: IOptions, ): Promise { const rules = this.getRules() @@ -246,9 +285,8 @@ export class LogicRule implements ILogicRule { return fragments.concat(...rule.extractFragments()) } - if (rule.extractFragment()) { - return fragments.concat(rule.extractFragment()) - } + const fragment = rule.extractFragment() + if (fragment) return fragments.concat(fragment) return fragments }, []) @@ -275,10 +313,10 @@ export class RuleOr extends LogicRule { * */ async resolve( - parent, - args, - ctx, - info, + parent: object, + args: object, + ctx: IShieldContext, + info: GraphQLResolveInfo, options: IOptions, ): Promise { const result = await this.evaluate(parent, args, ctx, info, options) @@ -308,10 +346,10 @@ export class RuleAnd extends LogicRule { * */ async resolve( - parent, - args, - ctx, - info, + parent: object, + args: object, + ctx: IShieldContext, + info: GraphQLResolveInfo, options: IOptions, ): Promise { const result = await this.evaluate(parent, args, ctx, info, options) @@ -341,10 +379,10 @@ export class RuleChain extends LogicRule { * */ async resolve( - parent, - args, - ctx, - info, + parent: object, + args: object, + ctx: IShieldContext, + info: GraphQLResolveInfo, options: IOptions, ): Promise { const result = await this.evaluate(parent, args, ctx, info, options) @@ -368,10 +406,10 @@ export class RuleChain extends LogicRule { * */ async evaluate( - parent, - args, - ctx, - info, + parent: object, + args: object, + ctx: IShieldContext, + info: GraphQLResolveInfo, options: IOptions, ): Promise { const rules = this.getRules() @@ -409,10 +447,10 @@ export class RuleNot extends LogicRule { * */ async resolve( - parent, - args, - ctx, - info, + parent: object, + args: object, + ctx: IShieldContext, + info: GraphQLResolveInfo, options: IOptions, ): Promise { const [res] = await this.evaluate(parent, args, ctx, info, options) diff --git a/src/types.ts b/src/types.ts index 3d7a143d2..0e58d1438 100644 --- a/src/types.ts +++ b/src/types.ts @@ -11,13 +11,19 @@ export declare class IRule { constructor(options: IRuleOptions) equals(rule: IRule): boolean - extractFragment(): IFragment - resolve(parent, args, ctx, info, options: IOptions): Promise + extractFragment(): IFragment | undefined + resolve( + parent: object, + args: object, + ctx: IShieldContext, + info: GraphQLResolveInfo, + options: IOptions, + ): Promise } export interface IRuleOptions { cache: ICache - fragment: IFragment + fragment?: IFragment } export declare class ILogicRule { @@ -25,8 +31,20 @@ export declare class ILogicRule { getRules(): ShieldRule[] extractFragments(): IFragment[] - evaluate(parent, args, ctx, info, options: IOptions): Promise - resolve(parent, args, ctx, info, options: IOptions): Promise + evaluate( + parent: object, + args: object, + ctx: IShieldContext, + info: GraphQLResolveInfo, + options: IOptions, + ): Promise + resolve( + parent: object, + args: object, + ctx: IShieldContext, + info: GraphQLResolveInfo, + options: IOptions, + ): Promise } export type IFragment = string @@ -94,6 +112,5 @@ export declare function shield( export interface IShieldContext { _shield: { cache: { [key: string]: IRuleResult | Promise } - hashFunction: IHashFunction } } diff --git a/src/utils.ts b/src/utils.ts index 0023c5018..09f41c07c 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -57,15 +57,16 @@ export function isRuleFieldMap(x: any): x is IRuleFieldMap { * evaluates to true from particular function. * */ -export function flattenObjectOf( - obj: object, - func: (x: any) => boolean, -): edge[] { - const values = Object.keys(obj).reduce((acc, key) => { - if (func(obj[key])) { - return [...acc, obj[key]] - } else if (typeof obj[key] === 'object' && !func(obj[key])) { - return [...acc, ...flattenObjectOf(obj[key], func)] +export function flattenObjectOf( + obj: { [key: string]: any }, + f: (x: any) => boolean, +): T[] { + const values = Object.keys(obj).reduce((acc, key) => { + const val = obj[key] + if (f(val)) { + return [...acc, val] + } else if (typeof val === 'object' && !f(val)) { + return [...acc, ...flattenObjectOf(val, f)] } else { return acc } diff --git a/src/validation.ts b/src/validation.ts index 7eb18578b..a53fce020 100644 --- a/src/validation.ts +++ b/src/validation.ts @@ -1,5 +1,4 @@ -import { Rule } from './rules' -import { IRules, ShieldRule, ILogicRule } from './types' +import { IRules, ShieldRule, ILogicRule, IRule } from './types' import { isRuleFunction, flattenObjectOf, isLogicRule } from './utils' /** @@ -16,7 +15,7 @@ export function validateRuleTree( ): { status: 'ok' } | { status: 'err'; message: string } { const rules = extractRules(ruleTree) - const valid = rules.reduce<{ map: Map; duplicates: string[] }>( + const valid = rules.reduce<{ map: Map; duplicates: string[] }>( ({ map, duplicates }, rule) => { if (!map.has(rule.name)) { return { map: map.set(rule.name, rule), duplicates } @@ -32,7 +31,7 @@ export function validateRuleTree( return { map, duplicates } } }, - { map: new Map(), duplicates: [] }, + { map: new Map(), duplicates: [] }, ) if (valid.duplicates.length === 0) { @@ -53,10 +52,10 @@ export function validateRuleTree( * Extracts rules from rule tree. * */ - function extractRules(ruleTree: IRules): Rule[] { + function extractRules(ruleTree: IRules): IRule[] { const resolvers = flattenObjectOf(ruleTree, isRuleFunction) - const rules = resolvers.reduce((rules, rule) => { + const rules = resolvers.reduce((rules, rule) => { if (isLogicRule(rule)) { return [...rules, ...extractLogicRules(rule)] } else { @@ -73,8 +72,8 @@ export function validateRuleTree( * * @param rule */ - function extractLogicRules(rule: ILogicRule): Rule[] { - return rule.getRules().reduce((acc, shieldRule) => { + function extractLogicRules(rule: ILogicRule): IRule[] { + return rule.getRules().reduce((acc, shieldRule) => { if (isLogicRule(shieldRule)) { return [...acc, ...extractLogicRules(shieldRule)] } else { diff --git a/tests/logic.test.ts b/tests/logic.test.ts index e22f93f9d..cf919e6b3 100644 --- a/tests/logic.test.ts +++ b/tests/logic.test.ts @@ -1,4 +1,4 @@ -import { graphql } from 'graphql' +import { graphql, GraphQLResolveInfo } from 'graphql' import { applyMiddleware } from 'graphql-middleware' import { makeExecutableSchema } from 'graphql-tools' import { shield, rule, allow, deny, and, or, not } from '../src' @@ -303,8 +303,8 @@ describe('internal execution', () => { const res = await rule.resolve( {}, {}, - {}, - {}, + { _shield: { cache: {} } }, + {} as GraphQLResolveInfo, { allowExternalErrors: false, debug: false, diff --git a/tsconfig.json b/tsconfig.json index 1b77af778..52792bbb2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,8 @@ "outDir": "dist", "sourceMap": true, "lib": ["dom", "es2017", "esnext.asynciterable"], - "skipLibCheck": true + "skipLibCheck": true, + "strict": true }, "include": ["./src/**/*.ts"], "exclude": ["node_modules", "examples", "dist"] diff --git a/tsconfig.test.json b/tsconfig.test.json new file mode 100644 index 000000000..3247254c3 --- /dev/null +++ b/tsconfig.test.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "es2017", + "module": "commonjs", + "moduleResolution": "node", + "noUnusedLocals": true, + "rootDir": "src", + "outDir": "dist", + "sourceMap": true, + "lib": ["dom", "es2017", "esnext.asynciterable"], + "skipLibCheck": true + }, + "include": ["./tests/**/*.ts"], + "exclude": ["node_modules", "examples", "dist"] +} diff --git a/yarn.lock b/yarn.lock index b4dfb7f44..3f8a88e5d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6057,12 +6057,12 @@ graphql-import@^0.7.0: lodash "^4.17.4" resolve-from "^4.0.0" -graphql-middleware@3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/graphql-middleware/-/graphql-middleware-3.0.2.tgz#c8cdb67615eec02aec237b455e679f5fc973ddc4" - integrity sha512-sRqu1sF+77z42z1OVM1QDHKQWnWY5K3nAgqWiZwx3U4tqNZprrDuXxSChPMliV343IrVkpYdejUYq9w24Ot3FA== +graphql-middleware@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/graphql-middleware/-/graphql-middleware-4.0.1.tgz#8c627b22cc046a47e9474a813cf9e0bd50fa0c4b" + integrity sha512-r9r+pcHV4yZW7LAOcjQYTbNY6nR9SrLgpVZKbrtgXxpQW/MUc1N8q3PESciebvp5s0EEUgRchcRjUkyaArCIFw== dependencies: - graphql-tools "^4.0.4" + graphql-tools "^4.0.5" graphql-playground-html@1.6.12: version "1.6.12" @@ -6102,7 +6102,7 @@ graphql-tag@^2.9.2: resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.10.1.tgz#10aa41f1cd8fae5373eaf11f1f67260a3cad5e02" integrity sha512-jApXqWBzNXQ8jYa/HLkZJaVw9jgwNqZkywa2zfFn16Iv1Zb7ELNHkJaXHR7Quvd5SIGsy6Ny7SUKATgnu05uEg== -graphql-tools@4.0.5: +graphql-tools@4.0.5, graphql-tools@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/graphql-tools/-/graphql-tools-4.0.5.tgz#d2b41ee0a330bfef833e5cdae7e1f0b0d86b1754" integrity sha512-kQCh3IZsMqquDx7zfIGWBau42xe46gmqabwYkpPlCLIjcEY1XK+auP7iGRD9/205BPyoQdY8hT96MPpgERdC9Q== @@ -6113,7 +6113,7 @@ graphql-tools@4.0.5: iterall "^1.1.3" uuid "^3.1.0" -graphql-tools@^4.0.0, graphql-tools@^4.0.4: +graphql-tools@^4.0.0: version "4.0.4" resolved "https://registry.yarnpkg.com/graphql-tools/-/graphql-tools-4.0.4.tgz#ca08a63454221fdde825fe45fbd315eb2a6d566b" integrity sha512-chF12etTIGVVGy3fCTJ1ivJX2KB7OSG4c6UOJQuqOHCmBQwTyNgCDuejZKvpYxNZiEx7bwIjrodDgDe9RIkjlw== @@ -6134,10 +6134,10 @@ graphql-upload@^8.0.0, graphql-upload@^8.0.2: http-errors "^1.7.2" object-path "^0.11.4" -graphql-yoga@1.18.1: - version "1.18.1" - resolved "https://registry.yarnpkg.com/graphql-yoga/-/graphql-yoga-1.18.1.tgz#c9c5e303a4bc9a3fba98fb265b95a8a233eeb8a7" - integrity sha512-xNjOTMaE1ybFjB+t65Q/YISSD8sqBl5ny1bsXYBuNcEWNS5/sdUq1R9dm7BbgCD9yUiRYg3q6QiKAuLxdAhfuw== +graphql-yoga@1.18.3: + version "1.18.3" + resolved "https://registry.yarnpkg.com/graphql-yoga/-/graphql-yoga-1.18.3.tgz#047fa511dbef63cf6d6ea7c06a71202d37444844" + integrity sha512-tR6JYbwLSBVu0Z8M7BIyt1PHhhexmRwneYM8Ru/g2pixrtsWbelBFAXU7bDPhXrqZ49Zxt2zLJ60x3bLNGo/bQ== dependencies: "@types/aws-lambda" "8.10.13" "@types/cors" "^2.8.4" @@ -6154,7 +6154,7 @@ graphql-yoga@1.18.1: graphql "^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0" graphql-deduplicator "^2.0.1" graphql-import "^0.7.0" - graphql-middleware "3.0.2" + graphql-middleware "4.0.1" graphql-playground-middleware-express "1.7.11" graphql-playground-middleware-lambda "1.7.12" graphql-subscriptions "^0.5.8" @@ -11243,10 +11243,10 @@ rimraf@2, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2. dependencies: glob "^7.1.3" -rimraf@2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== +rimraf@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.0.tgz#614176d4b3010b75e5c390eb0ee96f6dc0cebb9b" + integrity sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg== dependencies: glob "^7.1.3"