diff --git a/src/runtime/internal/security.ts b/src/runtime/internal/security.ts index 9b7de18e1..d96e705ad 100644 --- a/src/runtime/internal/security.ts +++ b/src/runtime/internal/security.ts @@ -185,7 +185,7 @@ export function assertSafeQuery(sql: string, collection: string) { // ORDER BY const _order = (orderBy + ' ' + order).split(', ') - if (!_order.every(column => column.match(/^("[a-zA-Z_]+"|[a-zA-Z_]+) (ASC|DESC)$/))) { + if (!_order.every(column => column.match(/^("[a-zA-Z_]\w*"|[a-zA-Z_]\w*) (ASC|DESC)$/))) { throw new Error('Invalid query: ORDER BY clause must contain valid column names followed by ASC or DESC') } diff --git a/test/unit/assertSafeQuery.test.ts b/test/unit/assertSafeQuery.test.ts index 0e80efb3b..c8d75f5e4 100644 --- a/test/unit/assertSafeQuery.test.ts +++ b/test/unit/assertSafeQuery.test.ts @@ -29,6 +29,10 @@ describe('decompressSQLDump', () => { 'SELECT * FROM _content_test ORDER BY id ASC, stem DESC': true, 'SELECT * FROM _content_test ORDER BY id ASC, publishedAt DESC': true, 'SELECT "PublishedAt" FROM _content_test ORDER BY id ASC, PublishedAt DESC': true, + // Digits are valid in quoted column names (schema fields such as `h1` or `publishedAt2`) + 'SELECT * FROM _content_test ORDER BY "field1" DESC': true, + 'SELECT * FROM _content_test ORDER BY id ASC, publishedAt2 DESC': true, + 'SELECT * FROM _content_test ORDER BY "a b" DESC': false, 'SELECT * FROM _content_test ORDER BY id DESC -- comment is not allowed': false, 'SELECT * FROM _content_test ORDER BY id DESC; SELECT * FROM _content_test ORDER BY id DESC': false, 'SELECT * FROM _content_test ORDER BY id DESC LIMIT 10': true, @@ -162,6 +166,9 @@ describe('decompressSQLDump', () => { await collectionQueryBuilder(mockCollection, mockFetch).order('stem', 'DESC').first() expect(() => assertSafeQuery(mockFetch.mock.lastCall![1], mockCollection)).not.toThrow() + await collectionQueryBuilder(mockCollection, mockFetch).order('field1' as never, 'DESC').first() + expect(() => assertSafeQuery(mockFetch.mock.lastCall![1], mockCollection)).not.toThrow() + await collectionQueryBuilder(mockCollection, mockFetch).order('stem', 'DESC').order('id', 'ASC').first() expect(() => assertSafeQuery(mockFetch.mock.lastCall![1], mockCollection)).not.toThrow()