fix: MongoDB timeout errors unhandled and potentially revealing internal data (#10020)

This commit is contained in:
Manuel
2026-01-25 00:15:01 +01:00
committed by GitHub
parent 1b5bd2f754
commit 1d3336d128
5 changed files with 164 additions and 4 deletions

View File

@@ -1064,6 +1064,129 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
});
});
describe('transient error handling', () => {
it('should transform MongoWaitQueueTimeoutError to Parse.Error.INTERNAL_SERVER_ERROR', async () => {
const adapter = new MongoStorageAdapter({ uri: databaseURI });
await adapter.connect();
// Create a mock error with the MongoWaitQueueTimeoutError name
const mockError = new Error('Timed out while checking out a connection from connection pool');
mockError.name = 'MongoWaitQueueTimeoutError';
try {
adapter.handleError(mockError);
fail('Expected handleError to throw');
} catch (error) {
expect(error instanceof Parse.Error).toBe(true);
expect(error.code).toBe(Parse.Error.INTERNAL_SERVER_ERROR);
expect(error.message).toBe('Database error');
}
});
it('should transform MongoServerSelectionError to Parse.Error.INTERNAL_SERVER_ERROR', async () => {
const adapter = new MongoStorageAdapter({ uri: databaseURI });
await adapter.connect();
const mockError = new Error('Server selection timed out');
mockError.name = 'MongoServerSelectionError';
try {
adapter.handleError(mockError);
fail('Expected handleError to throw');
} catch (error) {
expect(error instanceof Parse.Error).toBe(true);
expect(error.code).toBe(Parse.Error.INTERNAL_SERVER_ERROR);
expect(error.message).toBe('Database error');
}
});
it('should transform MongoNetworkTimeoutError to Parse.Error.INTERNAL_SERVER_ERROR', async () => {
const adapter = new MongoStorageAdapter({ uri: databaseURI });
await adapter.connect();
const mockError = new Error('Network timeout');
mockError.name = 'MongoNetworkTimeoutError';
try {
adapter.handleError(mockError);
fail('Expected handleError to throw');
} catch (error) {
expect(error instanceof Parse.Error).toBe(true);
expect(error.code).toBe(Parse.Error.INTERNAL_SERVER_ERROR);
expect(error.message).toBe('Database error');
}
});
it('should transform MongoNetworkError to Parse.Error.INTERNAL_SERVER_ERROR', async () => {
const adapter = new MongoStorageAdapter({ uri: databaseURI });
await adapter.connect();
const mockError = new Error('Network error');
mockError.name = 'MongoNetworkError';
try {
adapter.handleError(mockError);
fail('Expected handleError to throw');
} catch (error) {
expect(error instanceof Parse.Error).toBe(true);
expect(error.code).toBe(Parse.Error.INTERNAL_SERVER_ERROR);
expect(error.message).toBe('Database error');
}
});
it('should transform TransientTransactionError to Parse.Error.INTERNAL_SERVER_ERROR', async () => {
const adapter = new MongoStorageAdapter({ uri: databaseURI });
await adapter.connect();
const mockError = new Error('Transient transaction error');
mockError.hasErrorLabel = label => label === 'TransientTransactionError';
try {
adapter.handleError(mockError);
fail('Expected handleError to throw');
} catch (error) {
expect(error instanceof Parse.Error).toBe(true);
expect(error.code).toBe(Parse.Error.INTERNAL_SERVER_ERROR);
expect(error.message).toBe('Database error');
}
});
it('should not transform non-transient errors', async () => {
const adapter = new MongoStorageAdapter({ uri: databaseURI });
await adapter.connect();
const mockError = new Error('Some other error');
mockError.name = 'SomeOtherError';
try {
adapter.handleError(mockError);
fail('Expected handleError to throw');
} catch (error) {
expect(error instanceof Parse.Error).toBe(false);
expect(error.message).toBe('Some other error');
}
});
it('should handle null/undefined errors', async () => {
const adapter = new MongoStorageAdapter({ uri: databaseURI });
await adapter.connect();
try {
adapter.handleError(null);
fail('Expected handleError to throw');
} catch (error) {
expect(error).toBeNull();
}
try {
adapter.handleError(undefined);
fail('Expected handleError to throw');
} catch (error) {
expect(error).toBeUndefined();
}
});
});
describe('MongoDB Client Metadata', () => {
it('should not pass metadata to MongoClient by default', async () => {
const adapter = new MongoStorageAdapter({ uri: databaseURI });

View File

@@ -55,7 +55,7 @@ describe('Server Url Checks', () => {
parseServerProcess.on('close', async code => {
expect(code).toEqual(1);
expect(stdout).not.toContain('UnhandledPromiseRejectionWarning');
expect(stderr).toContain('MongoServerSelectionError');
expect(stderr).toContain('Database error');
await reconfigureServer();
done();
});

View File

@@ -73,7 +73,7 @@ describe('server', () => {
}),
});
const error = await server.start().catch(e => e);
expect(`${error}`.includes('MongoServerSelectionError')).toBeTrue();
expect(`${error}`.includes('Database error')).toBeTrue();
await reconfigureServer();
});