mirror of
https://github.com/parse-community/parse-server
synced 2026-08-09 13:03:18 +00:00
fix: Malformed $regex query leaks database error details in API response (GHSA-9cp7-3q5w-j92g) (#10102)
This commit is contained in:
@@ -478,6 +478,75 @@ describe('Vulnerabilities', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Malformed $regex information disclosure', () => {
|
||||
it('should not leak database error internals for invalid regex pattern in class query', async () => {
|
||||
const logger = require('../lib/logger').default;
|
||||
const loggerErrorSpy = spyOn(logger, 'error').and.callThrough();
|
||||
const obj = new Parse.Object('TestObject');
|
||||
await obj.save({ field: 'value' });
|
||||
|
||||
try {
|
||||
await request({
|
||||
method: 'GET',
|
||||
url: `http://localhost:8378/1/classes/TestObject`,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest',
|
||||
},
|
||||
qs: {
|
||||
where: JSON.stringify({ field: { $regex: '[abc' } }),
|
||||
},
|
||||
});
|
||||
fail('Request should have failed');
|
||||
} catch (e) {
|
||||
expect(e.data.code).toBe(Parse.Error.INTERNAL_SERVER_ERROR);
|
||||
expect(e.data.error).toBe('An internal server error occurred');
|
||||
expect(typeof e.data.error).toBe('string');
|
||||
expect(JSON.stringify(e.data)).not.toContain('errmsg');
|
||||
expect(JSON.stringify(e.data)).not.toContain('codeName');
|
||||
expect(JSON.stringify(e.data)).not.toContain('errorResponse');
|
||||
expect(loggerErrorSpy).toHaveBeenCalledWith(
|
||||
'Sanitized error:',
|
||||
jasmine.stringMatching(/[Rr]egular expression/i)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('should not leak database error internals for invalid regex pattern in role query', async () => {
|
||||
const logger = require('../lib/logger').default;
|
||||
const loggerErrorSpy = spyOn(logger, 'error').and.callThrough();
|
||||
const role = new Parse.Role('testrole', new Parse.ACL());
|
||||
await role.save(null, { useMasterKey: true });
|
||||
try {
|
||||
await request({
|
||||
method: 'GET',
|
||||
url: `http://localhost:8378/1/roles`,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest',
|
||||
},
|
||||
qs: {
|
||||
where: JSON.stringify({ name: { $regex: '[abc' } }),
|
||||
},
|
||||
});
|
||||
fail('Request should have failed');
|
||||
} catch (e) {
|
||||
expect(e.data.code).toBe(Parse.Error.INTERNAL_SERVER_ERROR);
|
||||
expect(e.data.error).toBe('An internal server error occurred');
|
||||
expect(typeof e.data.error).toBe('string');
|
||||
expect(JSON.stringify(e.data)).not.toContain('errmsg');
|
||||
expect(JSON.stringify(e.data)).not.toContain('codeName');
|
||||
expect(JSON.stringify(e.data)).not.toContain('errorResponse');
|
||||
expect(loggerErrorSpy).toHaveBeenCalledWith(
|
||||
'Sanitized error:',
|
||||
jasmine.stringMatching(/[Rr]egular expression/i)
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('Postgres regex sanitizater', () => {
|
||||
it('sanitizes the regex correctly to prevent Injection', async () => {
|
||||
const user = new Parse.User();
|
||||
|
||||
@@ -20,6 +20,7 @@ import SchemaCache from '../Adapters/Cache/SchemaCache';
|
||||
import type { LoadSchemaOptions } from './types';
|
||||
import type { ParseServerOptions } from '../Options';
|
||||
import type { QueryOptions, FullQueryOptions } from '../Adapters/Storage/StorageAdapter';
|
||||
import { createSanitizedError } from '../Error';
|
||||
|
||||
function addWriteACL(query, acl) {
|
||||
const newQuery = _.cloneDeep(query);
|
||||
@@ -1354,7 +1355,19 @@ class DatabaseController {
|
||||
})
|
||||
)
|
||||
.catch(error => {
|
||||
throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, error);
|
||||
if (error instanceof Parse.Error) {
|
||||
throw error;
|
||||
}
|
||||
const detailedMessage =
|
||||
typeof error === 'string'
|
||||
? error
|
||||
: error?.message || 'An internal server error occurred';
|
||||
throw createSanitizedError(
|
||||
Parse.Error.INTERNAL_SERVER_ERROR,
|
||||
detailedMessage,
|
||||
this.options,
|
||||
'An internal server error occurred'
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
+4
-2
@@ -6,9 +6,11 @@ import defaultLogger from './logger';
|
||||
*
|
||||
* @param {number} errorCode - The Parse.Error code (e.g., Parse.Error.OPERATION_FORBIDDEN)
|
||||
* @param {string} detailedMessage - The detailed error message to log server-side
|
||||
* @param {object} config - Parse Server config with enableSanitizedErrorResponse
|
||||
* @param {string} [sanitizedMessage='Permission denied'] - The sanitized message to return to clients
|
||||
* @returns {Parse.Error} A Parse.Error with sanitized message
|
||||
*/
|
||||
function createSanitizedError(errorCode, detailedMessage, config) {
|
||||
function createSanitizedError(errorCode, detailedMessage, config, sanitizedMessage = 'Permission denied') {
|
||||
// On testing we need to add a prefix to the message to allow to find the correct call in the TestUtils.js file
|
||||
if (process.env.TESTING) {
|
||||
defaultLogger.error('Sanitized error:', detailedMessage);
|
||||
@@ -16,7 +18,7 @@ function createSanitizedError(errorCode, detailedMessage, config) {
|
||||
defaultLogger.error(detailedMessage);
|
||||
}
|
||||
|
||||
return new Parse.Error(errorCode, config?.enableSanitizedErrorResponse !== false ? 'Permission denied' : detailedMessage);
|
||||
return new Parse.Error(errorCode, config?.enableSanitizedErrorResponse !== false ? sanitizedMessage : detailedMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user