Socket
Socket
Sign inDemoInstall

apollo-server-core

Package Overview
Dependencies
Maintainers
1
Versions
314
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

apollo-server-core - npm Package Compare versions

Comparing version 3.6.8 to 3.7.0

1

dist/ApolloServer.d.ts

@@ -19,2 +19,3 @@ import { GraphQLSchema, DocumentNode } from 'graphql';

protected plugins: ApolloServerPlugin[];
protected csrfPreventionRequestHeaders: string[] | null;
private parseOptions;

@@ -21,0 +22,0 @@ private config;

@@ -60,5 +60,9 @@ "use strict";

}
const recommendedCsrfPreventionRequestHeaders = [
'x-apollo-operation-name',
'apollo-require-preflight',
];
class ApolloServerBase {
constructor(config) {
var _a;
var _a, _b;
this.graphqlPath = '/graphql';

@@ -77,3 +81,3 @@ this.requestOptions = Object.create(null);

};
const { context, resolvers, schema, modules, typeDefs, parseOptions = {}, introspection, plugins, gateway, apollo, stopOnTerminationSignals, mocks, mockEntireSchema, documentStore, ...requestOptions } = this.config;
const { context, resolvers, schema, modules, typeDefs, parseOptions = {}, introspection, plugins, gateway, apollo, stopOnTerminationSignals, mocks, mockEntireSchema, documentStore, csrfPrevention, ...requestOptions } = this.config;
if (config.logger) {

@@ -98,2 +102,10 @@ this.logger = config.logger;

this.context = context;
this.csrfPreventionRequestHeaders =
csrfPrevention === true
? recommendedCsrfPreventionRequestHeaders
: csrfPrevention === false
? null
: csrfPrevention === undefined
? null
: (_b = csrfPrevention.requestHeaders) !== null && _b !== void 0 ? _b : recommendedCsrfPreventionRequestHeaders;
const isDev = this.config.nodeEnv !== 'production';

@@ -100,0 +112,0 @@ this.stopOnTerminationSignals =

6

dist/runHttpQuery.d.ts

@@ -1,4 +0,4 @@

import { Request, Headers } from 'apollo-server-env';
import { Headers, Request } from 'apollo-server-env';
import type { GraphQLExecutionResult, ValueOrPromise, WithRequired } from 'apollo-server-types';
import { default as GraphQLOptions } from './graphqlOptions';
import type { WithRequired, GraphQLExecutionResult, ValueOrPromise } from 'apollo-server-types';
export interface HttpQueryRequest {

@@ -30,3 +30,3 @@ method: string;

export declare function throwHttpGraphQLError<E extends Error>(statusCode: number, errors: Array<E>, options?: Pick<GraphQLOptions, 'debug' | 'formatError'>, extensions?: GraphQLExecutionResult['extensions'], headers?: Headers): never;
export declare function runHttpQuery(handlerArguments: Array<any>, request: HttpQueryRequest): Promise<HttpQueryResponse>;
export declare function runHttpQuery(handlerArguments: Array<any>, request: HttpQueryRequest, csrfPreventionRequestHeaders?: string[] | null): Promise<HttpQueryResponse>;
export declare function processHTTPRequest<TContext>(options: WithRequired<GraphQLOptions<TContext>, 'cache' | 'plugins'> & {

@@ -33,0 +33,0 @@ context: TContext;

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var _a;

@@ -6,6 +9,7 @@ Object.defineProperty(exports, "__esModule", { value: true });

const apollo_server_env_1 = require("apollo-server-env");
const apollo_server_errors_1 = require("apollo-server-errors");
const whatwg_mimetype_1 = __importDefault(require("whatwg-mimetype"));
const cachePolicy_1 = require("./cachePolicy");
const graphqlOptions_1 = require("./graphqlOptions");
const apollo_server_errors_1 = require("apollo-server-errors");
const requestPipeline_1 = require("./requestPipeline");
const cachePolicy_1 = require("./cachePolicy");
class HttpQueryError extends Error {

@@ -49,6 +53,36 @@ constructor(statusCode, message, isGraphQLError = false, headers) {

const NODE_ENV = (_a = process.env.NODE_ENV) !== null && _a !== void 0 ? _a : '';
async function runHttpQuery(handlerArguments, request) {
const NON_PREFLIGHTED_CONTENT_TYPES = [
'application/x-www-form-urlencoded',
'multipart/form-data',
'text/plain',
];
function preventCsrf(headers, csrfPreventionRequestHeaders) {
const contentType = headers.get('content-type');
if (contentType !== null) {
const contentTypeParsed = whatwg_mimetype_1.default.parse(contentType);
if (contentTypeParsed === null) {
return;
}
if (!NON_PREFLIGHTED_CONTENT_TYPES.includes(contentTypeParsed.essence)) {
return;
}
}
if (csrfPreventionRequestHeaders.some((header) => {
const value = headers.get(header);
return value !== null && value.length > 0;
})) {
return;
}
throw new HttpQueryError(400, `This operation has been blocked as a potential Cross-Site Request Forgery ` +
`(CSRF). Please either specify a 'content-type' header (with a type that ` +
`is not one of ${NON_PREFLIGHTED_CONTENT_TYPES.join(', ')}) or provide ` +
`a non-empty value for one of the following headers: ${csrfPreventionRequestHeaders.join(', ')}\n`);
}
async function runHttpQuery(handlerArguments, request, csrfPreventionRequestHeaders) {
function debugFromNodeEnv(nodeEnv = NODE_ENV) {
return nodeEnv !== 'production' && nodeEnv !== 'test';
}
if (csrfPreventionRequestHeaders) {
preventCsrf(request.request.headers, csrfPreventionRequestHeaders);
}
let options;

@@ -55,0 +89,0 @@ try {

@@ -52,3 +52,7 @@ import type { GraphQLSchema, DocumentNode } from 'graphql';

documentStore?: DocumentStore | null;
csrfPrevention?: CSRFPreventionOptions | boolean;
}
export interface CSRFPreventionOptions {
requestHeaders?: string[];
}
//# sourceMappingURL=types.d.ts.map
{
"name": "apollo-server-core",
"version": "3.6.8",
"version": "3.7.0",
"description": "Core engine for Apollo GraphQL server",

@@ -48,3 +48,4 @@ "main": "dist/index.js",

"sha.js": "^2.4.11",
"uuid": "^8.0.0"
"uuid": "^8.0.0",
"whatwg-mimetype": "^3.0.0"
},

@@ -54,3 +55,3 @@ "peerDependencies": {

},
"gitHead": "c4a3f346533885df825297853610c1367fa984de"
"gitHead": "58afc1a0e6660aa192b41483048cd75021be5cf7"
}

@@ -129,2 +129,17 @@ import { addMocksToSchema } from '@graphql-tools/mock';

// Our recommended set of CSRF prevention headers. Operations that do not
// provide a content-type such as `application/json` (in practice, this
// means GET operations) must include at least one of these headers.
// Apollo Client Web's default behavior is to always sends a
// `content-type` even for `GET`, and Apollo iOS and Apollo Kotlin always
// send `x-apollo-operation-name`. So if you set
// `csrfPreventionRequestHeaders: true` then any `GET` operation from these
// three client projects and any `POST` operation at all should work
// successfully; if you need `GET`s from another kind of client to work,
// just add `apollo-require-preflight: true` to their requests.
const recommendedCsrfPreventionRequestHeaders = [
'x-apollo-operation-name',
'apollo-require-preflight',
];
export class ApolloServerBase<

@@ -142,2 +157,3 @@ // The type of the argument to the `context` function for this integration.

protected plugins: ApolloServerPlugin[] = [];
protected csrfPreventionRequestHeaders: string[] | null;

@@ -177,2 +193,3 @@ private parseOptions: ParseOptions;

documentStore,
csrfPrevention,
...requestOptions

@@ -213,2 +230,12 @@ } = this.config;

this.csrfPreventionRequestHeaders =
csrfPrevention === true
? recommendedCsrfPreventionRequestHeaders
: csrfPrevention === false
? null
: csrfPrevention === undefined
? null // In AS4, change this to be equivalent to 'true'.
: csrfPrevention.requestHeaders ??
recommendedCsrfPreventionRequestHeaders;
const isDev = this.config.nodeEnv !== 'production';

@@ -215,0 +242,0 @@

@@ -1,2 +0,11 @@

import { Request, Headers } from 'apollo-server-env';
import { Headers, Request } from 'apollo-server-env';
import { ApolloError, formatApolloErrors } from 'apollo-server-errors';
import type { ApolloServerPlugin } from 'apollo-server-plugin-base';
import type {
GraphQLExecutionResult,
ValueOrPromise,
WithRequired,
} from 'apollo-server-types';
import MIMEType from 'whatwg-mimetype';
import { newCachePolicy } from './cachePolicy';
import {

@@ -6,16 +15,8 @@ default as GraphQLOptions,

} from './graphqlOptions';
import { ApolloError, formatApolloErrors } from 'apollo-server-errors';
import {
processGraphQLRequest,
GraphQLRequest,
GraphQLRequestContext,
GraphQLResponse,
processGraphQLRequest,
} from './requestPipeline';
import type { ApolloServerPlugin } from 'apollo-server-plugin-base';
import type {
WithRequired,
GraphQLExecutionResult,
ValueOrPromise,
} from 'apollo-server-types';
import { newCachePolicy } from './cachePolicy';

@@ -120,5 +121,85 @@ export interface HttpQueryRequest {

// See https://fetch.spec.whatwg.org/#cors-safelisted-request-header
const NON_PREFLIGHTED_CONTENT_TYPES = [
'application/x-www-form-urlencoded',
'multipart/form-data',
'text/plain',
];
// We don't want random websites to be able to execute actual GraphQL operations
// from a user's browser unless our CORS policy supports it. It's not good
// enough just to ensure that the browser can't read the response from the
// operation; we also want to prevent CSRF, where the attacker can cause side
// effects with an operation or can measure the timing of a read operation. Our
// goal is to ensure that we don't run the context function or execute the
// GraphQL operation until the browser has evaluated the CORS policy, which
// means we want all operations to be pre-flighted. We can do that by only
// processing operations that have at least one header set that appears to be
// manually set by the JS code rather than by the browser automatically.
//
// POST requests generally have a content-type `application/json`, which is
// sufficient to trigger preflighting. So we take extra care with requests that
// specify no content-type or that specify one of the three non-preflighted
// content types. For those operations, we require (if this feature is enabled)
// one of a set of specific headers to be set. By ensuring that every operation
// either has a custom content-type or sets one of these headers, we know we
// won't execute operations at the request of origins who our CORS policy will
// block.
function preventCsrf(headers: Headers, csrfPreventionRequestHeaders: string[]) {
const contentType = headers.get('content-type');
// We have to worry about CSRF if it looks like this may have been a
// non-preflighted request. If we see a content-type header that is not one of
// the three CORS-safelisted MIME types (see
// https://fetch.spec.whatwg.org/#cors-safelisted-request-header) then we know
// it was preflighted and we don't have to worry.
if (contentType !== null) {
const contentTypeParsed = MIMEType.parse(contentType);
if (contentTypeParsed === null) {
// If we got null, then parsing the content-type failed... which is
// actually *ok* because that would lead to a preflight. (For example, the
// header is empty, or doesn't have a slash, or has bad characters.) The
// scary CSRF case is only if there's *not* an error. So it is actually
// fine for us to just `return` here. (That said, it would also be
// reasonable to reject such requests with provided yet unparsable
// Content-Type here.)
return;
}
if (!NON_PREFLIGHTED_CONTENT_TYPES.includes(contentTypeParsed.essence)) {
// We managed to parse a MIME type that was not one of the
// CORS-safelisted ones. (Probably application/json!) That means that if
// the client is a browser, the browser must have applied CORS
// preflighting and we don't have to worry about CSRF.
return;
}
}
// Either there was no content-type, or the content-type parsed properly as
// one of the three CORS-safelisted values. Let's look for another header that
// (if this was a browser) must have been set by the user's code and would
// have caused a preflight.
if (
csrfPreventionRequestHeaders.some((header) => {
const value = headers.get(header);
return value !== null && value.length > 0;
})
) {
return;
}
throw new HttpQueryError(
400,
`This operation has been blocked as a potential Cross-Site Request Forgery ` +
`(CSRF). Please either specify a 'content-type' header (with a type that ` +
`is not one of ${NON_PREFLIGHTED_CONTENT_TYPES.join(', ')}) or provide ` +
`a non-empty value for one of the following headers: ${csrfPreventionRequestHeaders.join(
', ',
)}\n`,
);
}
export async function runHttpQuery(
handlerArguments: Array<any>,
request: HttpQueryRequest,
csrfPreventionRequestHeaders?: string[] | null,
): Promise<HttpQueryResponse> {

@@ -129,2 +210,8 @@ function debugFromNodeEnv(nodeEnv: string = NODE_ENV) {

// If enabled, check to ensure that this request was preflighted before doing
// anything real (such as running the context function).
if (csrfPreventionRequestHeaders) {
preventCsrf(request.request.headers, csrfPreventionRequestHeaders);
}
let options: GraphQLOptions;

@@ -131,0 +218,0 @@ try {

@@ -112,2 +112,19 @@ import type { GraphQLSchema, DocumentNode } from 'graphql';

documentStore?: DocumentStore | null;
csrfPrevention?: CSRFPreventionOptions | boolean;
}
export interface CSRFPreventionOptions {
// CSRF prevention works by only processing operations from requests whose
// structure indicates that if they were sent by a web browser, then the
// browser would have had to send a preflight OPTIONS request already. We do
// this by specifying some headers that a browser will never automatically set
// and which will trigger the browser to preflight. Apollo Server will reject
// any operation that does not set at least one of these headers *and* does
// not set a content-type (to a header whose parsed type is not
// application/x-www-form-urlencoded, multipart/form-data, or text/plain). If
// CSRF prevention is enabled (eg, with `csrfPrevention: true`) this list
// defaults to ['x-apollo-operation-name', 'apollo-require-preflight']. This
// will allow POST operations from any client and GET operations from Apollo
// Client Web, Apollo iOS, and Apollo Kotlin.
requestHeaders?: string[];
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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