feat: Add security check for server option mountPlayground for GraphQL development (#10103)

This commit is contained in:
Manuel
2026-03-05 23:18:07 +00:00
committed by GitHub
parent 87dc54c0a7
commit 2ae5db1425
7 changed files with 27 additions and 8 deletions
+3 -3
View File
@@ -822,7 +822,7 @@ $ parse-server --appId APPLICATION_ID --masterKey MASTER_KEY --databaseURI mongo
After starting the server, you can visit http://localhost:1337/playground in your browser to start playing with your GraphQL API.
**_Note:_** Do **_NOT_** use --mountPlayground option in production. [Parse Dashboard](https://github.com/parse-community/parse-dashboard) has a built-in GraphQL Playground and it is the recommended option for production apps.
**_Note:_** Do **_NOT_** use --mountPlayground option in production. The GraphQL Playground exposes the master key in the browser page. [Parse Dashboard](https://github.com/parse-community/parse-dashboard) has a built-in GraphQL Playground and is the recommended option for production apps.
### Using Docker
@@ -845,7 +845,7 @@ $ docker run --name my-parse-server --link my-mongo:mongo -v config-vol:/parse-s
After starting the server, you can visit http://localhost:1337/playground in your browser to start playing with your GraphQL API.
**_Note:_** Do **_NOT_** use --mountPlayground option in production. [Parse Dashboard](https://github.com/parse-community/parse-dashboard) has a built-in GraphQL Playground and it is the recommended option for production apps.
**_Note:_** Do **_NOT_** use --mountPlayground option in production. The GraphQL Playground exposes the master key in the browser page. [Parse Dashboard](https://github.com/parse-community/parse-dashboard) has a built-in GraphQL Playground and is the recommended option for production apps.
### Using Express.js
@@ -899,7 +899,7 @@ $ node index.js
After starting the app, you can visit http://localhost:1337/playground in your browser to start playing with your GraphQL API.
**_Note:_** Do **_NOT_** mount the GraphQL Playground in production. [Parse Dashboard](https://github.com/parse-community/parse-dashboard) has a built-in GraphQL Playground and it is the recommended option for production apps.
**_Note:_** Do **_NOT_** mount the GraphQL Playground in production. The GraphQL Playground exposes the master key in the browser page. [Parse Dashboard](https://github.com/parse-community/parse-dashboard) has a built-in GraphQL Playground and is the recommended option for production apps.
## Checking the API health
+6 -2
View File
@@ -34,6 +34,7 @@ describe('Security Check Groups', () => {
config.allowClientClassCreation = false;
config.enableInsecureAuthAdapters = false;
config.graphQLPublicIntrospection = false;
config.mountPlayground = false;
await reconfigureServer(config);
const group = new CheckGroupServerConfig();
@@ -43,6 +44,7 @@ describe('Security Check Groups', () => {
expect(group.checks()[2].checkState()).toBe(CheckState.success);
expect(group.checks()[4].checkState()).toBe(CheckState.success);
expect(group.checks()[5].checkState()).toBe(CheckState.success);
expect(group.checks()[6].checkState()).toBe(CheckState.success);
});
it('checks fail correctly', async () => {
@@ -51,6 +53,7 @@ describe('Security Check Groups', () => {
config.allowClientClassCreation = true;
config.enableInsecureAuthAdapters = true;
config.graphQLPublicIntrospection = true;
config.mountPlayground = true;
await reconfigureServer(config);
const group = new CheckGroupServerConfig();
@@ -60,6 +63,7 @@ describe('Security Check Groups', () => {
expect(group.checks()[2].checkState()).toBe(CheckState.fail);
expect(group.checks()[4].checkState()).toBe(CheckState.fail);
expect(group.checks()[5].checkState()).toBe(CheckState.fail);
expect(group.checks()[6].checkState()).toBe(CheckState.fail);
});
it_only_db('mongo')('checks succeed correctly (MongoDB specific)', async () => {
@@ -69,7 +73,7 @@ describe('Security Check Groups', () => {
const group = new CheckGroupServerConfig();
await group.run();
expect(group.checks()[6].checkState()).toBe(CheckState.success);
expect(group.checks()[7].checkState()).toBe(CheckState.success);
});
it_only_db('mongo')('checks fail correctly (MongoDB specific)', async () => {
@@ -79,7 +83,7 @@ describe('Security Check Groups', () => {
const group = new CheckGroupServerConfig();
await group.run();
expect(group.checks()[6].checkState()).toBe(CheckState.fail);
expect(group.checks()[7].checkState()).toBe(CheckState.fail);
});
});
+1 -1
View File
@@ -404,7 +404,7 @@ module.exports.ParseServerOptions = {
},
mountPlayground: {
env: 'PARSE_SERVER_MOUNT_PLAYGROUND',
help: 'Mounts the GraphQL Playground - never use this option in production',
help: 'Mounts the GraphQL Playground which exposes the master key in the browser - never use this option in production',
action: parsers.booleanParser,
default: false,
},
+1 -1
View File
@@ -77,7 +77,7 @@
* @property {Union} middleware middleware for express server, can be string or function
* @property {Boolean} mountGraphQL Mounts the GraphQL endpoint
* @property {String} mountPath Mount path for the server, defaults to /parse
* @property {Boolean} mountPlayground Mounts the GraphQL Playground - never use this option in production
* @property {Boolean} mountPlayground Mounts the GraphQL Playground which exposes the master key in the browser - never use this option in production
* @property {Number} objectIdSize Sets the number of characters in generated object id's, default 10
* @property {PagesOptions} pages The options for pages such as password reset and email verification.
* @property {PasswordPolicyOptions} passwordPolicy The password policy for enforcing password related rules.
+1 -1
View File
@@ -339,7 +339,7 @@ export interface ParseServerOptions {
:ENV: PARSE_SERVER_GRAPHQL_PUBLIC_INTROSPECTION
:DEFAULT: false */
graphQLPublicIntrospection: ?boolean;
/* Mounts the GraphQL Playground - never use this option in production
/* Mounts the GraphQL Playground which exposes the master key in the browser - never use this option in production
:ENV: PARSE_SERVER_MOUNT_PLAYGROUND
:DEFAULT: false */
mountPlayground: ?boolean;
+3
View File
@@ -458,6 +458,9 @@ class ParseServer {
if (options.mountPlayground) {
parseGraphQLServer.applyPlayground(app);
logging.getLogger().warn(
'GraphQL Playground is enabled and exposes the master key in the browser. The playground is a developer tool and should not be used in production. Use Parse Dashboard for production environments.'
);
}
}
const server = await new Promise(resolve => {
@@ -90,6 +90,18 @@ class CheckGroupServerConfig extends CheckGroup {
}
},
}),
new Check({
title: 'GraphQL Playground disabled',
warning:
'GraphQL Playground is enabled and exposes the master key in the browser page.',
solution:
"Change Parse Server configuration to 'mountPlayground: false'. Use Parse Dashboard for GraphQL exploration in production.",
check: () => {
if (config.mountPlayground) {
throw 1;
}
},
}),
new Check({
title: 'Public database explain disabled',
warning: