Skip to content
30 changes: 25 additions & 5 deletions spec/AudienceRouter.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,55 +263,75 @@ describe('AudiencesRouter', () => {
});

it('should only create with master key', done => {
const logger = require('../lib/logger').default;
const loggerErrorSpy = spyOn(logger, 'error').and.callThrough();
loggerErrorSpy.calls.reset();
Parse._request('POST', 'push_audiences', {
name: 'My Audience',
query: JSON.stringify({ deviceType: 'ios' }),
}).then(
() => {},
error => {
expect(error.message).toEqual('unauthorized: master key is required');
expect(error.message).toEqual('Permission denied');
expect(loggerErrorSpy).toHaveBeenCalledWith('Sanitized error:', jasmine.stringContaining('unauthorized: master key is required'));
done();
}
);
});

it('should only find with master key', done => {
const logger = require('../lib/logger').default;
const loggerErrorSpy = spyOn(logger, 'error').and.callThrough();
Copy link
Member

@mtrezza mtrezza Nov 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this outside the tests, to the top of the root describe block; only needs to be done once.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to do this in a beforeEach because some elements in the tests (I identified reconfigureServer, but there are others I haven’t identified yet) reset the logger. We need to re-require the default logger to ensure it works correctly.
I tried using a beforeAll, but it didn’t work, which is why I implemented it in each test. However, a beforeEach solution might be a good alternative

loggerErrorSpy.calls.reset();
Parse._request('GET', 'push_audiences', {}).then(
() => {},
error => {
expect(error.message).toEqual('unauthorized: master key is required');
expect(error.message).toEqual('Permission denied');
expect(loggerErrorSpy).toHaveBeenCalledWith('Sanitized error:', jasmine.stringContaining('unauthorized: master key is required'));
done();
}
);
});

it('should only get with master key', done => {
const logger = require('../lib/logger').default;
const loggerErrorSpy = spyOn(logger, 'error').and.callThrough();
loggerErrorSpy.calls.reset();
Parse._request('GET', `push_audiences/someId`, {}).then(
() => {},
error => {
expect(error.message).toEqual('unauthorized: master key is required');
expect(error.message).toEqual('Permission denied');
expect(loggerErrorSpy).toHaveBeenCalledWith('Sanitized error:', jasmine.stringContaining('unauthorized: master key is required'));
done();
}
);
});

it('should only update with master key', done => {
const logger = require('../lib/logger').default;
const loggerErrorSpy = spyOn(logger, 'error').and.callThrough();
loggerErrorSpy.calls.reset();
Parse._request('PUT', `push_audiences/someId`, {
name: 'My Audience 2',
}).then(
() => {},
error => {
expect(error.message).toEqual('unauthorized: master key is required');
expect(error.message).toEqual('Permission denied');
expect(loggerErrorSpy).toHaveBeenCalledWith('Sanitized error:', jasmine.stringContaining('unauthorized: master key is required'));
done();
}
);
});

