diff --git a/backend/e2e-test/routes/v3/secret-recursive.spec.ts b/backend/e2e-test/routes/v3/secret-recursive.spec.ts new file mode 100644 index 000000000..b28dff985 --- /dev/null +++ b/backend/e2e-test/routes/v3/secret-recursive.spec.ts @@ -0,0 +1,86 @@ +import { createFolder, deleteFolder } from "e2e-test/testUtils/folders"; +import { createSecretV2, deleteSecretV2, getSecretsV2 } from "e2e-test/testUtils/secrets"; + +import { seedData1 } from "@app/db/seed-data"; + +describe("Secret Recursive Testing", async () => { + const projectId = seedData1.projectV3.id; + const folderAndSecretNames = [ + { name: "deep1", path: "/", expectedSecretCount: 4 }, + { name: "deep21", path: "/deep1", expectedSecretCount: 2 }, + { name: "deep3", path: "/deep1/deep2", expectedSecretCount: 1 }, + { name: "deep22", path: "/deep2", expectedSecretCount: 1 } + ]; + + beforeAll(async () => { + const rootFolderIds: string[] = []; + for (const folder of folderAndSecretNames) { + // eslint-disable-next-line no-await-in-loop + const createdFolder = await createFolder({ + authToken: jwtAuthToken, + environmentSlug: "prod", + workspaceId: projectId, + secretPath: folder.path, + name: folder.name + }); + + if (folder.path === "/") { + rootFolderIds.push(createdFolder.id); + } + // eslint-disable-next-line no-await-in-loop + await createSecretV2({ + secretPath: folder.path, + authToken: jwtAuthToken, + environmentSlug: "prod", + workspaceId: projectId, + key: folder.name, + value: folder.name + }); + } + + return async () => { + await Promise.all( + rootFolderIds.map((id) => + deleteFolder({ + authToken: jwtAuthToken, + secretPath: "/", + id, + workspaceId: projectId, + environmentSlug: "prod" + }) + ) + ); + + await deleteSecretV2({ + authToken: jwtAuthToken, + secretPath: "/", + workspaceId: projectId, + environmentSlug: "prod", + key: folderAndSecretNames[0].name + }); + }; + }); + + test.each(folderAndSecretNames)("$path recursive secret fetching", async ({ path, expectedSecretCount }) => { + const secrets = await getSecretsV2({ + authToken: jwtAuthToken, + secretPath: path, + workspaceId: projectId, + environmentSlug: "prod", + recursive: true + }); + + expect(secrets.secrets.length).toEqual(expectedSecretCount); + expect(secrets.secrets.sort((a, b) => a.secretKey.localeCompare(b.secretKey))).toEqual( + folderAndSecretNames + .filter((el) => el.path.startsWith(path)) + .sort((a, b) => a.name.localeCompare(b.name)) + .map((el) => + expect.objectContaining({ + secretKey: el.name, + secretValue: el.name + }) + ) + ); + }); +}); diff --git a/backend/e2e-test/testUtils/secrets.ts b/backend/e2e-test/testUtils/secrets.ts index 96ecc91c6..8b9e47f2f 100644 --- a/backend/e2e-test/testUtils/secrets.ts +++ b/backend/e2e-test/testUtils/secrets.ts @@ -97,6 +97,7 @@ export const getSecretsV2 = async (dto: { environmentSlug: string; secretPath: string; authToken: string; + recursive?: boolean; }) => { const getSecretsResponse = await testServer.inject({ method: "GET", @@ -109,7 +110,8 @@ export const getSecretsV2 = async (dto: { environment: dto.environmentSlug, secretPath: dto.secretPath, expandSecretReferences: "true", - include_imports: "true" + include_imports: "true", + recursive: String(dto.recursive || false) } }); expect(getSecretsResponse.statusCode).toBe(200); diff --git a/backend/src/services/secret-v2-bridge/secret-v2-bridge-fns.ts b/backend/src/services/secret-v2-bridge/secret-v2-bridge-fns.ts index 95d2cdbf4..79aa22799 100644 --- a/backend/src/services/secret-v2-bridge/secret-v2-bridge-fns.ts +++ b/backend/src/services/secret-v2-bridge/secret-v2-bridge-fns.ts @@ -365,9 +365,8 @@ export const recursivelyGetSecretPaths = async ({ folderId: p.folderId })); - const pathsInCurrentDirectory = paths.filter((folder) => - folder.path.startsWith(currentPath === "/" ? "" : currentPath) - ); + // path relative will start with ../ if its outside directory + const pathsInCurrentDirectory = paths.filter((folder) => !path.relative(currentPath, folder.path).startsWith("..")); return pathsInCurrentDirectory; };