Skip to content

Fix patternProperties regex character classes misinterpreted as array index in dev-mode schema check#8577

Merged
pubkey merged 1 commit into
masterfrom
copilot/add-test-and-fix-to-suite
May 26, 2026
Merged

Fix patternProperties regex character classes misinterpreted as array index in dev-mode schema check#8577
pubkey merged 1 commit into
masterfrom
copilot/add-test-and-fix-to-suite

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 26, 2026

When a schema uses patternProperties with regex character classes (e.g. ^[a-z]\w*$), the dev-mode schema validator incorrectly interprets the square brackets as array index notation, causing a crash when creating the collection.

Changes

  • src/plugins/dev-mode/check-schema.ts: Filter out flattened schema key paths containing .patternProperties. before the legacy index: true check, preventing regex pattern strings from being parsed as array accessors.
  • test/unit/rx-schema.test.ts: Added regression test #8522 — schema with patternProperties: { '^[a-z]\\w*$': ... } must successfully create a collection, insert, and retrieve a document.
  • orga/changelog/fix-pattern-properties-character-class.md: Changelog entry.

Example

Previously this schema would throw in dev mode:

const schema = {
    version: 0,
    primaryKey: 'id',
    type: 'object',
    properties: {
        id: { type: 'string', maxLength: 100 },
        personFields: {
            type: 'object',
            patternProperties: {
                '^[a-z]\\w*$': { type: 'string' }  // square brackets caused crash
            },
            additionalProperties: false
        }
    }
};

@github-actions
Copy link
Copy Markdown
Contributor

✅ Verify Test Reproduction: Tests FAILED without the fix (expected)

This confirms the changed tests correctly reproduce the bug that the source changes fix.

This workflow runs the changed tests without the source fix to verify they reproduce the bug.

