Skip to content

Commit d54d800

Browse files
authored
feat: Deprecate GraphQL Playground that exposes master key in HTTP response (#10112)
1 parent a6da226 commit d54d800

File tree

8 files changed

+73
-9
lines changed

8 files changed

+73
-9
lines changed

DEPRECATIONS.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ The following is a list of deprecations, according to the [Deprecation Policy](h
1818
| DEPPS12 | Database option `allowPublicExplain` defaults to `false` | [#7519](https://github.com/parse-community/parse-server/issues/7519) | 8.5.0 (2025) | 9.0.0 (2026) | changed | - |
1919
| DEPPS13 | Config option `enableInsecureAuthAdapters` defaults to `false` | [#9667](https://github.com/parse-community/parse-server/pull/9667) | 8.0.0 (2025) | 9.0.0 (2026) | changed | - |
2020
| DEPPS14 | Config option `pages.encodePageParamHeaders` defaults to `true` | [#10063](https://github.com/parse-community/parse-server/issues/10063) | 9.4.0 (2026) | 10.0.0 (2027) | deprecated | - |
21-
| DEPPS15 | Config option `readOnlyMasterKeyIps` defaults to `['127.0.0.1', '::1']` | [#10115](https://github.com/parse-community/parse-server/pull/10115) | 9.5.0 (2026) | 10.0.0 (2027) | deprecated | - |
21+
| DEPPS15 | Config option `readOnlyMasterKeyIps` defaults to `['127.0.0.1', '::1']` | [#10115](https://github.com/parse-community/parse-server/pull/10115) | 9.5.0 (2026) | 10.0.0 (2027) | deprecated | - |
22+
| DEPPS16 | Remove config option `mountPlayground` | [#10110](https://github.com/parse-community/parse-server/issues/10110) | 9.5.0 (2026) | 10.0.0 (2027) | deprecated | - |
23+
| DEPPS17 | Remove config option `playgroundPath` | [#10110](https://github.com/parse-community/parse-server/issues/10110) | 9.5.0 (2026) | 10.0.0 (2027) | deprecated | - |
2224

2325
[i_deprecation]: ## "The version and date of the deprecation."
2426
[i_change]: ## "The version and date of the planned change."

spec/Deprecator.spec.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,50 @@ describe('Deprecator', () => {
103103
})
104104
);
105105
});
106+
107+
it('logs deprecation for removed key when option is set', async () => {
108+
deprecations = [{ optionKey: 'exampleKey', changeNewKey: '', solution: 'Use something else.' }];
109+
110+
spyOn(Deprecator, '_getDeprecations').and.callFake(() => deprecations);
111+
const logger = require('../lib/logger').logger;
112+
const logSpy = spyOn(logger, 'warn').and.callFake(() => {});
113+
114+
await reconfigureServer({ exampleKey: true });
115+
expect(logSpy).toHaveBeenCalledWith(
116+
`DeprecationWarning: The Parse Server option '${deprecations[0].optionKey}' is deprecated and will be removed in a future version. ${deprecations[0].solution}`
117+
);
118+
});
119+
120+
it('does not log deprecation for removed key when option is not set', async () => {
121+
deprecations = [{ optionKey: 'exampleKey', changeNewKey: '', solution: 'Use something else.' }];
122+
123+
spyOn(Deprecator, '_getDeprecations').and.callFake(() => deprecations);
124+
const logSpy = spyOn(Deprecator, '_logOption').and.callFake(() => {});
125+
126+
await reconfigureServer();
127+
expect(logSpy).not.toHaveBeenCalled();
128+
});
129+
130+
it('logs deprecation for mountPlayground when set', async () => {
131+
const logSpy = spyOn(Deprecator, '_logOption').and.callFake(() => {});
132+
133+
await reconfigureServer({ mountPlayground: true, mountGraphQL: true });
134+
expect(logSpy).toHaveBeenCalledWith(
135+
jasmine.objectContaining({
136+
optionKey: 'mountPlayground',
137+
changeNewKey: '',
138+
})
139+
);
140+
});
141+
142+
it('does not log deprecation for mountPlayground when not set', async () => {
143+
const logSpy = spyOn(Deprecator, '_logOption').and.callFake(() => {});
144+
145+
await reconfigureServer();
146+
expect(logSpy).not.toHaveBeenCalledWith(
147+
jasmine.objectContaining({
148+
optionKey: 'mountPlayground',
149+
})
150+
);
151+
});
106152
});

src/Deprecator/Deprecations.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,14 @@ module.exports = [
3131
changeNewDefault: '["127.0.0.1", "::1"]',
3232
solution: "Set 'readOnlyMasterKeyIps' to the IP addresses that should be allowed to use the read-only master key, or to '[\"127.0.0.1\", \"::1\"]' to restrict access to localhost.",
3333
},
34+
{
35+
optionKey: 'mountPlayground',
36+
changeNewKey: '',
37+
solution: "Use Parse Dashboard as GraphQL IDE or configure a third-party GraphQL client such as Apollo Sandbox, GraphiQL, or Insomnia with custom request headers.",
38+
},
39+
{
40+
optionKey: 'playgroundPath',
41+
changeNewKey: '',
42+
solution: "Use Parse Dashboard as GraphQL IDE or configure a third-party GraphQL client such as Apollo Sandbox, GraphiQL, or Insomnia with custom request headers.",
43+
},
3444
];

src/Deprecator/Deprecator.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,17 @@ class Deprecator {
2020
const solution = deprecation.solution;
2121
const optionKey = deprecation.optionKey;
2222
const changeNewDefault = deprecation.changeNewDefault;
23+
const changeNewKey = deprecation.changeNewKey;
2324

2425
// If default will change, only throw a warning if option is not set
2526
if (changeNewDefault != null && Utils.getNestedProperty(options, optionKey) == null) {
2627
Deprecator._logOption({ optionKey, changeNewDefault, solution });
2728
}
29+
30+
// If key will be removed or renamed, only throw a warning if option is set
31+
if (changeNewKey != null && Utils.getNestedProperty(options, optionKey) != null) {
32+
Deprecator._logOption({ optionKey, changeNewKey, solution });
33+
}
2834
}
2935
}
3036

@@ -107,7 +113,7 @@ class Deprecator {
107113

108114
// Compose message
109115
let output = `DeprecationWarning: The Parse Server ${type} '${key}' `;
110-
output += changeNewKey ? `is deprecated and will be ${keyAction} in a future version.` : '';
116+
output += changeNewKey != null ? `is deprecated and will be ${keyAction} in a future version.` : '';
111117
output += changeNewDefault
112118
? `default will change to '${changeNewDefault}' in a future version.`
113119
: '';

src/Options/Definitions.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ module.exports.ParseServerOptions = {
404404
},
405405
mountPlayground: {
406406
env: 'PARSE_SERVER_MOUNT_PLAYGROUND',
407-
help: 'Mounts the GraphQL Playground which exposes the master key in the browser - never use this option in production',
407+
help: 'Deprecated. Mounts the GraphQL Playground which is deprecated and will be removed in a future version. The playground exposes the master key in the browser. Use Parse Dashboard as GraphQL IDE or configure a third-party GraphQL client with custom request headers.',
408408
action: parsers.booleanParser,
409409
default: false,
410410
},
@@ -429,7 +429,7 @@ module.exports.ParseServerOptions = {
429429
},
430430
playgroundPath: {
431431
env: 'PARSE_SERVER_PLAYGROUND_PATH',
432-
help: 'Mount path for the GraphQL Playground, defaults to /playground',
432+
help: 'Deprecated. Mount path for the GraphQL Playground. The playground is deprecated and will be removed in a future version.',
433433
default: '/playground',
434434
},
435435
port: {

src/Options/docs.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Options/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -342,11 +342,11 @@ export interface ParseServerOptions {
342342
:ENV: PARSE_SERVER_GRAPHQL_PUBLIC_INTROSPECTION
343343
:DEFAULT: false */
344344
graphQLPublicIntrospection: ?boolean;
345-
/* Mounts the GraphQL Playground which exposes the master key in the browser - never use this option in production
345+
/* Deprecated. Mounts the GraphQL Playground which is deprecated and will be removed in a future version. The playground exposes the master key in the browser. Use Parse Dashboard as GraphQL IDE or configure a third-party GraphQL client with custom request headers.
346346
:ENV: PARSE_SERVER_MOUNT_PLAYGROUND
347347
:DEFAULT: false */
348348
mountPlayground: ?boolean;
349-
/* Mount path for the GraphQL Playground, defaults to /playground
349+
/* Deprecated. Mount path for the GraphQL Playground. The playground is deprecated and will be removed in a future version.
350350
:ENV: PARSE_SERVER_PLAYGROUND_PATH
351351
:DEFAULT: /playground */
352352
playgroundPath: ?string;

src/ParseServer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,7 @@ class ParseServer {
460460
if (options.mountPlayground) {
461461
parseGraphQLServer.applyPlayground(app);
462462
logging.getLogger().warn(
463-
'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.'
463+
'GraphQL Playground is deprecated and will be removed in a future version. It exposes the master key in the browser. Use Parse Dashboard as GraphQL IDE or configure a third-party GraphQL client with custom request headers.'
464464
);
465465
}
466466
}

0 commit comments

Comments
 (0)