UNPKG

@escape.tech/graphql-armor

Version:

Dead-simple, yet highly customizable security middleware for Apollo GraphQL servers shield

379 lines (354 loc) 12.1 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var graphqlArmorBlockFieldSuggestions = require('@escape.tech/graphql-armor-block-field-suggestions'); var graphqlArmorCostLimit = require('@escape.tech/graphql-armor-cost-limit'); var graphql = require('graphql'); var graphqlArmorMaxAliases = require('@escape.tech/graphql-armor-max-aliases'); var graphqlArmorMaxDepth = require('@escape.tech/graphql-armor-max-depth'); var graphqlArmorMaxDirectives = require('@escape.tech/graphql-armor-max-directives'); var graphqlArmorMaxTokens = require('@escape.tech/graphql-armor-max-tokens'); function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } class ApolloProtection { constructor(config) { _defineProperty(this, "config", void 0); _defineProperty(this, "enabledByDefault", true); this.config = config; } } const plugin$1 = ({ mask }) => { const _mask = mask !== null && mask !== void 0 ? mask : graphqlArmorBlockFieldSuggestions.blockFieldSuggestionsDefaultOptions.mask; return { async requestDidStart() { return { async didEncounterErrors({ errors }) { for (const error of errors) { error.message = error.message.replace(/Did you mean ".+"/g, _mask); } } }; } }; }; class ApolloBlockFieldSuggestionProtection extends ApolloProtection { get isEnabled() { var _this$config$blockFie; if (!this.config.blockFieldSuggestion) { return this.enabledByDefault; } return (_this$config$blockFie = this.config.blockFieldSuggestion.enabled) !== null && _this$config$blockFie !== void 0 ? _this$config$blockFie : this.enabledByDefault; } protect() { var _this$config$blockFie2; return { plugins: [plugin$1((_this$config$blockFie2 = this.config.blockFieldSuggestion) !== null && _this$config$blockFie2 !== void 0 ? _this$config$blockFie2 : graphqlArmorBlockFieldSuggestions.blockFieldSuggestionsDefaultOptions)] }; } } const reportToContext = (ctx, error) => { if (ctx) { ctx.reportError(new graphql.GraphQLError(error.message, { extensions: { code: 'BAD_USER_INPUT' } })); } }; /* * We want to use by default the context handler, because it allows us to * return a 400 error code, instead of 500 for the apollo server. * * Default `rejection` handler will be used only if throw is explicitly set to true. * If set to false, nothing will happen. */ const inferApolloPropagator = config => { if (config === undefined) { config = {}; } if (config.onReject === undefined) { config.onReject = []; } if (config.propagateOnRejection === true || config.propagateOnRejection === undefined) { config.propagateOnRejection = false; config.onReject.push(reportToContext); } return config; }; class ApolloCostLimitProtection extends ApolloProtection { get isEnabled() { var _this$config$costLimi; if (!this.config.costLimit) { return this.enabledByDefault; } return (_this$config$costLimi = this.config.costLimit.enabled) !== null && _this$config$costLimi !== void 0 ? _this$config$costLimi : this.enabledByDefault; } protect() { this.config.costLimit = inferApolloPropagator(this.config.costLimit); return { validationRules: [graphqlArmorCostLimit.costLimitRule(this.config.costLimit)] }; } } class ApolloMaxAliasesProtection extends ApolloProtection { get isEnabled() { var _this$config$maxAlias; if (!this.config.maxAliases) { return this.enabledByDefault; } return (_this$config$maxAlias = this.config.maxAliases.enabled) !== null && _this$config$maxAlias !== void 0 ? _this$config$maxAlias : this.enabledByDefault; } protect() { this.config.maxAliases = inferApolloPropagator(this.config.maxAliases); return { validationRules: [graphqlArmorMaxAliases.maxAliasesRule(this.config.maxAliases)] }; } } class ApolloMaxDepthProtection extends ApolloProtection { get isEnabled() { var _this$config$maxDepth; if (!this.config.maxDepth) { return this.enabledByDefault; } return (_this$config$maxDepth = this.config.maxDepth.enabled) !== null && _this$config$maxDepth !== void 0 ? _this$config$maxDepth : this.enabledByDefault; } protect() { this.config.maxDepth = inferApolloPropagator(this.config.maxDepth); return { validationRules: [graphqlArmorMaxDepth.maxDepthRule(this.config.maxDepth)] }; } } class ApolloMaxDirectivesProtection extends ApolloProtection { get isEnabled() { var _this$config$maxDirec; if (!this.config.maxDirectives) { return this.enabledByDefault; } return (_this$config$maxDirec = this.config.maxDirectives.enabled) !== null && _this$config$maxDirec !== void 0 ? _this$config$maxDirec : this.enabledByDefault; } protect() { this.config.maxDirectives = inferApolloPropagator(this.config.maxDirectives); return { validationRules: [graphqlArmorMaxDirectives.maxDirectivesRule(this.config.maxDirectives)] }; } } const plugin = options => { return { async unexpectedErrorProcessingRequest(err) { throw new graphql.GraphQLError(err.error, { extensions: { code: 'GRAPHQL_VALIDATION_FAILED' } }); }, async requestDidStart() { return { async parsingDidStart(requestContext) { const source = requestContext.source; if (source !== undefined) { const parser = new graphqlArmorMaxTokens.MaxTokensParserWLexer(source, options); parser.parseDocument(); } } }; } }; }; class ApolloMaxTokensProtection extends ApolloProtection { get isEnabled() { var _this$config$maxToken; if (!this.config.maxTokens) { return this.enabledByDefault; } return (_this$config$maxToken = this.config.maxTokens.enabled) !== null && _this$config$maxToken !== void 0 ? _this$config$maxToken : this.enabledByDefault; } protect() { var _this$config$maxToken2; return { plugins: [plugin((_this$config$maxToken2 = this.config.maxTokens) !== null && _this$config$maxToken2 !== void 0 ? _this$config$maxToken2 : graphqlArmorMaxTokens.maxTokenDefaultOptions)] }; } } class ApolloArmor { constructor(config = {}) { _defineProperty(this, "protections", void 0); this.protections = [new ApolloBlockFieldSuggestionProtection(config), new ApolloMaxTokensProtection(config), new ApolloCostLimitProtection(config), new ApolloMaxAliasesProtection(config), new ApolloMaxDirectivesProtection(config), new ApolloMaxDepthProtection(config)]; } protect() { let plugins = []; let validationRules = []; for (const protection of this.protections) { if (protection.isEnabled) { const { plugins: newPlugins, validationRules: newValidationRules } = protection.protect(); plugins = [...plugins, ...(newPlugins || [])]; validationRules = [...validationRules, ...(newValidationRules || [])]; } } return { plugins, validationRules, allowBatchedHttpRequests: false, includeStacktraceInErrorResponses: false }; } } class EnvelopProtection { constructor(config) { _defineProperty(this, "config", void 0); _defineProperty(this, "enabledByDefault", true); this.config = config; } } class EnvelopBlockFieldSuggestionProtection extends EnvelopProtection { get isEnabled() { var _this$config$blockFie; if (!this.config.blockFieldSuggestion) { return this.enabledByDefault; } return (_this$config$blockFie = this.config.blockFieldSuggestion.enabled) !== null && _this$config$blockFie !== void 0 ? _this$config$blockFie : this.enabledByDefault; } protect() { return { plugins: [graphqlArmorBlockFieldSuggestions.blockFieldSuggestionsPlugin(this.config.blockFieldSuggestion)] }; } } class EnvelopCostLimitProtection extends EnvelopProtection { get isEnabled() { var _this$config$costLimi; if (!this.config.costLimit) { return this.enabledByDefault; } return (_this$config$costLimi = this.config.costLimit.enabled) !== null && _this$config$costLimi !== void 0 ? _this$config$costLimi : this.enabledByDefault; } protect() { return { plugins: [graphqlArmorCostLimit.costLimitPlugin(this.config.costLimit)] }; } } class EnvelopMaxAliasesProtection extends EnvelopProtection { get isEnabled() { var _this$config$maxAlias; if (!this.config.maxAliases) { return this.enabledByDefault; } return (_this$config$maxAlias = this.config.maxAliases.enabled) !== null && _this$config$maxAlias !== void 0 ? _this$config$maxAlias : this.enabledByDefault; } protect() { return { plugins: [graphqlArmorMaxAliases.maxAliasesPlugin(this.config.maxAliases)] }; } } class EnvelopMaxDepthProtection extends EnvelopProtection { get isEnabled() { var _this$config$maxDepth; if (!this.config.maxDepth) { return this.enabledByDefault; } return (_this$config$maxDepth = this.config.maxDepth.enabled) !== null && _this$config$maxDepth !== void 0 ? _this$config$maxDepth : this.enabledByDefault; } protect() { return { plugins: [graphqlArmorMaxDepth.maxDepthPlugin(this.config.maxDepth)] }; } } class EnvelopMaxDirectivesProtection extends EnvelopProtection { get isEnabled() { var _this$config$maxDirec; if (!this.config.maxDirectives) { return this.enabledByDefault; } return (_this$config$maxDirec = this.config.maxDirectives.enabled) !== null && _this$config$maxDirec !== void 0 ? _this$config$maxDirec : this.enabledByDefault; } protect() { return { plugins: [graphqlArmorMaxDirectives.maxDirectivesPlugin(this.config.maxDirectives)] }; } } class EnvelopMaxTokensProtection extends EnvelopProtection { get isEnabled() { var _this$config$maxToken; if (!this.config.maxTokens) { return this.enabledByDefault; } return (_this$config$maxToken = this.config.maxTokens.enabled) !== null && _this$config$maxToken !== void 0 ? _this$config$maxToken : this.enabledByDefault; } protect() { return { plugins: [graphqlArmorMaxTokens.maxTokensPlugin(this.config.maxTokens)] }; } } const EnvelopArmorPlugin = config => { const armor = new EnvelopArmor(config); const enhancements = armor.protect(); return { onPluginInit({ addPlugin }) { for (const plugin of enhancements.plugins) { addPlugin(plugin); } } }; }; class EnvelopArmor { constructor(config = {}) { _defineProperty(this, "protections", void 0); this.protections = [new EnvelopBlockFieldSuggestionProtection(config), new EnvelopMaxTokensProtection(config), new EnvelopMaxDirectivesProtection(config), new EnvelopMaxAliasesProtection(config), new EnvelopCostLimitProtection(config), new EnvelopMaxDepthProtection(config)]; } protect() { const plugins = []; for (const protection of this.protections) { if (protection.isEnabled) { const enhancements = protection.protect(); plugins.push(...enhancements.plugins); } } return { plugins }; } } exports.ApolloArmor = ApolloArmor; exports.EnvelopArmor = EnvelopArmor; exports.EnvelopArmorPlugin = EnvelopArmorPlugin;