From f7b8e3ee989db488575595693233cabaed7f46ed Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Thu, 5 Mar 2026 22:24:09 +0000 Subject: [PATCH 1/2] feat --- README.md | 6 +++--- spec/SecurityCheckGroups.spec.js | 8 ++++++-- src/Options/Definitions.js | 2 +- src/Options/docs.js | 2 +- src/Options/index.js | 2 +- src/ParseServer.ts | 3 +++ src/Security/CheckGroups/CheckGroupServerConfig.js | 12 ++++++++++++ 7 files changed, 27 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 09c1ca6148..a204ae72c4 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/spec/SecurityCheckGroups.spec.js b/spec/SecurityCheckGroups.spec.js index 8031733322..4f4d5fa6d9 100644 --- a/spec/SecurityCheckGroups.spec.js +++ b/spec/SecurityCheckGroups.spec.js @@ -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); }); }); diff --git a/src/Options/Definitions.js b/src/Options/Definitions.js index 323cd19a9b..7b28fc5c26 100644 --- a/src/Options/Definitions.js +++ b/src/Options/Definitions.js @@ -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, }, diff --git a/src/Options/docs.js b/src/Options/docs.js index 13aaa00f17..8d0ad9445c 100644 --- a/src/Options/docs.js +++ b/src/Options/docs.js @@ -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. diff --git a/src/Options/index.js b/src/Options/index.js index 1539abd283..83277a76d6 100644 --- a/src/Options/index.js +++ b/src/Options/index.js @@ -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; diff --git a/src/ParseServer.ts b/src/ParseServer.ts index 8b1c6cdf8a..33079616ce 100644 --- a/src/ParseServer.ts +++ b/src/ParseServer.ts @@ -458,6 +458,9 @@ class ParseServer { if (options.mountPlayground) { parseGraphQLServer.applyPlayground(app); + console.warn( + `\nWARNING, 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.\n` + ); } } const server = await new Promise(resolve => { diff --git a/src/Security/CheckGroups/CheckGroupServerConfig.js b/src/Security/CheckGroups/CheckGroupServerConfig.js index ab2dfc4507..0d1bc8c92b 100644 --- a/src/Security/CheckGroups/CheckGroupServerConfig.js +++ b/src/Security/CheckGroups/CheckGroupServerConfig.js @@ -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: From fc9b359492d1c2ab7855e9bf6d203f9295937a0f Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Thu, 5 Mar 2026 22:41:14 +0000 Subject: [PATCH 2/2] fix log --- src/ParseServer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ParseServer.ts b/src/ParseServer.ts index 33079616ce..2e37c4728c 100644 --- a/src/ParseServer.ts +++ b/src/ParseServer.ts @@ -458,8 +458,8 @@ class ParseServer { if (options.mountPlayground) { parseGraphQLServer.applyPlayground(app); - console.warn( - `\nWARNING, 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.\n` + 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.' ); } }