From 4e20735f986ff0381b7301c40ae279eee75b0ae1 Mon Sep 17 00:00:00 2001 From: Akhil Mohan Date: Mon, 29 Jan 2024 00:21:42 +0530 Subject: [PATCH] feat: resolved trailing slash in secret paths --- backend/src/ee/routes/v1/project-router.ts | 5 ++-- .../ee/routes/v1/secret-rotation-router.ts | 3 +- backend/src/lib/fn/string.ts | 6 ++++ .../server/routes/v1/integration-router.ts | 6 ++-- .../server/routes/v1/secret-folder-router.ts | 17 ++++++----- .../server/routes/v1/secret-import-router.ts | 19 +++++++----- .../src/server/routes/v1/webhook-router.ts | 5 ++-- .../server/routes/v2/service-token-router.ts | 3 +- backend/src/server/routes/v3/secret-router.ts | 29 ++++++++++--------- .../secret-folder/secret-folder-dal.ts | 26 +++++++++++++---- 10 files changed, 75 insertions(+), 44 deletions(-) diff --git a/backend/src/ee/routes/v1/project-router.ts b/backend/src/ee/routes/v1/project-router.ts index 294150c837..3870123fd6 100644 --- a/backend/src/ee/routes/v1/project-router.ts +++ b/backend/src/ee/routes/v1/project-router.ts @@ -2,6 +2,7 @@ import { z } from "zod"; import { AuditLogsSchema, SecretSnapshotsSchema } from "@app/db/schemas"; import { EventType, UserAgentType } from "@app/ee/services/audit-log/audit-log-types"; +import { removeTrailingSlash } from "@app/lib/fn"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; @@ -15,7 +16,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { }), querystring: z.object({ environment: z.string().trim(), - path: z.string().trim().default("/"), + path: z.string().trim().default("/").transform(removeTrailingSlash), offset: z.coerce.number().default(0), limit: z.coerce.number().default(20) }), @@ -46,7 +47,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { }), querystring: z.object({ environment: z.string().trim(), - path: z.string().trim().default("/") + path: z.string().trim().default("/").transform(removeTrailingSlash) }), response: { 200: z.object({ diff --git a/backend/src/ee/routes/v1/secret-rotation-router.ts b/backend/src/ee/routes/v1/secret-rotation-router.ts index 95eb0bc880..062c519805 100644 --- a/backend/src/ee/routes/v1/secret-rotation-router.ts +++ b/backend/src/ee/routes/v1/secret-rotation-router.ts @@ -1,6 +1,7 @@ import { z } from "zod"; import { SecretRotationOutputsSchema, SecretRotationsSchema, SecretsSchema } from "@app/db/schemas"; +import { removeTrailingSlash } from "@app/lib/fn"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; @@ -11,7 +12,7 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) = schema: { body: z.object({ workspaceId: z.string().trim(), - secretPath: z.string().trim(), + secretPath: z.string().trim().transform(removeTrailingSlash), environment: z.string().trim(), interval: z.number().min(1), provider: z.string().trim(), diff --git a/backend/src/lib/fn/string.ts b/backend/src/lib/fn/string.ts index 27d84357d0..2d7466c35c 100644 --- a/backend/src/lib/fn/string.ts +++ b/backend/src/lib/fn/string.ts @@ -3,3 +3,9 @@ import path from "path"; // given two paths irrespective of ending with / or not // this will return true if its equal export const isSamePath = async (from: string, to: string) => !path.relative(from, to); + +export const removeTrailingSlash = (str: string) => { + if (str === "/") return str; + + return str.endsWith("/") ? str.slice(0, -1) : str; +}; diff --git a/backend/src/server/routes/v1/integration-router.ts b/backend/src/server/routes/v1/integration-router.ts index 23d8ef8bed..db73bf293e 100644 --- a/backend/src/server/routes/v1/integration-router.ts +++ b/backend/src/server/routes/v1/integration-router.ts @@ -2,7 +2,7 @@ import { z } from "zod"; import { IntegrationsSchema } from "@app/db/schemas"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; -import { shake } from "@app/lib/fn"; +import { removeTrailingSlash, shake } from "@app/lib/fn"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; @@ -16,7 +16,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { app: z.string().trim().optional(), isActive: z.boolean(), appId: z.string().trim().optional(), - secretPath: z.string().trim().default("/"), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), sourceEnvironment: z.string().trim(), targetEnvironment: z.string().trim().optional(), targetEnvironmentId: z.string().trim().optional(), @@ -89,7 +89,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { app: z.string().trim(), appId: z.string().trim(), isActive: z.boolean(), - secretPath: z.string().trim().default("/"), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), targetEnvironment: z.string().trim(), owner: z.string().trim(), environment: z.string().trim() diff --git a/backend/src/server/routes/v1/secret-folder-router.ts b/backend/src/server/routes/v1/secret-folder-router.ts index fd9798dac5..c717678682 100644 --- a/backend/src/server/routes/v1/secret-folder-router.ts +++ b/backend/src/server/routes/v1/secret-folder-router.ts @@ -2,6 +2,7 @@ import { z } from "zod"; import { SecretFoldersSchema } from "@app/db/schemas"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; +import { removeTrailingSlash } from "@app/lib/fn"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; @@ -14,9 +15,9 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) => workspaceId: z.string().trim(), environment: z.string().trim(), name: z.string().trim(), - path: z.string().trim().default("/"), + path: z.string().trim().default("/").transform(removeTrailingSlash), // backward compatiability with cli - directory: z.string().trim().default("/") + directory: z.string().trim().default("/").transform(removeTrailingSlash) }), response: { 200: z.object({ @@ -68,9 +69,9 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) => workspaceId: z.string().trim(), environment: z.string().trim(), name: z.string().trim(), - path: z.string().trim().default("/"), + path: z.string().trim().default("/").transform(removeTrailingSlash), // backward compatiability with cli - directory: z.string().trim().default("/") + directory: z.string().trim().default("/").transform(removeTrailingSlash) }), response: { 200: z.object({ @@ -122,9 +123,9 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) => body: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - path: z.string().trim().default("/"), + path: z.string().trim().default("/").transform(removeTrailingSlash), // keep this here as cli need directory - directory: z.string().trim().default("/") + directory: z.string().trim().default("/").transform(removeTrailingSlash) }), response: { 200: z.object({ @@ -172,9 +173,9 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) => querystring: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - path: z.string().trim().default("/"), + path: z.string().trim().default("/").transform(removeTrailingSlash), // backward compatiability with cli - directory: z.string().trim().default("/") + directory: z.string().trim().default("/").transform(removeTrailingSlash) }), response: { 200: z.object({ diff --git a/backend/src/server/routes/v1/secret-import-router.ts b/backend/src/server/routes/v1/secret-import-router.ts index 5f37c5f5e4..f27f9e0c2e 100644 --- a/backend/src/server/routes/v1/secret-import-router.ts +++ b/backend/src/server/routes/v1/secret-import-router.ts @@ -2,6 +2,7 @@ import { z } from "zod"; import { SecretImportsSchema, SecretsSchema } from "@app/db/schemas"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; +import { removeTrailingSlash } from "@app/lib/fn"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; @@ -13,10 +14,10 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => body: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - path: z.string().trim().default("/"), + path: z.string().trim().default("/").transform(removeTrailingSlash), import: z.object({ environment: z.string().trim(), - path: z.string().trim() + path: z.string().trim().transform(removeTrailingSlash) }) }), response: { @@ -74,10 +75,14 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => body: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - path: z.string().trim().default("/"), + path: z.string().trim().default("/").transform(removeTrailingSlash), import: z.object({ environment: z.string().trim().optional(), - path: z.string().trim().optional(), + path: z + .string() + .trim() + .optional() + .transform((val) => (val ? removeTrailingSlash(val) : val)), position: z.number().optional() }) }), @@ -137,7 +142,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => body: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - path: z.string().trim().default("/") + path: z.string().trim().default("/").transform(removeTrailingSlash) }), response: { 200: z.object({ @@ -191,7 +196,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => querystring: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - path: z.string().trim().default("/") + path: z.string().trim().default("/").transform(removeTrailingSlash) }), response: { 200: z.object({ @@ -243,7 +248,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) => querystring: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - path: z.string().trim().default("/") + path: z.string().trim().default("/").transform(removeTrailingSlash) }), response: { 200: z.object({ diff --git a/backend/src/server/routes/v1/webhook-router.ts b/backend/src/server/routes/v1/webhook-router.ts index 89564d9a3f..558192f68b 100644 --- a/backend/src/server/routes/v1/webhook-router.ts +++ b/backend/src/server/routes/v1/webhook-router.ts @@ -2,6 +2,7 @@ import { z } from "zod"; import { WebhooksSchema } from "@app/db/schemas"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; +import { removeTrailingSlash } from "@app/lib/fn"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; @@ -33,7 +34,7 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => { environment: z.string().trim(), webhookUrl: z.string().url().trim(), webhookSecretKey: z.string().trim().optional(), - secretPath: z.string().trim().default("/") + secretPath: z.string().trim().default("/").transform(removeTrailingSlash) }), response: { 200: z.object({ @@ -182,7 +183,7 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => { querystring: z.object({ workspaceId: z.string().trim(), environment: z.string().trim().optional(), - secretPath: z.string().trim().optional() + secretPath: z.string().trim().optional().transform((val)=> val?removeTrailingSlash(val):val) }), response: { 200: z.object({ diff --git a/backend/src/server/routes/v2/service-token-router.ts b/backend/src/server/routes/v2/service-token-router.ts index 950720c4d7..31f5c22d67 100644 --- a/backend/src/server/routes/v2/service-token-router.ts +++ b/backend/src/server/routes/v2/service-token-router.ts @@ -2,6 +2,7 @@ import { z } from "zod"; import { ServiceTokensSchema } from "@app/db/schemas"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; +import { removeTrailingSlash } from "@app/lib/fn"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; @@ -42,7 +43,7 @@ export const registerServiceTokenRouter = async (server: FastifyZodProvider) => scopes: z .object({ environment: z.string().trim(), - secretPath: z.string().trim() + secretPath: z.string().trim().transform(removeTrailingSlash) }) .array() .min(1), diff --git a/backend/src/server/routes/v3/secret-router.ts b/backend/src/server/routes/v3/secret-router.ts index 04d187aefe..cdb92fae65 100644 --- a/backend/src/server/routes/v3/secret-router.ts +++ b/backend/src/server/routes/v3/secret-router.ts @@ -12,6 +12,7 @@ import { import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { CommitType } from "@app/ee/services/secret-approval-request/secret-approval-request-types"; import { BadRequestError } from "@app/lib/errors"; +import { removeTrailingSlash } from "@app/lib/fn"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { ActorType, AuthMode } from "@app/services/auth/auth-type"; import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types"; @@ -39,7 +40,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { querystring: z.object({ workspaceId: z.string().trim().optional(), environment: z.string().trim().optional(), - secretPath: z.string().trim().default("/"), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), include_imports: z .enum(["true", "false"]) .default("false") @@ -129,7 +130,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { querystring: z.object({ workspaceId: z.string().trim().optional(), environment: z.string().trim().optional(), - secretPath: z.string().trim().default("/"), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), version: z.coerce.number().optional(), type: z.nativeEnum(SecretType).default(SecretType.Shared), include_imports: z @@ -216,7 +217,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { body: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - secretPath: z.string().trim().default("/"), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), secretValue: z .string() .transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim())), @@ -256,7 +257,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { event: { type: EventType.CREATE_SECRET, metadata: { - environment: req.body.environment, + environment: req.body.environment, secretPath: req.body.secretPath, secretId: secret.id, secretKey: req.params.secretName, @@ -295,7 +296,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { secretValue: z .string() .transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim())), - secretPath: z.string().trim().default("/"), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), skipMultilineEncoding: z.boolean().optional(), type: z.nativeEnum(SecretType).default(SecretType.Shared) }), @@ -365,7 +366,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { body: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - secretPath: z.string().trim().default("/"), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), type: z.nativeEnum(SecretType).default(SecretType.Shared) }), response: { @@ -430,7 +431,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { querystring: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - secretPath: z.string().trim().default("/"), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), include_imports: z .enum(["true", "false"]) .default("false") @@ -518,7 +519,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { querystring: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - secretPath: z.string().trim().default("/"), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), type: z.nativeEnum(SecretType).default(SecretType.Shared), version: z.coerce.number().optional(), include_imports: z @@ -590,7 +591,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { workspaceId: z.string().trim(), environment: z.string().trim(), type: z.nativeEnum(SecretType).default(SecretType.Shared), - secretPath: z.string().trim().default("/"), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), secretKeyCiphertext: z.string().trim(), secretKeyIV: z.string().trim(), secretKeyTag: z.string().trim(), @@ -758,7 +759,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { environment: z.string().trim(), secretId: z.string().trim().optional(), type: z.nativeEnum(SecretType).default(SecretType.Shared), - secretPath: z.string().trim().default("/"), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), secretValueCiphertext: z.string().trim(), secretValueIV: z.string().trim(), secretValueTag: z.string().trim(), @@ -935,7 +936,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { }), body: z.object({ type: z.nativeEnum(SecretType).default(SecretType.Shared), - secretPath: z.string().trim().default("/"), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), secretId: z.string().trim().optional(), workspaceId: z.string().trim(), environment: z.string().trim() @@ -1050,7 +1051,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { body: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - secretPath: z.string().trim().default("/"), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), secrets: z .object({ secretName: z.string().trim(), @@ -1176,7 +1177,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { body: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - secretPath: z.string().trim().default("/"), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), secrets: z .object({ secretName: z.string().trim(), @@ -1301,7 +1302,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { body: z.object({ workspaceId: z.string().trim(), environment: z.string().trim(), - secretPath: z.string().trim().default("/"), + secretPath: z.string().trim().default("/").transform(removeTrailingSlash), secrets: z .object({ secretName: z.string().trim(), diff --git a/backend/src/services/secret-folder/secret-folder-dal.ts b/backend/src/services/secret-folder/secret-folder-dal.ts index 6129cfa9df..3d25217cb5 100644 --- a/backend/src/services/secret-folder/secret-folder-dal.ts +++ b/backend/src/services/secret-folder/secret-folder-dal.ts @@ -8,7 +8,7 @@ import { TSecretFoldersUpdate } from "@app/db/schemas"; import { BadRequestError, DatabaseError } from "@app/lib/errors"; -import { groupBy } from "@app/lib/fn"; +import { groupBy, removeTrailingSlash } from "@app/lib/fn"; import { ormify, selectAllTableCols } from "@app/lib/knex"; export const validateFolderName = (folderName: string) => { @@ -238,10 +238,15 @@ export const secretFolderDALFactory = (db: TDbClient) => { tx?: Knex ) => { try { - const folder = await sqlFindFolderByPathQuery(tx || db, projectId, environment, path) + const folder = await sqlFindFolderByPathQuery( + tx || db, + projectId, + environment, + removeTrailingSlash(path) + ) .orderBy("depth", "desc") .first(); - if (folder && folder.path !== path) { + if (folder && folder.path !== removeTrailingSlash(path)) { return; } if (!folder) return; @@ -262,7 +267,12 @@ export const secretFolderDALFactory = (db: TDbClient) => { tx?: Knex ) => { try { - const folder = await sqlFindFolderByPathQuery(tx || db, projectId, environment, path) + const folder = await sqlFindFolderByPathQuery( + tx || db, + projectId, + environment, + removeTrailingSlash(path) + ) .orderBy("depth", "desc") .first(); if (!folder) return; @@ -278,8 +288,12 @@ export const secretFolderDALFactory = (db: TDbClient) => { tx?: Knex ) => { try { - const folders = await sqlFindMultipleFolderByEnvPathQuery(tx || db, query); - return query.map(({ envId, secretPath }) => + const formatedQuery = query.map(({ secretPath, envId }) => ({ + envId, + secretPath: removeTrailingSlash(secretPath) + })); + const folders = await sqlFindMultipleFolderByEnvPathQuery(tx || db, formatedQuery); + return formatedQuery.map(({ envId, secretPath }) => folders.find( ({ path: targetPath, envId: targetEnvId }) => targetPath === secretPath && targetEnvId === envId