it('should only delete with master key', done => {
const logger = require('../lib/logger').default;
const loggerErrorSpy = spyOn(logger, 'error').and.callThrough();
loggerErrorSpy.calls.reset();
Parse._request('DELETE', `push_audiences/someId`, {}).then(
() => {},
error => {
expect(error.message).toEqual('unauthorized: master key is required');
expect(error.message).toEqual('Permission denied');
expect(loggerErrorSpy).toHaveBeenCalledWith('Sanitized error:', jasmine.stringContaining('unauthorized: master key is required'));
done();
}
);
Expand Down
6 changes: 5 additions & 1 deletion spec/LogsRouter.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ describe_only(() => {
});

it('can check invalid master key of request', done => {
const logger = require('../lib/logger').default;
const loggerErrorSpy = spyOn(logger, 'error').and.callThrough();
loggerErrorSpy.calls.reset();
request({
url: 'http://localhost:8378/1/scriptlog',
headers: {
Expand All @@ -61,7 +64,8 @@ describe_only(() => {
}).then(fail, response => {
const body = response.data;
expect(response.status).toEqual(403);
expect(body.error).toEqual('unauthorized: master key is required');
expect(body.error).toEqual('Permission denied');
expect(loggerErrorSpy).toHaveBeenCalledWith('Sanitized error:', jasmine.stringContaining('unauthorized: master key is required'));
done();
});
});
Expand Down
13 changes: 9 additions & 4 deletions spec/ParseAPI.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const request = require('../lib/request');
const Parse = require('parse/node');
const Config = require('../lib/Config');
const SchemaController = require('../lib/Controllers/SchemaController');
const TestUtils = require('../lib/TestUtils');
const { destroyAllDataPermanently } = require('../lib/TestUtils');

const userSchema = SchemaController.convertSchemaToAdapterSchema({
className: '_User',
Expand Down Expand Up @@ -169,7 +169,7 @@ describe('miscellaneous', () => {
}
const config = Config.get('test');
// Remove existing data to clear out unique index
TestUtils.destroyAllDataPermanently()
destroyAllDataPermanently()
.then(() => config.database.adapter.performInitialization({ VolatileClassesSchemas: [] }))
.then(() => config.database.adapter.createClass('_User', userSchema))
.then(() =>
Expand Down Expand Up @@ -210,7 +210,7 @@ describe('miscellaneous', () => {
it_id('d00f907e-41b9-40f6-8168-63e832199a8c')(it)('ensure that if people already have duplicate emails, they can still sign up new users', done => {
const config = Config.get('test');
// Remove existing data to clear out unique index
TestUtils.destroyAllDataPermanently()
destroyAllDataPermanently()
.then(() => config.database.adapter.performInitialization({ VolatileClassesSchemas: [] }))
.then(() => config.database.adapter.createClass('_User', userSchema))
.then(() =>
Expand Down Expand Up @@ -1710,11 +1710,15 @@ describe('miscellaneous', () => {
});

it('fail on purge all objects in class without master key', done => {
const logger = require('../lib/logger').default;
const loggerErrorSpy = spyOn(logger, 'error').and.callThrough();

const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
};
loggerErrorSpy.calls.reset();
request({
method: 'DELETE',
headers: headers,
Expand All @@ -1724,7 +1728,8 @@ describe('miscellaneous', () => {
fail('Should not succeed');
})
.catch(response => {
expect(response.data.error).toEqual('unauthorized: master key is required');
expect(response.data.error).toEqual('Permission denied');
expect(loggerErrorSpy).toHaveBeenCalledWith('Sanitized error:', jasmine.stringContaining('unauthorized: master key is required'));
done();
});
});
Expand Down
17 changes: 14 additions & 3 deletions spec/ParseFile.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ describe('Parse.File testing', () => {
});

it('blocks file deletions with missing or incorrect master-key header', done => {
const logger = require('../lib/logger').default;
const loggerErrorSpy = spyOn(logger, 'error').and.callThrough();

const headers = {
'Content-Type': 'image/jpeg',
'X-Parse-Application-Id': 'test',
Expand All @@ -146,6 +149,7 @@ describe('Parse.File testing', () => {
const b = response.data;
expect(b.url).toMatch(/^http:\/\/localhost:8378\/1\/files\/test\/.*thefile.jpg$/);
// missing X-Parse-Master-Key header
loggerErrorSpy.calls.reset();
request({
method: 'DELETE',
headers: {
Expand All @@ -156,8 +160,10 @@ describe('Parse.File testing', () => {
}).then(fail, response => {
const del_b = response.data;
expect(response.status).toEqual(403);
expect(del_b.error).toMatch(/unauthorized/);
expect(del_b.error).toBe('Permission denied');
expect(loggerErrorSpy).toHaveBeenCalledWith('Sanitized error:', jasmine.stringContaining('unauthorized: master key is required'));
// incorrect X-Parse-Master-Key header
loggerErrorSpy.calls.reset();
request({
method: 'DELETE',
headers: {
Expand All @@ -169,7 +175,8 @@ describe('Parse.File testing', () => {
}).then(fail, response => {
const del_b2 = response.data;
expect(response.status).toEqual(403);
expect(del_b2.error).toMatch(/unauthorized/);
expect(del_b2.error).toBe('Permission denied');
expect(loggerErrorSpy).toHaveBeenCalledWith('Sanitized error:', jasmine.stringContaining('unauthorized: master key is required'));
done();
});
});
Expand Down Expand Up @@ -756,11 +763,15 @@ describe('Parse.File testing', () => {

describe('getting files', () => {
it('does not crash on file request with invalid app ID', async () => {
const logger = require('../lib/logger').default;
const loggerErrorSpy = spyOn(logger, 'error').and.callThrough();
loggerErrorSpy.calls.reset();
const res1 = await request({
url: 'http://localhost:8378/1/files/invalid-id/invalid-file.txt',
}).catch(e => e);
expect(res1.status).toBe(403);
expect(res1.data).toEqual({ code: 119, error: 'Invalid application ID.' });
expect(res1.data).toEqual({ code: 119, error: 'Permission denied' });
expect(loggerErrorSpy).toHaveBeenCalledWith('Sanitized error:', jasmine.stringContaining('Invalid application ID.'));
// Ensure server did not crash
const res2 = await request({ url: 'http://localhost:8378/1/health' });
expect(res2.status).toEqual(200);
Expand Down
6 changes: 5 additions & 1 deletion spec/ParseGlobalConfig.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,9 @@ describe('a GlobalConfig', () => {
});

it('fail to update if master key is missing', done => {
const logger = require('../lib/logger').default;
const loggerErrorSpy = spyOn(logger, 'error').and.callThrough();
loggerErrorSpy.calls.reset();
request({
method: 'PUT',
url: 'http://localhost:8378/1/config',
Expand All @@ -233,7 +236,8 @@ describe('a GlobalConfig', () => {
}).then(fail, response => {
const body = response.data;
expect(response.status).toEqual(403);
expect(body.error).toEqual('unauthorized: master key is required');
expect(body.error).toEqual('Permission denied');
expect(loggerErrorSpy).toHaveBeenCalledWith('Sanitized error:', jasmine.stringContaining('unauthorized: master key is required'));
done();
});
});
Expand Down
45 changes: 36 additions & 9 deletions spec/ParseGraphQLServer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3488,6 +3488,9 @@ describe('ParseGraphQLServer', () => {
});

it('should require master key to create a new class', async () => {
const logger = require('../lib/logger').default;
const loggerErrorSpy = spyOn(logger, 'error').and.callThrough();
loggerErrorSpy.calls.reset();
try {
await apolloClient.mutate({
mutation: gql`
Expand All @@ -3501,7 +3504,8 @@ describe('ParseGraphQLServer', () => {
fail('should fail');
} catch (e) {
expect(e.graphQLErrors[0].extensions.code).toEqual(Parse.Error.OPERATION_FORBIDDEN);
expect(e.graphQLErrors[0].message).toEqual('unauthorized: master key is required');
expect(e.graphQLErrors[0].message).toEqual('Permission denied');
expect(loggerErrorSpy).toHaveBeenCalledWith('Sanitized error:', jasmine.stringContaining('unauthorized: master key is required'));
}
});

Expand Down Expand Up @@ -3858,6 +3862,9 @@ describe('ParseGraphQLServer', () => {
handleError(e);
}

const logger = require('../lib/logger').default;
const loggerErrorSpy = spyOn(logger, 'error').and.callThrough();
loggerErrorSpy.calls.reset();
try {
await apolloClient.mutate({
mutation: gql`
Expand All @@ -3871,7 +3878,8 @@ describe('ParseGraphQLServer', () => {
fail('should fail');
} catch (e) {
expect(e.graphQLErrors[0].extensions.code).toEqual(Parse.Error.OPERATION_FORBIDDEN);
expect(e.graphQLErrors[0].message).toEqual('unauthorized: master key is required');
expect(e.graphQLErrors[0].message).toEqual('Permission denied');
expect(loggerErrorSpy).toHaveBeenCalledWith('Sanitized error:', jasmine.stringContaining('unauthorized: master key is required'));
}
});

Expand Down Expand Up @@ -4083,6 +4091,9 @@ describe('ParseGraphQLServer', () => {
handleError(e);
}

const logger = require('../lib/logger').default;
const loggerErrorSpy = spyOn(logger, 'error').and.callThrough();
loggerErrorSpy.calls.reset();
try {
await apolloClient.mutate({
mutation: gql`
Expand All @@ -4096,7 +4107,8 @@ describe('ParseGraphQLServer', () => {
fail('should fail');
} catch (e) {
expect(e.graphQLErrors[0].extensions.code).toEqual(Parse.Error.OPERATION_FORBIDDEN);
expect(e.graphQLErrors[0].message).toEqual('unauthorized: master key is required');
expect(e.graphQLErrors[0].message).toEqual('Permission denied');
expect(loggerErrorSpy).toHaveBeenCalledWith('Sanitized error:', jasmine.stringContaining('unauthorized: master key is required'));
}
});

Expand Down Expand Up @@ -4124,6 +4136,9 @@ describe('ParseGraphQLServer', () => {
});

it('should require master key to get an existing class', async () => {
const logger = require('../lib/logger').default;
const loggerErrorSpy = spyOn(logger, 'error').and.callThrough();
loggerErrorSpy.calls.reset();
try {
await apolloClient.query({
query: gql`
Expand All @@ -4137,11 +4152,15 @@ describe('ParseGraphQLServer', () => {
fail('should fail');
} catch (e) {
expect(e.graphQLErrors[0].extensions.code).toEqual(Parse.Error.OPERATION_FORBIDDEN);
expect(e.graphQLErrors[0].message).toEqual('unauthorized: master key is required');
expect(e.graphQLErrors[0].message).toEqual('Permission denied');
expect(loggerErrorSpy).toHaveBeenCalledWith('Sanitized error:', jasmine.stringContaining('unauthorized: master key is required'));
}
});

it('should require master key to find the existing classes', async () => {
const logger = require('../lib/logger').default;
const loggerErrorSpy = spyOn(logger, 'error').and.callThrough();
loggerErrorSpy.calls.reset();
try {
await apolloClient.query({
query: gql`
Expand All @@ -4155,7 +4174,8 @@ describe('ParseGraphQLServer', () => {
fail('should fail');
} catch (e) {
expect(e.graphQLErrors[0].extensions.code).toEqual(Parse.Error.OPERATION_FORBIDDEN);
expect(e.graphQLErrors[0].message).toEqual('unauthorized: master key is required');
expect(e.graphQLErrors[0].message).toEqual('Permission denied');
expect(loggerErrorSpy).toHaveBeenCalledWith('Sanitized error:', jasmine.stringContaining('unauthorized: master key is required'));
}
});
});
Expand Down Expand Up @@ -6081,7 +6101,7 @@ describe('ParseGraphQLServer', () => {
}

await expectAsync(createObject('GraphQLClass')).toBeRejectedWith(
jasmine.stringMatching('Permission denied for action create on class GraphQLClass')
jasmine.stringMatching('Permission denied')
);
await expectAsync(createObject('PublicClass')).toBeResolved();
await expectAsync(
Expand Down Expand Up @@ -6115,7 +6135,7 @@ describe('ParseGraphQLServer', () => {
'X-Parse-Session-Token': user4.getSessionToken(),
})
).toBeRejectedWith(
jasmine.stringMatching('Permission denied for action create on class GraphQLClass')
jasmine.stringMatching('Permission denied')
);
await expectAsync(
createObject('PublicClass', {
Expand Down Expand Up @@ -7781,6 +7801,8 @@ describe('ParseGraphQLServer', () => {
});

it('should fail due to empty session token', async () => {
const logger = require('../lib/logger').default;
const loggerErrorSpy = spyOn(logger, 'error').and.callThrough();
try {
await apolloClient.query({
query: gql`
Expand All @@ -7802,7 +7824,8 @@ describe('ParseGraphQLServer', () => {
} catch (err) {
const { graphQLErrors } = err;
expect(graphQLErrors.length).toBe(1);
expect(graphQLErrors[0].message).toBe('Invalid session token');
expect(graphQLErrors[0].message).toBe('Permission denied');
expect(loggerErrorSpy).toHaveBeenCalledWith('Sanitized error:', jasmine.stringContaining('Invalid session token'));
}
});

Expand All @@ -7812,6 +7835,9 @@ describe('ParseGraphQLServer', () => {

await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear();

const logger = require('../lib/logger').default;
const loggerErrorSpy = spyOn(logger, 'error').and.callThrough();

try {
await apolloClient.query({
query: gql`
Expand Down Expand Up @@ -7840,7 +7866,8 @@ describe('ParseGraphQLServer', () => {
} catch (err) {
const { graphQLErrors } = err;
expect(graphQLErrors.length).toBe(1);
expect(graphQLErrors[0].message).toBe('Invalid session token');
expect(graphQLErrors[0].message).toBe('Permission denied');
expect(loggerErrorSpy).toHaveBeenCalledWith('Sanitized error:', jasmine.stringContaining('Invalid session token'));
}
});
});
Expand Down
Loading
Loading