Skip to content

Commit

Permalink
Warn on unconfigured cache (#6545)
Browse files Browse the repository at this point in the history
Issue a warning in production mode if neither the cache nor the APQ cache
(persistedQueries.cache) are configured.

We've provided a simple path to using a bounded cache via: #6536

The current default for AS3 is an unbounded in memory cache, which is
susceptible to a DOS attack since APQs can fill up the server's memory with no
limit. This warning provides an actionable recommendation to update their
configuration in order to prevent this.
  • Loading branch information
trevor-scheer committed Jun 15, 2022
1 parent 999adf5 commit ac8f9bf
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -14,6 +14,7 @@ The version headers in this history reflect the versions of Apollo Server itself
- Remove dependency on `keyv`/`@apollo/utils.keyvadapter` in favor of a simple `Map`-backed cache which implements TTL [PR #6535](https://github.com/apollographql/apollo-server/pull/6535)
- Add `cache: "bounded"` configuration option, allowing users to opt into bounded request cache (recommended) [PR #6536](https://github.com/apollographql/apollo-server/pull/6536)
- Remove `apollo-server-caching` and `apollo-server-cache-*` packages [PR #6541](https://github.com/apollographql/apollo-server/pull/6541)
- Warn when APQ cache is unbounded in production (which is default) [PR #6545](https://github.com/apollographql/apollo-server/pull/6545)

## v3.8.2

Expand Down
14 changes: 14 additions & 0 deletions packages/apollo-server-core/src/ApolloServer.ts
Expand Up @@ -267,6 +267,20 @@ export class ApolloServerBase<

if (!requestOptions.cache) {
requestOptions.cache = new UnboundedCache();

if (
!isDev &&
(requestOptions.persistedQueries === undefined ||
(requestOptions.persistedQueries &&
!requestOptions.persistedQueries.cache))
) {
this.logger.warn(
'Persisted queries are enabled and are using an unbounded cache. Your server' +
' is vulnerable to denial of service attacks via memory exhaustion. ' +
'Set `cache: "bounded"` or `persistedQueries: false` in your ApolloServer ' +
'constructor, or see FIXME:DOCS for other alternatives.',
);
}
}

if (requestOptions.persistedQueries !== false) {
Expand Down
Expand Up @@ -327,6 +327,7 @@ describe('apollo-server-express', () => {
},
},
nodeEnv: 'production',
cache: 'bounded',
});

const apolloFetch = createApolloFetch({ uri });
Expand Down Expand Up @@ -356,6 +357,7 @@ describe('apollo-server-express', () => {
},
},
nodeEnv: 'production',
cache: 'bounded',
});

const apolloFetch = createApolloFetch({ uri });
Expand Down
Expand Up @@ -346,6 +346,7 @@ describe('apollo-server-fastify', () => {
},
},
nodeEnv: 'production',
cache: 'bounded',
});

const apolloFetch = createApolloFetch({ uri });
Expand Down Expand Up @@ -375,6 +376,7 @@ describe('apollo-server-fastify', () => {
},
},
nodeEnv: 'production',
cache: 'bounded',
});

const apolloFetch = createApolloFetch({ uri });
Expand Down
Expand Up @@ -350,6 +350,7 @@ describe('non-integration tests', () => {
},
},
nodeEnv: 'production',
cache: 'bounded',
});

const apolloFetch = createApolloFetch({ uri });
Expand Down Expand Up @@ -379,6 +380,7 @@ describe('non-integration tests', () => {
},
},
nodeEnv: 'production',
cache: 'bounded',
});

const apolloFetch = createApolloFetch({ uri });
Expand Down
69 changes: 69 additions & 0 deletions packages/apollo-server-integration-testsuite/src/ApolloServer.ts
Expand Up @@ -268,6 +268,7 @@ export function testApolloServer<AS extends ApolloServerBase>(
schema,
stopOnTerminationSignals: false,
nodeEnv: 'production',
cache: 'bounded',
});

const apolloFetch = createApolloFetch({ uri });
Expand All @@ -287,6 +288,7 @@ export function testApolloServer<AS extends ApolloServerBase>(
introspection: true,
stopOnTerminationSignals: false,
nodeEnv: 'production',
cache: 'bounded',
});

const apolloFetch = createApolloFetch({ uri });
Expand Down Expand Up @@ -1730,6 +1732,7 @@ export function testApolloServer<AS extends ApolloServerBase>(
},
stopOnTerminationSignals: false,
nodeEnv: 'production',
cache: 'bounded',
});

const apolloFetch = createApolloFetch({ uri });
Expand Down Expand Up @@ -1760,6 +1763,7 @@ export function testApolloServer<AS extends ApolloServerBase>(
},
stopOnTerminationSignals: false,
nodeEnv: 'production',
cache: 'bounded',
});

const apolloFetch = createApolloFetch({ uri });
Expand Down Expand Up @@ -2296,6 +2300,71 @@ export function testApolloServer<AS extends ApolloServerBase>(
expect(server['requestOptions'].cache).toBe(customCache);
});

it("warns in production mode when cache isn't configured and APQ isn't disabled", () => {
const mockLogger = {
warn: jest.fn(),
debug: jest.fn(),
error: jest.fn(),
info: jest.fn(),
};

new ApolloServerBase({
typeDefs: `type Query { hello: String }`,
nodeEnv: 'production',
logger: mockLogger,
});

expect(mockLogger.warn).toHaveBeenCalledWith(
expect.stringMatching(
/Persisted queries are enabled and are using an unbounded cache/,
),
);
});

it("doesn't warn about cache configuration if: not production mode, cache configured, APQ disabled, or APQ cache configured", () => {
const mockLogger = {
warn: jest.fn(),
debug: jest.fn(),
error: jest.fn(),
info: jest.fn(),
};

// dev mode
new ApolloServerBase({
typeDefs: `type Query { hello: String }`,
nodeEnv: 'development',
logger: mockLogger,
});

// cache configured
new ApolloServerBase({
typeDefs: `type Query { hello: String }`,
nodeEnv: 'production',
logger: mockLogger,
cache: 'bounded',
});

// APQ disabled
new ApolloServerBase({
typeDefs: `type Query { hello: String }`,
nodeEnv: 'development',
logger: mockLogger,
persistedQueries: false,
});

// APQ cache configured
new ApolloServerBase({
typeDefs: `type Query { hello: String }`,
nodeEnv: 'development',
logger: mockLogger,
persistedQueries: {
cache: {} as KeyValueCache,
},
});

expect(mockLogger.warn).not.toHaveBeenCalled();
});

it('basic caching', async () => {
const typeDefs = gql`
type Query {
Expand Down
2 changes: 2 additions & 0 deletions packages/apollo-server-koa/src/__tests__/ApolloServer.test.ts
Expand Up @@ -316,6 +316,7 @@ describe('apollo-server-koa', () => {
},
},
nodeEnv: 'production',
cache: 'bounded',
});

const apolloFetch = createApolloFetch({ uri });
Expand Down Expand Up @@ -345,6 +346,7 @@ describe('apollo-server-koa', () => {
},
},
nodeEnv: 'production',
cache: 'bounded',
});

const apolloFetch = createApolloFetch({ uri });
Expand Down

0 comments on commit ac8f9bf

Please sign in to comment.