From d6e1bcb3829fafba06a95b9f66230021524363f0 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 7 Aug 2024 13:56:25 +0200 Subject: [PATCH 1/8] Type search validators --- packages/server/src/api/routes/utils/validators.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/server/src/api/routes/utils/validators.ts b/packages/server/src/api/routes/utils/validators.ts index 671ce95038..0322120ecf 100644 --- a/packages/server/src/api/routes/utils/validators.ts +++ b/packages/server/src/api/routes/utils/validators.ts @@ -1,6 +1,6 @@ import { auth, permissions } from "@budibase/backend-core" import { DataSourceOperation } from "../../../constants" -import { Table, WebhookActionType } from "@budibase/types" +import { SearchFilters, Table, WebhookActionType } from "@budibase/types" import Joi, { CustomValidator } from "joi" import { ValidSnippetNameRegex, helpers } from "@budibase/shared-core" import sdk from "../../../sdk" @@ -84,7 +84,7 @@ export function datasourceValidator() { } function filterObject() { - return Joi.object({ + const filtersValidators: Record = { string: Joi.object().optional(), fuzzy: Joi.object().optional(), range: Joi.object().optional(), @@ -96,7 +96,8 @@ function filterObject() { contains: Joi.object().optional(), notContains: Joi.object().optional(), allOr: Joi.boolean().optional(), - }).unknown(true) + } + return Joi.object(filtersValidators).unknown(true) } export function internalSearchValidator() { From 8d1c658c7c744f0a3a7d984600b8830c91d6075d Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 7 Aug 2024 13:57:34 +0200 Subject: [PATCH 2/8] Add containsAny validator --- packages/server/src/api/routes/utils/validators.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/server/src/api/routes/utils/validators.ts b/packages/server/src/api/routes/utils/validators.ts index 0322120ecf..9b9a763e2c 100644 --- a/packages/server/src/api/routes/utils/validators.ts +++ b/packages/server/src/api/routes/utils/validators.ts @@ -95,6 +95,7 @@ function filterObject() { oneOf: Joi.object().optional(), contains: Joi.object().optional(), notContains: Joi.object().optional(), + containsAny: Joi.object().optional(), allOr: Joi.boolean().optional(), } return Joi.object(filtersValidators).unknown(true) From c6f7f0133dcb52aa77ee0e5c906a5ce2d0b19695 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 7 Aug 2024 13:57:54 +0200 Subject: [PATCH 3/8] Disallow fuzzyOr and documentType --- packages/server/src/api/routes/utils/validators.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/server/src/api/routes/utils/validators.ts b/packages/server/src/api/routes/utils/validators.ts index 9b9a763e2c..ddd98f3858 100644 --- a/packages/server/src/api/routes/utils/validators.ts +++ b/packages/server/src/api/routes/utils/validators.ts @@ -97,6 +97,8 @@ function filterObject() { notContains: Joi.object().optional(), containsAny: Joi.object().optional(), allOr: Joi.boolean().optional(), + fuzzyOr: Joi.disallow(), + documentType: Joi.disallow(), } return Joi.object(filtersValidators).unknown(true) } From 22d9b930fc280c7ca129f05aae1d5b6232ee1f8b Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 7 Aug 2024 14:02:09 +0200 Subject: [PATCH 4/8] Validate onEmptyFilter --- packages/server/src/api/routes/utils/validators.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/server/src/api/routes/utils/validators.ts b/packages/server/src/api/routes/utils/validators.ts index ddd98f3858..72c97396db 100644 --- a/packages/server/src/api/routes/utils/validators.ts +++ b/packages/server/src/api/routes/utils/validators.ts @@ -1,6 +1,11 @@ import { auth, permissions } from "@budibase/backend-core" import { DataSourceOperation } from "../../../constants" -import { SearchFilters, Table, WebhookActionType } from "@budibase/types" +import { + EmptyFilterOption, + SearchFilters, + Table, + WebhookActionType, +} from "@budibase/types" import Joi, { CustomValidator } from "joi" import { ValidSnippetNameRegex, helpers } from "@budibase/shared-core" import sdk from "../../../sdk" @@ -97,6 +102,9 @@ function filterObject() { notContains: Joi.object().optional(), containsAny: Joi.object().optional(), allOr: Joi.boolean().optional(), + onEmptyFilter: Joi.string() + .optional() + .valid(...Object.values(EmptyFilterOption)), fuzzyOr: Joi.disallow(), documentType: Joi.disallow(), } From 00c12b9686dd07d6a466d099ae6b90ccc49e62a8 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 7 Aug 2024 14:32:42 +0200 Subject: [PATCH 5/8] Add tests --- .../src/api/routes/tests/search.spec.ts | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/server/src/api/routes/tests/search.spec.ts b/packages/server/src/api/routes/tests/search.spec.ts index e3f17c36cb..05f9e1676e 100644 --- a/packages/server/src/api/routes/tests/search.spec.ts +++ b/packages/server/src/api/routes/tests/search.spec.ts @@ -2769,6 +2769,37 @@ describe.each([ }, }).toFindNothing() }) + + it("validates conditions that are not objects", async () => { + await expect( + expectQuery({ + $and: { + conditions: [{ equal: { age: 10 } }, "invalidCondition" as any], + }, + }).toFindNothing() + ).rejects.toThrow( + 'Invalid body - "query.$and.conditions[1]" must be of type object' + ) + }) + + it("validates $and without conditions", async () => { + await expect( + expectQuery({ + $and: { + conditions: [ + { equal: { age: 10 } }, + { + $and: { + conditions: undefined as any, + }, + }, + ], + }, + }).toFindNothing() + ).rejects.toThrow( + 'Invalid body - "query.$and.conditions[1].$and.conditions" is required' + ) + }) }) !isLucene && From bc7ab264b0500410f18a93f2a9e0bddd0893f125 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 7 Aug 2024 14:32:59 +0200 Subject: [PATCH 6/8] Add validations --- packages/server/src/api/routes/utils/validators.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/server/src/api/routes/utils/validators.ts b/packages/server/src/api/routes/utils/validators.ts index 72c97396db..9aa112cf4d 100644 --- a/packages/server/src/api/routes/utils/validators.ts +++ b/packages/server/src/api/routes/utils/validators.ts @@ -89,6 +89,11 @@ export function datasourceValidator() { } function filterObject() { + const conditionalFilteringObject = () => + Joi.object({ + conditions: Joi.array().items(Joi.link("#schema")).required(), + }) + const filtersValidators: Record = { string: Joi.object().optional(), fuzzy: Joi.object().optional(), @@ -105,10 +110,12 @@ function filterObject() { onEmptyFilter: Joi.string() .optional() .valid(...Object.values(EmptyFilterOption)), - fuzzyOr: Joi.disallow(), - documentType: Joi.disallow(), + $and: conditionalFilteringObject(), + $or: conditionalFilteringObject(), + fuzzyOr: Joi.forbidden(), + documentType: Joi.forbidden(), } - return Joi.object(filtersValidators).unknown(true) + return Joi.object(filtersValidators).unknown(true).id("schema") } export function internalSearchValidator() { From 32702f2e9db4f7b53df67b9775a6733eee2a1353 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 7 Aug 2024 14:39:05 +0200 Subject: [PATCH 7/8] Don't validate for in-memory --- packages/server/src/api/routes/tests/search.spec.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/server/src/api/routes/tests/search.spec.ts b/packages/server/src/api/routes/tests/search.spec.ts index 05f9e1676e..a92e2d3164 100644 --- a/packages/server/src/api/routes/tests/search.spec.ts +++ b/packages/server/src/api/routes/tests/search.spec.ts @@ -2770,6 +2770,7 @@ describe.each([ }).toFindNothing() }) + !isInMemory && it("validates conditions that are not objects", async () => { await expect( expectQuery({ @@ -2782,6 +2783,7 @@ describe.each([ ) }) + !isInMemory && it("validates $and without conditions", async () => { await expect( expectQuery({ From 44a053ee0812e974d10aea4d7fd844aace9529bb Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 7 Aug 2024 14:40:23 +0200 Subject: [PATCH 8/8] Lint --- .../src/api/routes/tests/search.spec.ts | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/packages/server/src/api/routes/tests/search.spec.ts b/packages/server/src/api/routes/tests/search.spec.ts index a92e2d3164..9f0a9e6269 100644 --- a/packages/server/src/api/routes/tests/search.spec.ts +++ b/packages/server/src/api/routes/tests/search.spec.ts @@ -2771,37 +2771,37 @@ describe.each([ }) !isInMemory && - it("validates conditions that are not objects", async () => { - await expect( - expectQuery({ - $and: { - conditions: [{ equal: { age: 10 } }, "invalidCondition" as any], - }, - }).toFindNothing() - ).rejects.toThrow( - 'Invalid body - "query.$and.conditions[1]" must be of type object' - ) - }) + it("validates conditions that are not objects", async () => { + await expect( + expectQuery({ + $and: { + conditions: [{ equal: { age: 10 } }, "invalidCondition" as any], + }, + }).toFindNothing() + ).rejects.toThrow( + 'Invalid body - "query.$and.conditions[1]" must be of type object' + ) + }) !isInMemory && - it("validates $and without conditions", async () => { - await expect( - expectQuery({ - $and: { - conditions: [ - { equal: { age: 10 } }, - { - $and: { - conditions: undefined as any, + it("validates $and without conditions", async () => { + await expect( + expectQuery({ + $and: { + conditions: [ + { equal: { age: 10 } }, + { + $and: { + conditions: undefined as any, + }, }, - }, - ], - }, - }).toFindNothing() - ).rejects.toThrow( - 'Invalid body - "query.$and.conditions[1].$and.conditions" is required' - ) - }) + ], + }, + }).toFindNothing() + ).rejects.toThrow( + 'Invalid body - "query.$and.conditions[1].$and.conditions" is required' + ) + }) }) !isLucene &&