Show output
...(truncated, showing last 200 of 1126 lines)
        �[32m✓ �[39m_deleted documents must loose all attachments
        �[32m✓ �[39mmust be able to load multiple attachments data in parallel
      .cleanup
        �[32m✓ �[39mshould have cleaned up the deleted document
        �[32m✓ �[39mshould clean up all deleted documents when multiple are deleted
        �[32m✓ �[39mshould at some time return true (when all docs are cleaned up)
      .close()
        �[32m✓ �[39mclosing multiple times should not error
      .remove()
        �[32m✓ �[39mshould have deleted all data
        �[32m✓ �[39mshould throw on call to .remove() after .close()
        �[32m✓ �[39mshould NOT throw on call to .close() after .remove()
    multiInstance
      �[32m✓ �[39mshould update the state on the other instance
      �[32m✓ �[39mshould be able to write and read documents
      �[32m✓ �[39mshould be able to finish a query even when the leading instance gets closed
      �[32m✓ �[39mshould not mix up documents stored with different schema versions
      �[32m✓ �[39mshould not mix up documents stored in a different database name
      �[32m✓ �[39mshould emit events from one instance to the other
    migration
      �[32m✓ �[39mdocuments that are stored on different schema versions, should not interfere

  rx-storage-query-correctness.test.ts
    �[32m✓ �[39m$gt/$gte
    �[32m✓ �[39m$lt/$lte
    �[32m✓ �[39mnested properties
    �[32m✓ �[39m$or
    �[32m✓ �[39m$in
    �[32m✓ �[39m$in for array fields named skills
    �[32m✓ �[39m$nin
    �[32m✓ �[39m$elemMatch/$size
    �[32m✓ �[39m$elemMatch nested arrays with regex
    �[32m✓ �[39m$eq operator
    �[32m✓ �[39m$eq operator with composite primary key
    �[32m✓ �[39missue: compound index has wrong range
    �[32m✓ �[39m$type
    �[32m✓ �[39missue: wrong results on complex index
    �[32m✓ �[39missue: wrong results using skip and limit
    �[32m✓ �[39mUsing undefined in a selector must have the same result in all storages
    �[32m✓ �[39mnumber comparison at exact boundary values including min/max
    �[32m✓ �[39mnegative decimal numbers sort and query correctly
    �[32m✓ �[39mcompound index with decimal numbers preserves correct sort order
    �[32m✓ �[39menum field with $eq should be sort-irrelevant and return correct results
    �[32m✓ �[39m$gt/$lt on field before boolean index field should not include boundary docs
    �[32m✓ �[39mstring fields with control characters (codepoint < 32) must be found by queries

  rx-storage-helper.test.ts
    .categorizeBulkWriteRows()
      �[32m✓ �[39mperformance

  rx-storage-dexie.test.js
    helper
      .getDexieStoreSchema()
        �[32m✓ �[39mshould start with the primary key
      .fromStorageToDexie()
        �[32m✓ �[39mshould convert unsupported IndexedDB key
      .fromDexieToStorage()
        �[32m✓ �[39mshould revert escaped unsupported IndexedDB key
    .query()
      �[32m✓ �[39mshould throw on optional index

  instance-of-check.test.js
    �[32m✓ �[39mpositive
    �[32m✓ �[39mnegative

  rx-schema.test.ts
    static
      .getIndexes()
        �[32m✓ �[39mget single indexes
        �[32m✓ �[39mget multiple indexes
        �[32m✓ �[39mget sub-index
        �[32m✓ �[39mget no index
        �[32m✓ �[39mget compoundIndex
      .checkSchema()
        �[32m✓ �[39mshould not throw error for such schema
        positive
          �[32m✓ �[39mvalidate human
          �[32m✓ �[39mvalidate bigHuman
          �[32m✓ �[39mvalidate without index
          �[32m✓ �[39mvalidate with compoundIndexes
          �[32m✓ �[39mvalidate empty
          �[32m✓ �[39mvalidate with defaults
          �[32m✓ �[39mvalidate point
          �[32m✓ �[39mvalidates deep nested indexes
          �[32m✓ �[39mvalidate anyOf
          �[32m✓ �[39mvalidate items array
        negative
          �[32m✓ �[39mbreak when index defined at object property level
          �[32m✓ �[39mthrow when underscore field is used as property name
          �[32m✓ �[39mbreak when index is no string
          �[32m✓ �[39mbreak when index does not exist in schema properties
          �[32m✓ �[39mbreak when index is string but too long
          �[32m✓ �[39mbreak when primaryKey maxLength string but too long
          �[32m✓ �[39mbreak compoundIndex key is no string
          �[32m✓ �[39mbreak when dots in fieldname
          �[32m✓ �[39mbreak when required is set via required: true
          �[32m✓ �[39mshould not allow $-char in fieldnames
          �[32m✓ �[39mshould not allow $-char in nested fieldnames
          �[32m✓ �[39mshould not allow ending lodash _ in fieldnames (reserved for populate)
          �[32m✓ �[39mshould not allow RxDocument-properties as top-fieldnames (own)
          �[32m✓ �[39mshould not allow RxDocument-properties as top-fieldnames (prototype)
          �[32m✓ �[39mthrow when no version
          �[32m✓ �[39mthrow when version < 0
          �[32m✓ �[39mthrow when version no number
          �[32m✓ �[39mthrow when defaults on non-first-level field
          �[32m✓ �[39mthrow when _id is not primary
          �[32m✓ �[39mshould throw when composite primary key field is encrypted (SC15)
          �[32m✓ �[39mshould throw when encrypted field is nested inside another encrypted field (SC43)
          �[32m✓ �[39mshould throw when composite primary key field is in indexes (SC13)
          �[32m✓ �[39mthrow when $ref field is used
      .fillWithDefaultSettings() / .normalizeRxJsonSchema()
        �[32m✓ �[39mshould sort array with objects and strings
        �[32m✓ �[39mshould be the same object
        �[32m✓ �[39mshould deep sort one schema with different orders to be the same
        �[32m✓ �[39mshould not sort indexes array in the schema (related with https://github.com/pubkey/rxdb/pull/1695#issuecomment-554636433)
        �[32m✓ �[39mshould have added the primaryKey to indexes that did not contain it
        �[32m✓ �[39mshould deduplicate indexes that become identical after adding _deleted prefix and primaryKey suffix
      .create()
        positive
          �[32m✓ �[39mcreate human
          �[32m✓ �[39mcreate nested
          �[32m✓ �[39mcreate point
          �[32m✓ �[39mshould have indexes human
        negative
          �[32m✓ �[39mbroken schema (nostringIndex)
      .getFinalFields()
        �[32m✓ �[39mshould contain the field
        �[32m✓ �[39mshould contain the primary
    instance
      .getPreviousVersions()
        �[32m✓ �[39mget empty array when current==0
        �[32m✓ �[39mget valid array when current==5
      .hash
        positive
          �[32m✓ �[39mshould hash
          �[32m✓ �[39mshould normalize one schema with two different orders and generate for each the same hash
          �[32m✓ �[39m#4005 should respect the sort order
      .validateChange()
        positive
          �[32m✓ �[39mshould allow a valid change
        negative
          �[32m✓ �[39mshould not allow to change the primary
          �[32m✓ �[39mshould not allow to change a final field
      .getSchemaByObjectPath()
        positive
          �[32m✓ �[39mget firstLevel
          �[32m✓ �[39mget deeper
          �[32m✓ �[39mget nested
      .fillObjectWithDefaults()
        positive
          �[32m✓ �[39mshould fill all unset fields
          �[32m✓ �[39mshould not overwrite given values
          �[32m✓ �[39mshould not share non-primitive default value references between filled objects
    issues
      �[32m✓ �[39m#590 Strange schema behavior with sub-sub-index
      �[32m✓ �[39m#620 indexes should not be required
      �[32m✓ �[39m#697 Indexes do not work in objects named "properties"
      �[32m✓ �[39m#697(2) should also work deep nested
      �[32m✓ �[39m#3994 must work with a boolean index
      �[32m✓ �[39m#4951 patternProperties are allowed
      �[31m✗ �[39m�[31m#8522 patternProperties with square bracket character classes should not be interpreted as array index�[39m
	Error: Invalid character in an index
	    at getPathSegments (webpack://rxdb/./dist/esm/plugins/utils/utils-object-dot-prop.js?:107:19)
	    at getProperty (webpack://rxdb/./dist/esm/plugins/utils/utils-object-dot-prop.js?:205:19)
	    at eval (webpack://rxdb/./dist/esm/plugins/dev-mode/check-schema.js?:435:85)
	    at Array.filter (<anonymous>)
	    at checkSchema (webpack://rxdb/./dist/esm/plugins/dev-mode/check-schema.js?:433:4)
	    at eval (webpack://rxdb/./dist/esm/hooks.js?:108:35)
	    at Array.forEach (<anonymous>)
	    at runPluginHooks (webpack://rxdb/./dist/esm/hooks.js?:108:20)
	    at createRxSchema (webpack://rxdb/./dist/esm/rx-schema.js?:184:62)
	    at eval (webpack://rxdb/./dist/esm/rx-database.js?:262:82)
	    at Array.map (<anonymous>)
	    at RxDatabaseBase.addCollections (webpack://rxdb/./dist/esm/rx-database.js?:258:58)
	    at Context.eval (webpack://rxdb/./test_tmp/unit/rx-schema.test.js?:1317:34)


Chrome Headless 148.0.0.0 (Linux 0.0.0): Executed 316 of 1296�[31m (1 FAILED)�[39m (6.61 secs / 6.467 secs)
�[31mTOTAL: 1 FAILED, 315 SUCCESS�[39m


�[31m1) #8522 patternProperties with square bracket character classes should not be interpreted as array index
�[39m�[31m     rx-schema.test.ts issues
�[39m�[90m     Error: Invalid character in an index
    at getPathSegments (webpack://rxdb/./dist/esm/plugins/utils/utils-object-dot-prop.js?:107:19)
    at getProperty (webpack://rxdb/./dist/esm/plugins/utils/utils-object-dot-prop.js?:205:19)
    at eval (webpack://rxdb/./dist/esm/plugins/dev-mode/check-schema.js?:435:85)
    at Array.filter (<anonymous>)
    at checkSchema (webpack://rxdb/./dist/esm/plugins/dev-mode/check-schema.js?:433:4)
    at eval (webpack://rxdb/./dist/esm/hooks.js?:108:35)
    at Array.forEach (<anonymous>)
    at runPluginHooks (webpack://rxdb/./dist/esm/hooks.js?:108:20)
    at createRxSchema (webpack://rxdb/./dist/esm/rx-schema.js?:184:62)
    at eval (webpack://rxdb/./dist/esm/rx-database.js?:262:82)
    at Array.map (<anonymous>)
    at RxDatabaseBase.addCollections (webpack://rxdb/./dist/esm/rx-database.js?:258:58)
    at Context.eval (webpack://rxdb/./test_tmp/unit/rx-schema.test.js?:1317:34)
�[39m



View full workflow run

@pubkey pubkey marked this pull request as ready for review May 26, 2026 14:52
@pubkey pubkey merged commit d789bef into master May 26, 2026
21 of 24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants