From 1f9b6fe9459e889e8891c5d1a9a62669767ed0a9 Mon Sep 17 00:00:00 2001 From: rgunindi Date: Sun, 24 Aug 2025 17:35:23 +0200 Subject: [PATCH 1/2] fix: Prevent TypeError when type is not a string Added a type check in mongoFieldToParseSchemaField to ensure `type` is a string before calling `startsWith`. This prevents crashes when Parse Server processes MongoDB schema fields with undefined, null, or unexpected type values. Closes #9847 --- src/Adapters/Storage/Mongo/MongoSchemaCollection.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Adapters/Storage/Mongo/MongoSchemaCollection.js b/src/Adapters/Storage/Mongo/MongoSchemaCollection.js index 45b27f7516..b27fa56cb4 100644 --- a/src/Adapters/Storage/Mongo/MongoSchemaCollection.js +++ b/src/Adapters/Storage/Mongo/MongoSchemaCollection.js @@ -2,6 +2,14 @@ import MongoCollection from './MongoCollection'; import Parse from 'parse/node'; function mongoFieldToParseSchemaField(type) { + // Add type validation to prevent TypeError + if (!type || typeof type !== 'string') { + throw new Parse.Error( + Parse.Error.INVALID_SCHEMA_OPERATION, + `Invalid field type: ${type}. Expected a string. Field type must be one of: string, number, boolean, date, map, object, array, geopoint, file, bytes, polygon, or a valid relation/pointer format.` + ); + } + if (type[0] === '*') { return { type: 'Pointer', From a882ffe2b081465f43227974b55b3be70bfa6b56 Mon Sep 17 00:00:00 2001 From: rgunindi Date: Thu, 28 Aug 2025 20:24:38 +0200 Subject: [PATCH 2/2] test: Add comprehensive tests for mongoFieldToParseSchemaField type validation and clear error message - Add tests for valid field type conversions - Add tests for invalid type handling (null, undefined, non-string values) - Ensure Parse.Error code is thrown for invalid inputs - Test coverage for the type validation improvements - Add a clear error explanation --- spec/MongoSchemaCollectionAdapter.spec.js | 103 ++++++++++++++++++ .../Storage/Mongo/MongoSchemaCollection.js | 2 +- 2 files changed, 104 insertions(+), 1 deletion(-) diff --git a/spec/MongoSchemaCollectionAdapter.spec.js b/spec/MongoSchemaCollectionAdapter.spec.js index 8e376b9d1d..c46e5a0d81 100644 --- a/spec/MongoSchemaCollectionAdapter.spec.js +++ b/spec/MongoSchemaCollectionAdapter.spec.js @@ -96,4 +96,107 @@ describe('MongoSchemaCollection', () => { }); done(); }); + + describe('mongoFieldToParseSchemaField function', () => { + // Test successful type conversions + it('should convert valid mongo field types to parse schema fields', () => { + const testCases = [ + { input: 'string', expected: { type: 'String' } }, + { input: 'number', expected: { type: 'Number' } }, + { input: 'boolean', expected: { type: 'Boolean' } }, + { input: 'date', expected: { type: 'Date' } }, + { input: 'map', expected: { type: 'Object' } }, + { input: 'object', expected: { type: 'Object' } }, + { input: 'array', expected: { type: 'Array' } }, + { input: 'geopoint', expected: { type: 'GeoPoint' } }, + { input: 'file', expected: { type: 'File' } }, + { input: 'bytes', expected: { type: 'Bytes' } }, + { input: 'polygon', expected: { type: 'Polygon' } }, + { input: '*_User', expected: { type: 'Pointer', targetClass: '_User' } }, + { input: '*Post', expected: { type: 'Pointer', targetClass: 'Post' } }, + { input: 'relation<_User>', expected: { type: 'Relation', targetClass: '_User' } }, + { input: 'relation', expected: { type: 'Relation', targetClass: 'Post' } }, + ]; + + testCases.forEach(({ input, expected }) => { + const result = MongoSchemaCollection._TESTmongoSchemaToParseSchema({ + _id: 'TestClass', + testField: input, + }); + + expect(result.fields.testField).toEqual(expected); + }); + }); + + // Test error handling for invalid types (non-string values) + it('should throw error for invalid field types', () => { + const invalidInputs = [ + null, + undefined, + 123, + true, + false, + {}, + [], + '', + ]; + + invalidInputs.forEach(invalidInput => { + expect(() => { + MongoSchemaCollection._TESTmongoSchemaToParseSchema({ + _id: 'TestClass', + testField: invalidInput, + }); + }).toThrow(); + }); + }); + + it('should throw error with correct message for null input', () => { + try { + MongoSchemaCollection._TESTmongoSchemaToParseSchema({ + _id: 'TestClass', + testField: null, + }); + } catch (error) { + expect(error.code).toBe(255); + expect(error.message).toContain('Invalid field type'); + } + }); + + it('should throw error with correct message for undefined input', () => { + try { + MongoSchemaCollection._TESTmongoSchemaToParseSchema({ + _id: 'TestClass', + testField: undefined, + }); + } catch (error) { + expect(error.code).toBe(255); + expect(error.message).toContain('Invalid field type'); + } + }); + + it('should throw error with correct message for non-string input', () => { + try { + MongoSchemaCollection._TESTmongoSchemaToParseSchema({ + _id: 'TestClass', + testField: 123, + }); + } catch (error) { + expect(error.code).toBe(255); + expect(error.message).toContain('Invalid field type'); + } + }); + + it('should throw error with correct message for empty string input', () => { + try { + MongoSchemaCollection._TESTmongoSchemaToParseSchema({ + _id: 'TestClass', + testField: '', + }); + } catch (error) { + expect(error.code).toBe(255); + expect(error.message).toContain('Invalid field type'); + } + }); + }); }); diff --git a/src/Adapters/Storage/Mongo/MongoSchemaCollection.js b/src/Adapters/Storage/Mongo/MongoSchemaCollection.js index b27fa56cb4..c52a285bf6 100644 --- a/src/Adapters/Storage/Mongo/MongoSchemaCollection.js +++ b/src/Adapters/Storage/Mongo/MongoSchemaCollection.js @@ -6,7 +6,7 @@ function mongoFieldToParseSchemaField(type) { if (!type || typeof type !== 'string') { throw new Parse.Error( Parse.Error.INVALID_SCHEMA_OPERATION, - `Invalid field type: ${type}. Expected a string. Field type must be one of: string, number, boolean, date, map, object, array, geopoint, file, bytes, polygon, or a valid relation/pointer format.` + `Invalid field type: ${type}. Expected a string. Fix the type mismatch in your schema configuration.` ); }