From fe11b137463e97811214823ac7fd8e9310d6bcd1 Mon Sep 17 00:00:00 2001 From: "opensearch-trigger-bot[bot]" <98922864+opensearch-trigger-bot[bot]@users.noreply.github.com> Date: Mon, 28 Oct 2024 16:27:09 +0800 Subject: [PATCH] [Backport 2.18] Setting validation for transform APIs (#1195) * Setting validation for transform APIs (#1191) * Enabling validation for transform APIs Signed-off-by: Kshitij Tandon * Updating snap files Signed-off-by: Kshitij Tandon * Reverting some of the updated snap files from previous commit Signed-off-by: Kshitij Tandon * Reverting some changes to test workflows Signed-off-by: Kshitij Tandon * Change workflows java version to 21 from 11 Signed-off-by: Kshitij Tandon * Reverting some changes done just to test the workflows Signed-off-by: Kshitij Tandon * Updating vaidation rules and tests Signed-off-by: Kshitij Tandon * Fixing an issue with API validation Signed-off-by: Kshitij Tandon --------- Signed-off-by: Kshitij Tandon (cherry picked from commit fd0e765651bdca566878ecd23df51ba124358d6b) Signed-off-by: github-actions[bot] * feat: use fixed version Signed-off-by: SuZhou-Joe * feat: update Signed-off-by: SuZhou-Joe * feat: update Signed-off-by: SuZhou-Joe --------- Signed-off-by: Kshitij Tandon Signed-off-by: github-actions[bot] Signed-off-by: SuZhou-Joe Co-authored-by: github-actions[bot] Co-authored-by: SuZhou-Joe --- .github/workflows/cypress-workflow.yml | 4 +- server/routes/transforms.ts | 63 ++++++++++++++++++++++-- server/services/TransformService.test.ts | 56 +++++++++++++++++++++ server/services/TransformService.ts | 2 +- 4 files changed, 118 insertions(+), 7 deletions(-) create mode 100644 server/services/TransformService.test.ts diff --git a/.github/workflows/cypress-workflow.yml b/.github/workflows/cypress-workflow.yml index a727f7b1e..2924839ea 100644 --- a/.github/workflows/cypress-workflow.yml +++ b/.github/workflows/cypress-workflow.yml @@ -22,13 +22,13 @@ jobs: uses: actions/setup-java@v1 with: # TODO: Parse this from index management plugin - java-version: 11 + java-version: 21 - name: Checkout index management uses: actions/checkout@v2 with: path: index-management repository: opensearch-project/index-management - ref: '2.x' + ref: '2.18' - name: Run opensearch with plugin run: | cd index-management diff --git a/server/routes/transforms.ts b/server/routes/transforms.ts index a28aa00c1..bb5b1da97 100644 --- a/server/routes/transforms.ts +++ b/server/routes/transforms.ts @@ -111,12 +111,27 @@ export default function (services: NodeServices, router: IRouter, dataSourceEnab path: `${NODE_API._SEARCH_SAMPLE_DATA}/{index}`, validate: { params: schema.object({ - index: schema.string(), + index: schema.string({ + validate: (value) => { + const invalidCharactersPattern = /[\s,:\"*+\/\\|?#><]/; + if (value !== value.toLowerCase() || value.startsWith("_") || value.startsWith("-") || invalidCharactersPattern.test(value)) { + return "Invalid index name."; + } + + return undefined; + }, + }), }), query: schema.object({ from: schema.number(), size: schema.number(), - ...(dataSourceEnabled ? { dataSourceId: schema.string() } : {}), + ...(dataSourceEnabled + ? { + dataSourceId: schema.string({ + maxLength: 100000, + }), + } + : {}), }), body: schema.any(), }, @@ -129,10 +144,50 @@ export default function (services: NodeServices, router: IRouter, dataSourceEnab path: `${NODE_API.TRANSFORMS}/_preview`, validate: { body: schema.object({ - transform: schema.any(), + transform: schema.object( + { + source_index: schema.string({ + validate: (value) => { + const invalidCharactersPattern = /[\s,:\"*+\/\\|?#><]/; + if ( + value !== value.toLowerCase() || + value.startsWith("_") || + value.startsWith("-") || + invalidCharactersPattern.test(value) + ) { + return "Invalid index name."; + } + + return undefined; + }, + }), + target_index: schema.string({ + validate: (value) => { + const invalidCharactersPattern = /[\s,:\"*+\/\\|?#><]/; + if ( + value !== value.toLowerCase() || + value.startsWith("_") || + value.startsWith("-") || + invalidCharactersPattern.test(value) + ) { + return "Invalid index name."; + } + + return undefined; + }, + }), + }, + { unknowns: "allow" } + ), }), query: schema.object({ - ...(dataSourceEnabled ? { dataSourceId: schema.string() } : {}), + ...(dataSourceEnabled + ? { + dataSourceId: schema.string({ + maxLength: 100000, + }), + } + : {}), }), }, }, diff --git a/server/services/TransformService.test.ts b/server/services/TransformService.test.ts new file mode 100644 index 000000000..7c7208b5f --- /dev/null +++ b/server/services/TransformService.test.ts @@ -0,0 +1,56 @@ +import { schema } from "@osd/config-schema"; + +describe("Index Name Validation", () => { + const validateSchema = schema.object({ + indexName: schema.string({ + validate: (value) => { + const invalidCharactersPattern = /[\s,:\"*+\/\\|?#><]/; + if (value !== value.toLowerCase() || value.startsWith("_") || value.startsWith("-") || invalidCharactersPattern.test(value)) { + return "Invalid index name."; + } + + return undefined; + }, + }), + dataSourceId: schema.maybe(schema.string()), + }); + + const validateQuery = (indexName: string) => { + try { + validateSchema.validate({ indexName }); + return undefined; + } catch (e) { + return e.message; + } + }; + + it("should fail validation for index names with uppercase letters", () => { + const errorMessage = validateQuery("IndexNameWithUppercase"); + expect(errorMessage).toBe("[indexName]: Invalid index name."); + }); + + it("should fail validation for index names starting with an underscore", () => { + const errorMessage = validateQuery("_indexname"); + expect(errorMessage).toBe("[indexName]: Invalid index name."); + }); + + it("should fail validation for index names starting with a hyphen", () => { + const errorMessage = validateQuery("-indexname"); + expect(errorMessage).toBe("[indexName]: Invalid index name."); + }); + + it("should fail validation for index names containing invalid characters", () => { + const errorMessage = validateQuery("********************************"); + expect(errorMessage).toBe("[indexName]: Invalid index name."); + }); + + it("should pass validation for valid index names", () => { + const errorMessage = validateQuery("valid_index-name123"); + expect(errorMessage).toBeUndefined(); + }); + + it("should fail validation for index names containing spaces", () => { + const errorMessage = validateQuery("invalid index"); + expect(errorMessage).toBe("[indexName]: Invalid index name."); + }); +}); diff --git a/server/services/TransformService.ts b/server/services/TransformService.ts index 87b09ac79..c4cb8d35e 100644 --- a/server/services/TransformService.ts +++ b/server/services/TransformService.ts @@ -334,7 +334,7 @@ export default class TransformService extends MDSEnabledClientService { }, }); } catch (err) { - if (err.statusCode === 404 && err.body.error.type === "index_not_found_exception") { + if (err.statusCode === 404 && err.body?.error?.type === "index_not_found_exception") { return response.custom({ statusCode: 200, body: {