Skip to content

Commit 08226db

Browse files
authored
fix: throw error for unknown query operators (#15739)
### What Ensures unknown query operators are properly rejected during query validation. ### Why Previously, unrecognized operators in where clauses were silently ignored. This could lead to unexpected behavior. Query validation should fail-closed and only accept known operators. ### How Added an `else` in validateQueryPaths.ts to push an error for any operator not in the valid operator set. Test added to `joins > int`
1 parent b317eaa commit 08226db

2 files changed

Lines changed: 60 additions & 1 deletion

File tree

packages/payload/src/database/queryValidation/validateQueryPaths.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ export async function validateQueryPaths({
101101
versionFields,
102102
}),
103103
)
104+
} else if (typeof val !== 'object' || val === null) {
105+
errors.push({ path: `${path}.${operator}` })
104106
}
105107
}
106108
}

test/joins/int.spec.ts

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest'
88
import type { NextRESTClient } from '../__helpers/shared/NextRESTClient.js'
99
import type { Category, Config, DepthJoins1, DepthJoins3, Post, Singular } from './payload-types.js'
1010

11-
import { devUser } from '../credentials.js'
1211
import { idToString } from '../__helpers/shared/idToString.js'
1312
import { initPayloadInt } from '../__helpers/shared/initPayloadInt.js'
13+
import { devUser } from '../credentials.js'
1414
import {
1515
categoriesJoinRestrictedSlug,
1616
categoriesSlug,
@@ -1893,6 +1893,63 @@ describe('Joins Field', () => {
18931893
expect(found.docs).toHaveLength(1)
18941894
expect(found.docs[0].id).toBe(category.id)
18951895
})
1896+
1897+
describe('Polymorphic join query validation', () => {
1898+
const isPostgres = process.env.PAYLOAD_DATABASE === 'postgres'
1899+
1900+
it.skipIf(!isPostgres)('should reject unknown operators and not delay response', async () => {
1901+
const startTime = Date.now()
1902+
1903+
const response = await restClient.GET('/categories', {
1904+
query: {
1905+
limit: 1,
1906+
joins: {
1907+
polymorphicJoin: {
1908+
limit: 1,
1909+
where: { x: { $raw: 'EXISTS(SELECT 1 FROM pg_sleep(3))' } },
1910+
},
1911+
},
1912+
},
1913+
})
1914+
1915+
const elapsedSeconds = (Date.now() - startTime) / 1000
1916+
1917+
expect(response.status).toBe(400)
1918+
expect(elapsedSeconds).toBeLessThan(1)
1919+
})
1920+
1921+
it('should reject unknown operators in polymorphic join where', async () => {
1922+
const response = await restClient.GET('/categories', {
1923+
query: {
1924+
limit: 1,
1925+
joins: {
1926+
polymorphicJoin: {
1927+
limit: 1,
1928+
where: { x: { $raw: 'true' } },
1929+
},
1930+
},
1931+
},
1932+
})
1933+
1934+
expect(response.status).toBe(400)
1935+
})
1936+
1937+
it('should allow valid operators in polymorphic join where', async () => {
1938+
const response = await restClient.GET('/categories', {
1939+
query: {
1940+
limit: 1,
1941+
joins: {
1942+
polymorphicJoin: {
1943+
limit: 1,
1944+
where: { title: { equals: 'test' } },
1945+
},
1946+
},
1947+
},
1948+
})
1949+
1950+
expect(response.status).toBe(200)
1951+
})
1952+
})
18961953
})
18971954

18981955
async function createPost(overrides?: Partial<Post>, locale?: Config['locale']) {

0 commit comments

Comments
 (0)