Skip to content

Commit 00d4872

Browse files
committed
fix(knowledge): reject tag-filter operators invalid for the field type
Greptile P2: documentTagFilterSchema accepted any non-empty operator string, so an unsupported operator was silently dropped by the query builder instead of returning 400. Validate the operator against the field type's allowed set (single source of truth in filters/types) via superRefine.
1 parent 1d33330 commit 00d4872

2 files changed

Lines changed: 38 additions & 7 deletions

File tree

apps/sim/lib/api/contracts/knowledge/documents.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,21 @@ describe('parseDocumentTagFiltersParam', () => {
4343
it('throws when the shape is wrong', () => {
4444
expect(() => parseDocumentTagFiltersParam(JSON.stringify([{ tagSlot: '' }]))).toThrow()
4545
})
46+
47+
it('rejects an operator that is not valid for the field type', () => {
48+
// unknown operator
49+
expect(() =>
50+
parseDocumentTagFiltersParam(
51+
JSON.stringify([{ tagSlot: 'tag1', fieldType: 'text', operator: 'bogus', value: 'x' }])
52+
)
53+
).toThrow()
54+
// valid operator name, wrong field type (contains is text-only)
55+
expect(() =>
56+
parseDocumentTagFiltersParam(
57+
JSON.stringify([
58+
{ tagSlot: 'number1', fieldType: 'number', operator: 'contains', value: '1' },
59+
])
60+
)
61+
).toThrow()
62+
})
4663
})

apps/sim/lib/api/contracts/knowledge/documents.ts

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,28 @@ import {
1313
wireDateSchema,
1414
} from '@/lib/api/contracts/knowledge/shared'
1515
import { defineRouteContract } from '@/lib/api/contracts/types'
16+
import { getOperatorsForFieldType } from '@/lib/knowledge/filters/types'
1617

17-
export const documentTagFilterSchema = z.object({
18-
tagSlot: z.string().min(1),
19-
fieldType: z.enum(['text', 'number', 'date', 'boolean']),
20-
operator: z.string().min(1),
21-
value: z.unknown(),
22-
valueTo: z.unknown().optional(),
23-
})
18+
export const documentTagFilterSchema = z
19+
.object({
20+
tagSlot: z.string().min(1),
21+
fieldType: z.enum(['text', 'number', 'date', 'boolean']),
22+
operator: z.string().min(1),
23+
value: z.unknown(),
24+
valueTo: z.unknown().optional(),
25+
})
26+
.superRefine((filter, ctx) => {
27+
// Reject operators that aren't valid for the field type so a bad operator
28+
// returns a 400 instead of being silently dropped by the query builder.
29+
const validOperators = getOperatorsForFieldType(filter.fieldType).map((op) => op.value)
30+
if (!validOperators.includes(filter.operator)) {
31+
ctx.addIssue({
32+
code: 'custom',
33+
path: ['operator'],
34+
message: `Unsupported operator "${filter.operator}" for a ${filter.fieldType} tag filter`,
35+
})
36+
}
37+
})
2438
export type DocumentTagFilter = z.output<typeof documentTagFilterSchema>
2539

2640
export const listKnowledgeDocumentsQuerySchema = z.object({

0 commit comments

Comments
 (0)