Merge pull request #2313 from akhilmhdh/feat/test-import

Feat/test import
This commit is contained in:
Maidul Islam
2024-09-03 09:33:26 -04:00
committed by GitHub
24 changed files with 2306 additions and 704 deletions
@@ -0,0 +1,35 @@
import { seedData1 } from "@app/db/seed-data";
const createPolicy = async (dto: { name: string; secretPath: string; approvers: string[]; approvals: number }) => {
const res = await testServer.inject({
method: "POST",
url: `/api/v1/secret-approvals`,
headers: {
authorization: `Bearer ${jwtAuthToken}`
},
body: {
workspaceId: seedData1.project.id,
environment: seedData1.environment.slug,
name: dto.name,
secretPath: dto.secretPath,
approvers: dto.approvers,
approvals: dto.approvals
}
});
expect(res.statusCode).toBe(200);
return res.json().approval;
};
describe("Secret approval policy router", async () => {
test("Create policy", async () => {
const policy = await createPolicy({
secretPath: "/",
approvals: 1,
approvers: [seedData1.id],
name: "test-policy"
});
expect(policy.name).toBe("test-policy");
});
});
+666 -64
View File
@@ -1,73 +1,61 @@
import { createFolder, deleteFolder } from "e2e-test/testUtils/folders";
import { createSecretImport, deleteSecretImport } from "e2e-test/testUtils/secret-imports";
import { createSecretV2, deleteSecretV2, getSecretByNameV2, getSecretsV2 } from "e2e-test/testUtils/secrets";
import { seedData1 } from "@app/db/seed-data"; import { seedData1 } from "@app/db/seed-data";
const createSecretImport = async (importPath: string, importEnv: string) => {
const res = await testServer.inject({
method: "POST",
url: `/api/v1/secret-imports`,
headers: {
authorization: `Bearer ${jwtAuthToken}`
},
body: {
workspaceId: seedData1.project.id,
environment: seedData1.environment.slug,
path: "/",
import: {
environment: importEnv,
path: importPath
}
}
});
expect(res.statusCode).toBe(200);
const payload = JSON.parse(res.payload);
expect(payload).toHaveProperty("secretImport");
return payload.secretImport;
};
const deleteSecretImport = async (id: string) => {
const res = await testServer.inject({
method: "DELETE",
url: `/api/v1/secret-imports/${id}`,
headers: {
authorization: `Bearer ${jwtAuthToken}`
},
body: {
workspaceId: seedData1.project.id,
environment: seedData1.environment.slug,
path: "/"
}
});
expect(res.statusCode).toBe(200);
const payload = JSON.parse(res.payload);
expect(payload).toHaveProperty("secretImport");
return payload.secretImport;
};
describe("Secret Import Router", async () => { describe("Secret Import Router", async () => {
test.each([ test.each([
{ importEnv: "prod", importPath: "/" }, // one in root { importEnv: "prod", importPath: "/" }, // one in root
{ importEnv: "staging", importPath: "/" } // then create a deep one creating intermediate ones { importEnv: "staging", importPath: "/" } // then create a deep one creating intermediate ones
])("Create secret import $importEnv with path $importPath", async ({ importPath, importEnv }) => { ])("Create secret import $importEnv with path $importPath", async ({ importPath, importEnv }) => {
// check for default environments // check for default environments
const payload = await createSecretImport(importPath, importEnv); const payload = await createSecretImport({
authToken: jwtAuthToken,
secretPath: "/",
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.project.id,
importPath,
importEnv
});
expect(payload).toEqual( expect(payload).toEqual(
expect.objectContaining({ expect.objectContaining({
id: expect.any(String), id: expect.any(String),
importPath: expect.any(String), importPath,
importEnv: expect.objectContaining({ importEnv: expect.objectContaining({
name: expect.any(String), name: expect.any(String),
slug: expect.any(String), slug: importEnv,
id: expect.any(String) id: expect.any(String)
}) })
}) })
); );
await deleteSecretImport(payload.id);
await deleteSecretImport({
id: payload.id,
workspaceId: seedData1.project.id,
environmentSlug: seedData1.environment.slug,
secretPath: "/",
authToken: jwtAuthToken
});
}); });
test("Get secret imports", async () => { test("Get secret imports", async () => {
const createdImport1 = await createSecretImport("/", "prod"); const createdImport1 = await createSecretImport({
const createdImport2 = await createSecretImport("/", "staging"); authToken: jwtAuthToken,
secretPath: "/",
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.project.id,
importPath: "/",
importEnv: "prod"
});
const createdImport2 = await createSecretImport({
authToken: jwtAuthToken,
secretPath: "/",
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.project.id,
importPath: "/",
importEnv: "staging"
});
const res = await testServer.inject({ const res = await testServer.inject({
method: "GET", method: "GET",
url: `/api/v1/secret-imports`, url: `/api/v1/secret-imports`,
@@ -89,25 +77,60 @@ describe("Secret Import Router", async () => {
expect.arrayContaining([ expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
id: expect.any(String), id: expect.any(String),
importPath: expect.any(String), importPath: "/",
importEnv: expect.objectContaining({ importEnv: expect.objectContaining({
name: expect.any(String), name: expect.any(String),
slug: expect.any(String), slug: "prod",
id: expect.any(String)
})
}),
expect.objectContaining({
id: expect.any(String),
importPath: "/",
importEnv: expect.objectContaining({
name: expect.any(String),
slug: "staging",
id: expect.any(String) id: expect.any(String)
}) })
}) })
]) ])
); );
await deleteSecretImport(createdImport1.id); await deleteSecretImport({
await deleteSecretImport(createdImport2.id); id: createdImport1.id,
workspaceId: seedData1.project.id,
environmentSlug: seedData1.environment.slug,
secretPath: "/",
authToken: jwtAuthToken
});
await deleteSecretImport({
id: createdImport2.id,
workspaceId: seedData1.project.id,
environmentSlug: seedData1.environment.slug,
secretPath: "/",
authToken: jwtAuthToken
});
}); });
test("Update secret import position", async () => { test("Update secret import position", async () => {
const prodImportDetails = { path: "/", envSlug: "prod" }; const prodImportDetails = { path: "/", envSlug: "prod" };
const stagingImportDetails = { path: "/", envSlug: "staging" }; const stagingImportDetails = { path: "/", envSlug: "staging" };
const createdImport1 = await createSecretImport(prodImportDetails.path, prodImportDetails.envSlug); const createdImport1 = await createSecretImport({
const createdImport2 = await createSecretImport(stagingImportDetails.path, stagingImportDetails.envSlug); authToken: jwtAuthToken,
secretPath: "/",
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.project.id,
importPath: prodImportDetails.path,
importEnv: prodImportDetails.envSlug
});
const createdImport2 = await createSecretImport({
authToken: jwtAuthToken,
secretPath: "/",
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.project.id,
importPath: stagingImportDetails.path,
importEnv: stagingImportDetails.envSlug
});
const updateImportRes = await testServer.inject({ const updateImportRes = await testServer.inject({
method: "PATCH", method: "PATCH",
@@ -161,22 +184,55 @@ describe("Secret Import Router", async () => {
expect(secretImportList.secretImports[1].id).toEqual(createdImport1.id); expect(secretImportList.secretImports[1].id).toEqual(createdImport1.id);
expect(secretImportList.secretImports[0].id).toEqual(createdImport2.id); expect(secretImportList.secretImports[0].id).toEqual(createdImport2.id);
await deleteSecretImport(createdImport1.id); await deleteSecretImport({
await deleteSecretImport(createdImport2.id); id: createdImport1.id,
workspaceId: seedData1.project.id,
environmentSlug: seedData1.environment.slug,
secretPath: "/",
authToken: jwtAuthToken
});
await deleteSecretImport({
id: createdImport2.id,
workspaceId: seedData1.project.id,
environmentSlug: seedData1.environment.slug,
secretPath: "/",
authToken: jwtAuthToken
});
}); });
test("Delete secret import position", async () => { test("Delete secret import position", async () => {
const createdImport1 = await createSecretImport("/", "prod"); const createdImport1 = await createSecretImport({
const createdImport2 = await createSecretImport("/", "staging"); authToken: jwtAuthToken,
const deletedImport = await deleteSecretImport(createdImport1.id); secretPath: "/",
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.project.id,
importPath: "/",
importEnv: "prod"
});
const createdImport2 = await createSecretImport({
authToken: jwtAuthToken,
secretPath: "/",
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.project.id,
importPath: "/",
importEnv: "staging"
});
const deletedImport = await deleteSecretImport({
id: createdImport1.id,
workspaceId: seedData1.project.id,
environmentSlug: seedData1.environment.slug,
secretPath: "/",
authToken: jwtAuthToken
});
// check for default environments // check for default environments
expect(deletedImport).toEqual( expect(deletedImport).toEqual(
expect.objectContaining({ expect.objectContaining({
id: expect.any(String), id: expect.any(String),
importPath: expect.any(String), importPath: "/",
importEnv: expect.objectContaining({ importEnv: expect.objectContaining({
name: expect.any(String), name: expect.any(String),
slug: expect.any(String), slug: "prod",
id: expect.any(String) id: expect.any(String)
}) })
}) })
@@ -201,6 +257,552 @@ describe("Secret Import Router", async () => {
expect(secretImportList.secretImports.length).toEqual(1); expect(secretImportList.secretImports.length).toEqual(1);
expect(secretImportList.secretImports[0].position).toEqual(1); expect(secretImportList.secretImports[0].position).toEqual(1);
await deleteSecretImport(createdImport2.id); await deleteSecretImport({
id: createdImport2.id,
workspaceId: seedData1.project.id,
environmentSlug: seedData1.environment.slug,
secretPath: "/",
authToken: jwtAuthToken
});
}); });
}); });
// dev <- stage <- prod
describe.each([{ path: "/" }, { path: "/deep" }])(
"Secret import waterfall pattern testing - %path",
({ path: testSuitePath }) => {
beforeAll(async () => {
let prodFolder: { id: string };
let stagingFolder: { id: string };
let devFolder: { id: string };
if (testSuitePath !== "/") {
prodFolder = await createFolder({
authToken: jwtAuthToken,
environmentSlug: "prod",
workspaceId: seedData1.projectV3.id,
secretPath: "/",
name: "deep"
});
stagingFolder = await createFolder({
authToken: jwtAuthToken,
environmentSlug: "staging",
workspaceId: seedData1.projectV3.id,
secretPath: "/",
name: "deep"
});
devFolder = await createFolder({
authToken: jwtAuthToken,
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.projectV3.id,
secretPath: "/",
name: "deep"
});
}
const devImportFromStage = await createSecretImport({
authToken: jwtAuthToken,
secretPath: testSuitePath,
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.projectV3.id,
importPath: testSuitePath,
importEnv: "staging"
});
const stageImportFromProd = await createSecretImport({
authToken: jwtAuthToken,
secretPath: testSuitePath,
environmentSlug: "staging",
workspaceId: seedData1.projectV3.id,
importPath: testSuitePath,
importEnv: "prod"
});
return async () => {
await deleteSecretImport({
id: stageImportFromProd.id,
workspaceId: seedData1.projectV3.id,
environmentSlug: "staging",
secretPath: testSuitePath,
authToken: jwtAuthToken
});
await deleteSecretImport({
id: devImportFromStage.id,
workspaceId: seedData1.projectV3.id,
environmentSlug: seedData1.environment.slug,
secretPath: testSuitePath,
authToken: jwtAuthToken
});
if (prodFolder) {
await deleteFolder({
authToken: jwtAuthToken,
secretPath: "/",
id: prodFolder.id,
workspaceId: seedData1.projectV3.id,
environmentSlug: "prod"
});
}
if (stagingFolder) {
await deleteFolder({
authToken: jwtAuthToken,
secretPath: "/",
id: stagingFolder.id,
workspaceId: seedData1.projectV3.id,
environmentSlug: "staging"
});
}
if (devFolder) {
await deleteFolder({
authToken: jwtAuthToken,
secretPath: "/",
id: devFolder.id,
workspaceId: seedData1.projectV3.id,
environmentSlug: seedData1.environment.slug
});
}
};
});
test("Check one level imported secret exist", async () => {
await createSecretV2({
environmentSlug: "staging",
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken,
key: "STAGING_KEY",
value: "stage-value"
});
const secret = await getSecretByNameV2({
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken,
key: "STAGING_KEY"
});
expect(secret.secretKey).toBe("STAGING_KEY");
expect(secret.secretValue).toBe("stage-value");
const listSecrets = await getSecretsV2({
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken
});
expect(listSecrets.imports).toEqual(
expect.arrayContaining([
expect.objectContaining({
secrets: expect.arrayContaining([
expect.objectContaining({
secretKey: "STAGING_KEY",
secretValue: "stage-value"
})
])
})
])
);
await deleteSecretV2({
environmentSlug: "staging",
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken,
key: "STAGING_KEY"
});
});
test("Check two level imported secret exist", async () => {
await createSecretV2({
environmentSlug: "prod",
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken,
key: "PROD_KEY",
value: "prod-value"
});
const secret = await getSecretByNameV2({
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken,
key: "PROD_KEY"
});
expect(secret.secretKey).toBe("PROD_KEY");
expect(secret.secretValue).toBe("prod-value");
const listSecrets = await getSecretsV2({
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken
});
expect(listSecrets.imports).toEqual(
expect.arrayContaining([
expect.objectContaining({
secrets: expect.arrayContaining([
expect.objectContaining({
secretKey: "PROD_KEY",
secretValue: "prod-value"
})
])
})
])
);
await deleteSecretV2({
environmentSlug: "prod",
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken,
key: "PROD_KEY"
});
});
}
);
// dev <- stage, dev <- prod
describe.each([{ path: "/" }, { path: "/deep" }])(
"Secret import multiple destination to one source pattern testing - %path",
({ path: testSuitePath }) => {
beforeAll(async () => {
let prodFolder: { id: string };
let stagingFolder: { id: string };
let devFolder: { id: string };
if (testSuitePath !== "/") {
prodFolder = await createFolder({
authToken: jwtAuthToken,
environmentSlug: "prod",
workspaceId: seedData1.projectV3.id,
secretPath: "/",
name: "deep"
});
stagingFolder = await createFolder({
authToken: jwtAuthToken,
environmentSlug: "staging",
workspaceId: seedData1.projectV3.id,
secretPath: "/",
name: "deep"
});
devFolder = await createFolder({
authToken: jwtAuthToken,
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.projectV3.id,
secretPath: "/",
name: "deep"
});
}
const devImportFromStage = await createSecretImport({
authToken: jwtAuthToken,
secretPath: testSuitePath,
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.projectV3.id,
importPath: testSuitePath,
importEnv: "staging"
});
const devImportFromProd = await createSecretImport({
authToken: jwtAuthToken,
secretPath: testSuitePath,
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.projectV3.id,
importPath: testSuitePath,
importEnv: "prod"
});
return async () => {
await deleteSecretImport({
id: devImportFromProd.id,
workspaceId: seedData1.projectV3.id,
environmentSlug: seedData1.environment.slug,
secretPath: testSuitePath,
authToken: jwtAuthToken
});
await deleteSecretImport({
id: devImportFromStage.id,
workspaceId: seedData1.projectV3.id,
environmentSlug: seedData1.environment.slug,
secretPath: testSuitePath,
authToken: jwtAuthToken
});
if (prodFolder) {
await deleteFolder({
authToken: jwtAuthToken,
secretPath: "/",
id: prodFolder.id,
workspaceId: seedData1.projectV3.id,
environmentSlug: "prod"
});
}
if (stagingFolder) {
await deleteFolder({
authToken: jwtAuthToken,
secretPath: "/",
id: stagingFolder.id,
workspaceId: seedData1.projectV3.id,
environmentSlug: "staging"
});
}
if (devFolder) {
await deleteFolder({
authToken: jwtAuthToken,
secretPath: "/",
id: devFolder.id,
workspaceId: seedData1.projectV3.id,
environmentSlug: seedData1.environment.slug
});
}
};
});
test("Check imported secret exist", async () => {
await createSecretV2({
environmentSlug: "staging",
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken,
key: "STAGING_KEY",
value: "stage-value"
});
await createSecretV2({
environmentSlug: "prod",
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken,
key: "PROD_KEY",
value: "prod-value"
});
const secret = await getSecretByNameV2({
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken,
key: "STAGING_KEY"
});
expect(secret.secretKey).toBe("STAGING_KEY");
expect(secret.secretValue).toBe("stage-value");
const listSecrets = await getSecretsV2({
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken
});
expect(listSecrets.imports).toEqual(
expect.arrayContaining([
expect.objectContaining({
secrets: expect.arrayContaining([
expect.objectContaining({
secretKey: "STAGING_KEY",
secretValue: "stage-value"
})
])
}),
expect.objectContaining({
secrets: expect.arrayContaining([
expect.objectContaining({
secretKey: "PROD_KEY",
secretValue: "prod-value"
})
])
})
])
);
await deleteSecretV2({
environmentSlug: "staging",
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken,
key: "STAGING_KEY"
});
await deleteSecretV2({
environmentSlug: "prod",
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken,
key: "PROD_KEY"
});
});
}
);
// dev -> stage, prod
describe.each([{ path: "/" }, { path: "/deep" }])(
"Secret import one source to multiple destination pattern testing - %path",
({ path: testSuitePath }) => {
beforeAll(async () => {
let prodFolder: { id: string };
let stagingFolder: { id: string };
let devFolder: { id: string };
if (testSuitePath !== "/") {
prodFolder = await createFolder({
authToken: jwtAuthToken,
environmentSlug: "prod",
workspaceId: seedData1.projectV3.id,
secretPath: "/",
name: "deep"
});
stagingFolder = await createFolder({
authToken: jwtAuthToken,
environmentSlug: "staging",
workspaceId: seedData1.projectV3.id,
secretPath: "/",
name: "deep"
});
devFolder = await createFolder({
authToken: jwtAuthToken,
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.projectV3.id,
secretPath: "/",
name: "deep"
});
}
const stageImportFromDev = await createSecretImport({
authToken: jwtAuthToken,
secretPath: testSuitePath,
environmentSlug: "staging",
workspaceId: seedData1.projectV3.id,
importPath: testSuitePath,
importEnv: seedData1.environment.slug
});
const prodImportFromDev = await createSecretImport({
authToken: jwtAuthToken,
secretPath: testSuitePath,
environmentSlug: "prod",
workspaceId: seedData1.projectV3.id,
importPath: testSuitePath,
importEnv: seedData1.environment.slug
});
return async () => {
await deleteSecretImport({
id: prodImportFromDev.id,
workspaceId: seedData1.projectV3.id,
environmentSlug: "prod",
secretPath: testSuitePath,
authToken: jwtAuthToken
});
await deleteSecretImport({
id: stageImportFromDev.id,
workspaceId: seedData1.projectV3.id,
environmentSlug: "staging",
secretPath: testSuitePath,
authToken: jwtAuthToken
});
if (prodFolder) {
await deleteFolder({
authToken: jwtAuthToken,
secretPath: "/",
id: prodFolder.id,
workspaceId: seedData1.projectV3.id,
environmentSlug: "prod"
});
}
if (stagingFolder) {
await deleteFolder({
authToken: jwtAuthToken,
secretPath: "/",
id: stagingFolder.id,
workspaceId: seedData1.projectV3.id,
environmentSlug: "staging"
});
}
if (devFolder) {
await deleteFolder({
authToken: jwtAuthToken,
secretPath: "/",
id: devFolder.id,
workspaceId: seedData1.projectV3.id,
environmentSlug: seedData1.environment.slug
});
}
};
});
test("Check imported secret exist", async () => {
await createSecretV2({
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken,
key: "STAGING_KEY",
value: "stage-value"
});
await createSecretV2({
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken,
key: "PROD_KEY",
value: "prod-value"
});
const stagingSecret = await getSecretByNameV2({
environmentSlug: "staging",
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken,
key: "STAGING_KEY"
});
expect(stagingSecret.secretKey).toBe("STAGING_KEY");
expect(stagingSecret.secretValue).toBe("stage-value");
const prodSecret = await getSecretByNameV2({
environmentSlug: "prod",
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken,
key: "PROD_KEY"
});
expect(prodSecret.secretKey).toBe("PROD_KEY");
expect(prodSecret.secretValue).toBe("prod-value");
await deleteSecretV2({
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken,
key: "STAGING_KEY"
});
await deleteSecretV2({
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken,
key: "PROD_KEY"
});
});
}
);
@@ -0,0 +1,406 @@
import { createFolder, deleteFolder } from "e2e-test/testUtils/folders";
import { createSecretImport, deleteSecretImport } from "e2e-test/testUtils/secret-imports";
import { createSecretV2, deleteSecretV2, getSecretByNameV2, getSecretsV2 } from "e2e-test/testUtils/secrets";
import { seedData1 } from "@app/db/seed-data";
// dev <- stage <- prod
describe.each([{ secretPath: "/" }, { secretPath: "/deep" }])(
"Secret replication waterfall pattern testing - %secretPath",
({ secretPath: testSuitePath }) => {
beforeAll(async () => {
let prodFolder: { id: string };
let stagingFolder: { id: string };
let devFolder: { id: string };
if (testSuitePath !== "/") {
prodFolder = await createFolder({
authToken: jwtAuthToken,
environmentSlug: "prod",
workspaceId: seedData1.projectV3.id,
secretPath: "/",
name: "deep"
});
stagingFolder = await createFolder({
authToken: jwtAuthToken,
environmentSlug: "staging",
workspaceId: seedData1.projectV3.id,
secretPath: "/",
name: "deep"
});
devFolder = await createFolder({
authToken: jwtAuthToken,
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.projectV3.id,
secretPath: "/",
name: "deep"
});
}
const devImportFromStage = await createSecretImport({
authToken: jwtAuthToken,
secretPath: testSuitePath,
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.projectV3.id,
importPath: testSuitePath,
importEnv: "staging",
isReplication: true
});
const stageImportFromProd = await createSecretImport({
authToken: jwtAuthToken,
secretPath: testSuitePath,
environmentSlug: "staging",
workspaceId: seedData1.projectV3.id,
importPath: testSuitePath,
importEnv: "prod",
isReplication: true
});
return async () => {
await deleteSecretImport({
id: stageImportFromProd.id,
workspaceId: seedData1.projectV3.id,
environmentSlug: "staging",
secretPath: testSuitePath,
authToken: jwtAuthToken
});
await deleteSecretImport({
id: devImportFromStage.id,
workspaceId: seedData1.projectV3.id,
environmentSlug: seedData1.environment.slug,
secretPath: testSuitePath,
authToken: jwtAuthToken
});
if (prodFolder) {
await deleteFolder({
authToken: jwtAuthToken,
secretPath: "/",
id: prodFolder.id,
workspaceId: seedData1.projectV3.id,
environmentSlug: "prod"
});
}
if (stagingFolder) {
await deleteFolder({
authToken: jwtAuthToken,
secretPath: "/",
id: stagingFolder.id,
workspaceId: seedData1.projectV3.id,
environmentSlug: "staging"
});
}
if (devFolder) {
await deleteFolder({
authToken: jwtAuthToken,
secretPath: "/",
id: devFolder.id,
workspaceId: seedData1.projectV3.id,
environmentSlug: seedData1.environment.slug
});
}
};
});
test("Check one level imported secret exist", async () => {
await createSecretV2({
environmentSlug: "staging",
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken,
key: "STAGING_KEY",
value: "stage-value"
});
// wait for 5 second for replication to finish
await new Promise((resolve) => {
setTimeout(resolve, 5000); // time to breathe for db
});
const secret = await getSecretByNameV2({
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken,
key: "STAGING_KEY"
});
expect(secret.secretKey).toBe("STAGING_KEY");
expect(secret.secretValue).toBe("stage-value");
const listSecrets = await getSecretsV2({
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken
});
expect(listSecrets.imports).toEqual(
expect.arrayContaining([
expect.objectContaining({
secrets: expect.arrayContaining([
expect.objectContaining({
secretKey: "STAGING_KEY",
secretValue: "stage-value"
})
])
})
])
);
await deleteSecretV2({
environmentSlug: "staging",
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken,
key: "STAGING_KEY"
});
});
test("Check two level imported secret exist", async () => {
await createSecretV2({
environmentSlug: "prod",
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken,
key: "PROD_KEY",
value: "prod-value"
});
// wait for 5 second for replication to finish
await new Promise((resolve) => {
setTimeout(resolve, 5000); // time to breathe for db
});
const secret = await getSecretByNameV2({
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken,
key: "PROD_KEY"
});
expect(secret.secretKey).toBe("PROD_KEY");
expect(secret.secretValue).toBe("prod-value");
const listSecrets = await getSecretsV2({
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken
});
expect(listSecrets.imports).toEqual(
expect.arrayContaining([
expect.objectContaining({
secrets: expect.arrayContaining([
expect.objectContaining({
secretKey: "PROD_KEY",
secretValue: "prod-value"
})
])
})
])
);
await deleteSecretV2({
environmentSlug: "prod",
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken,
key: "PROD_KEY"
});
});
},
{ timeout: 30000 }
);
// dev <- stage, dev <- prod
describe.each([{ path: "/" }, { path: "/deep" }])(
"Secret replication 1-N pattern testing - %path",
({ path: testSuitePath }) => {
beforeAll(async () => {
let prodFolder: { id: string };
let stagingFolder: { id: string };
let devFolder: { id: string };
if (testSuitePath !== "/") {
prodFolder = await createFolder({
authToken: jwtAuthToken,
environmentSlug: "prod",
workspaceId: seedData1.projectV3.id,
secretPath: "/",
name: "deep"
});
stagingFolder = await createFolder({
authToken: jwtAuthToken,
environmentSlug: "staging",
workspaceId: seedData1.projectV3.id,
secretPath: "/",
name: "deep"
});
devFolder = await createFolder({
authToken: jwtAuthToken,
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.projectV3.id,
secretPath: "/",
name: "deep"
});
}
const devImportFromStage = await createSecretImport({
authToken: jwtAuthToken,
secretPath: testSuitePath,
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.projectV3.id,
importPath: testSuitePath,
importEnv: "staging",
isReplication: true
});
const devImportFromProd = await createSecretImport({
authToken: jwtAuthToken,
secretPath: testSuitePath,
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.projectV3.id,
importPath: testSuitePath,
importEnv: "prod",
isReplication: true
});
return async () => {
await deleteSecretImport({
id: devImportFromProd.id,
workspaceId: seedData1.projectV3.id,
environmentSlug: seedData1.environment.slug,
secretPath: testSuitePath,
authToken: jwtAuthToken
});
await deleteSecretImport({
id: devImportFromStage.id,
workspaceId: seedData1.projectV3.id,
environmentSlug: seedData1.environment.slug,
secretPath: testSuitePath,
authToken: jwtAuthToken
});
if (prodFolder) {
await deleteFolder({
authToken: jwtAuthToken,
secretPath: "/",
id: prodFolder.id,
workspaceId: seedData1.projectV3.id,
environmentSlug: "prod"
});
}
if (stagingFolder) {
await deleteFolder({
authToken: jwtAuthToken,
secretPath: "/",
id: stagingFolder.id,
workspaceId: seedData1.projectV3.id,
environmentSlug: "staging"
});
}
if (devFolder) {
await deleteFolder({
authToken: jwtAuthToken,
secretPath: "/",
id: devFolder.id,
workspaceId: seedData1.projectV3.id,
environmentSlug: seedData1.environment.slug
});
}
};
});
test("Check imported secret exist", async () => {
await createSecretV2({
environmentSlug: "staging",
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken,
key: "STAGING_KEY",
value: "stage-value"
});
await createSecretV2({
environmentSlug: "prod",
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken,
key: "PROD_KEY",
value: "prod-value"
});
// wait for 5 second for replication to finish
await new Promise((resolve) => {
setTimeout(resolve, 5000); // time to breathe for db
});
const secret = await getSecretByNameV2({
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken,
key: "STAGING_KEY"
});
expect(secret.secretKey).toBe("STAGING_KEY");
expect(secret.secretValue).toBe("stage-value");
const listSecrets = await getSecretsV2({
environmentSlug: seedData1.environment.slug,
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken
});
expect(listSecrets.imports).toEqual(
expect.arrayContaining([
expect.objectContaining({
secrets: expect.arrayContaining([
expect.objectContaining({
secretKey: "STAGING_KEY",
secretValue: "stage-value"
})
])
}),
expect.objectContaining({
secrets: expect.arrayContaining([
expect.objectContaining({
secretKey: "PROD_KEY",
secretValue: "prod-value"
})
])
})
])
);
await deleteSecretV2({
environmentSlug: "staging",
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken,
key: "STAGING_KEY"
});
await deleteSecretV2({
environmentSlug: "prod",
workspaceId: seedData1.projectV3.id,
secretPath: testSuitePath,
authToken: jwtAuthToken,
key: "PROD_KEY"
});
});
},
{ timeout: 30000 }
);
@@ -0,0 +1,330 @@
import { createFolder, deleteFolder } from "e2e-test/testUtils/folders";
import { createSecretImport, deleteSecretImport } from "e2e-test/testUtils/secret-imports";
import { createSecretV2, deleteSecretV2, getSecretByNameV2, getSecretsV2 } from "e2e-test/testUtils/secrets";
import { seedData1 } from "@app/db/seed-data";
describe("Secret expansion", () => {
const projectId = seedData1.projectV3.id;
beforeAll(async () => {
const prodRootFolder = await createFolder({
authToken: jwtAuthToken,
environmentSlug: "prod",
workspaceId: projectId,
secretPath: "/",
name: "deep"
});
await createFolder({
authToken: jwtAuthToken,
environmentSlug: "prod",
workspaceId: projectId,
secretPath: "/deep",
name: "nested"
});
return async () => {
await deleteFolder({
authToken: jwtAuthToken,
secretPath: "/",
id: prodRootFolder.id,
workspaceId: projectId,
environmentSlug: "prod"
});
};
});
test("Local secret reference", async () => {
const secrets = [
{
environmentSlug: seedData1.environment.slug,
workspaceId: projectId,
secretPath: "/",
authToken: jwtAuthToken,
key: "HELLO",
value: "world"
},
{
environmentSlug: seedData1.environment.slug,
workspaceId: projectId,
secretPath: "/",
authToken: jwtAuthToken,
key: "TEST",
// eslint-disable-next-line
value: "hello ${HELLO}"
}
];
await Promise.all(secrets.map((el) => createSecretV2(el)));
const expandedSecret = await getSecretByNameV2({
environmentSlug: seedData1.environment.slug,
workspaceId: projectId,
secretPath: "/",
authToken: jwtAuthToken,
key: "TEST"
});
expect(expandedSecret.secretValue).toBe("hello world");
const listSecrets = await getSecretsV2({
environmentSlug: seedData1.environment.slug,
workspaceId: projectId,
secretPath: "/",
authToken: jwtAuthToken
});
expect(listSecrets.secrets).toEqual(
expect.arrayContaining([
expect.objectContaining({
secretKey: "TEST",
secretValue: "hello world"
})
])
);
await Promise.all(secrets.map((el) => deleteSecretV2(el)));
});
test("Cross environment secret reference", async () => {
const secrets = [
{
environmentSlug: "prod",
workspaceId: projectId,
secretPath: "/deep",
authToken: jwtAuthToken,
key: "DEEP_KEY_1",
value: "testing"
},
{
environmentSlug: "prod",
workspaceId: projectId,
secretPath: "/deep/nested",
authToken: jwtAuthToken,
key: "NESTED_KEY_1",
value: "reference"
},
{
environmentSlug: "prod",
workspaceId: projectId,
secretPath: "/deep/nested",
authToken: jwtAuthToken,
key: "NESTED_KEY_2",
// eslint-disable-next-line
value: "secret ${NESTED_KEY_1}"
},
{
environmentSlug: seedData1.environment.slug,
workspaceId: projectId,
secretPath: "/",
authToken: jwtAuthToken,
key: "KEY",
// eslint-disable-next-line
value: "hello ${prod.deep.DEEP_KEY_1} ${prod.deep.nested.NESTED_KEY_2}"
}
];
await Promise.all(secrets.map((el) => createSecretV2(el)));
const expandedSecret = await getSecretByNameV2({
environmentSlug: seedData1.environment.slug,
workspaceId: projectId,
secretPath: "/",
authToken: jwtAuthToken,
key: "KEY"
});
expect(expandedSecret.secretValue).toBe("hello testing secret reference");
const listSecrets = await getSecretsV2({
environmentSlug: seedData1.environment.slug,
workspaceId: projectId,
secretPath: "/",
authToken: jwtAuthToken
});
expect(listSecrets.secrets).toEqual(
expect.arrayContaining([
expect.objectContaining({
secretKey: "KEY",
secretValue: "hello testing secret reference"
})
])
);
await Promise.all(secrets.map((el) => deleteSecretV2(el)));
});
test("Non replicated secret import secret expansion on local reference and nested reference", async () => {
const secrets = [
{
environmentSlug: "prod",
workspaceId: projectId,
secretPath: "/deep",
authToken: jwtAuthToken,
key: "DEEP_KEY_1",
value: "testing"
},
{
environmentSlug: "prod",
workspaceId: projectId,
secretPath: "/deep/nested",
authToken: jwtAuthToken,
key: "NESTED_KEY_1",
value: "reference"
},
{
environmentSlug: "prod",
workspaceId: projectId,
secretPath: "/deep/nested",
authToken: jwtAuthToken,
key: "NESTED_KEY_2",
// eslint-disable-next-line
value: "secret ${NESTED_KEY_1} ${prod.deep.DEEP_KEY_1}"
},
{
environmentSlug: seedData1.environment.slug,
workspaceId: projectId,
secretPath: "/",
authToken: jwtAuthToken,
key: "KEY",
// eslint-disable-next-line
value: "hello world"
}
];
await Promise.all(secrets.map((el) => createSecretV2(el)));
const secretImportFromProdToDev = await createSecretImport({
environmentSlug: seedData1.environment.slug,
workspaceId: projectId,
secretPath: "/",
authToken: jwtAuthToken,
importEnv: "prod",
importPath: "/deep/nested"
});
const listSecrets = await getSecretsV2({
environmentSlug: seedData1.environment.slug,
workspaceId: projectId,
secretPath: "/",
authToken: jwtAuthToken
});
expect(listSecrets.imports).toEqual(
expect.arrayContaining([
expect.objectContaining({
secretPath: "/deep/nested",
environment: "prod",
secrets: expect.arrayContaining([
expect.objectContaining({
secretKey: "NESTED_KEY_1",
secretValue: "reference"
}),
expect.objectContaining({
secretKey: "NESTED_KEY_2",
secretValue: "secret reference testing"
})
])
})
])
);
await Promise.all(secrets.map((el) => deleteSecretV2(el)));
await deleteSecretImport({
environmentSlug: seedData1.environment.slug,
workspaceId: projectId,
authToken: jwtAuthToken,
id: secretImportFromProdToDev.id,
secretPath: "/"
});
});
test(
"Replicated secret import secret expansion on local reference and nested reference",
async () => {
const secrets = [
{
environmentSlug: "prod",
workspaceId: projectId,
secretPath: "/deep",
authToken: jwtAuthToken,
key: "DEEP_KEY_1",
value: "testing"
},
{
environmentSlug: "prod",
workspaceId: projectId,
secretPath: "/deep/nested",
authToken: jwtAuthToken,
key: "NESTED_KEY_1",
value: "reference"
},
{
environmentSlug: "prod",
workspaceId: projectId,
secretPath: "/deep/nested",
authToken: jwtAuthToken,
key: "NESTED_KEY_2",
// eslint-disable-next-line
value: "secret ${NESTED_KEY_1} ${prod.deep.DEEP_KEY_1}"
},
{
environmentSlug: seedData1.environment.slug,
workspaceId: projectId,
secretPath: "/",
authToken: jwtAuthToken,
key: "KEY",
// eslint-disable-next-line
value: "hello world"
}
];
await Promise.all(secrets.map((el) => createSecretV2(el)));
const secretImportFromProdToDev = await createSecretImport({
environmentSlug: seedData1.environment.slug,
workspaceId: projectId,
secretPath: "/",
authToken: jwtAuthToken,
importEnv: "prod",
importPath: "/deep/nested",
isReplication: true
});
// wait for 5 second for replication to finish
await new Promise((resolve) => {
setTimeout(resolve, 5000); // time to breathe for db
});
const listSecrets = await getSecretsV2({
environmentSlug: seedData1.environment.slug,
workspaceId: projectId,
secretPath: "/",
authToken: jwtAuthToken
});
expect(listSecrets.imports).toEqual(
expect.arrayContaining([
expect.objectContaining({
secretPath: `/__reserve_replication_${secretImportFromProdToDev.id}`,
environment: seedData1.environment.slug,
secrets: expect.arrayContaining([
expect.objectContaining({
secretKey: "NESTED_KEY_1",
secretValue: "reference"
}),
expect.objectContaining({
secretKey: "NESTED_KEY_2",
secretValue: "secret reference testing"
})
])
})
])
);
await Promise.all(secrets.map((el) => deleteSecretV2(el)));
await deleteSecretImport({
environmentSlug: seedData1.environment.slug,
workspaceId: projectId,
authToken: jwtAuthToken,
id: secretImportFromProdToDev.id,
secretPath: "/"
});
},
{ timeout: 10000 }
);
});
@@ -8,6 +8,7 @@ type TRawSecret = {
secretComment?: string; secretComment?: string;
version: number; version: number;
}; };
const createSecret = async (dto: { path: string; key: string; value: string; comment: string; type?: SecretType }) => { const createSecret = async (dto: { path: string; key: string; value: string; comment: string; type?: SecretType }) => {
const createSecretReqBody = { const createSecretReqBody = {
workspaceId: seedData1.projectV3.id, workspaceId: seedData1.projectV3.id,
+73
View File
@@ -0,0 +1,73 @@
type TFolder = {
id: string;
name: string;
};
export const createFolder = async (dto: {
workspaceId: string;
environmentSlug: string;
secretPath: string;
name: string;
authToken: string;
}) => {
const res = await testServer.inject({
method: "POST",
url: `/api/v1/folders`,
headers: {
authorization: `Bearer ${dto.authToken}`
},
body: {
workspaceId: dto.workspaceId,
environment: dto.environmentSlug,
name: dto.name,
path: dto.secretPath
}
});
expect(res.statusCode).toBe(200);
return res.json().folder as TFolder;
};
export const deleteFolder = async (dto: {
workspaceId: string;
environmentSlug: string;
secretPath: string;
id: string;
authToken: string;
}) => {
const res = await testServer.inject({
method: "DELETE",
url: `/api/v1/folders/${dto.id}`,
headers: {
authorization: `Bearer ${dto.authToken}`
},
body: {
workspaceId: dto.workspaceId,
environment: dto.environmentSlug,
path: dto.secretPath
}
});
expect(res.statusCode).toBe(200);
return res.json().folder as TFolder;
};
export const listFolders = async (dto: {
workspaceId: string;
environmentSlug: string;
secretPath: string;
authToken: string;
}) => {
const res = await testServer.inject({
method: "GET",
url: `/api/v1/folders`,
headers: {
authorization: `Bearer ${dto.authToken}`
},
body: {
workspaceId: dto.workspaceId,
environment: dto.environmentSlug,
path: dto.secretPath
}
});
expect(res.statusCode).toBe(200);
return res.json().folders as TFolder[];
};
@@ -0,0 +1,93 @@
type TSecretImport = {
id: string;
importEnv: {
name: string;
slug: string;
id: string;
};
importPath: string;
};
export const createSecretImport = async (dto: {
workspaceId: string;
environmentSlug: string;
isReplication?: boolean;
secretPath: string;
importPath: string;
importEnv: string;
authToken: string;
}) => {
const res = await testServer.inject({
method: "POST",
url: `/api/v1/secret-imports`,
headers: {
authorization: `Bearer ${dto.authToken}`
},
body: {
workspaceId: dto.workspaceId,
environment: dto.environmentSlug,
isReplication: dto.isReplication,
path: dto.secretPath,
import: {
environment: dto.importEnv,
path: dto.importPath
}
}
});
expect(res.statusCode).toBe(200);
const payload = JSON.parse(res.payload);
expect(payload).toHaveProperty("secretImport");
return payload.secretImport as TSecretImport;
};
export const deleteSecretImport = async (dto: {
workspaceId: string;
environmentSlug: string;
secretPath: string;
authToken: string;
id: string;
}) => {
const res = await testServer.inject({
method: "DELETE",
url: `/api/v1/secret-imports/${dto.id}`,
headers: {
authorization: `Bearer ${dto.authToken}`
},
body: {
workspaceId: dto.workspaceId,
environment: dto.environmentSlug,
path: dto.secretPath
}
});
expect(res.statusCode).toBe(200);
const payload = JSON.parse(res.payload);
expect(payload).toHaveProperty("secretImport");
return payload.secretImport as TSecretImport;
};
export const listSecretImport = async (dto: {
workspaceId: string;
environmentSlug: string;
secretPath: string;
authToken: string;
}) => {
const res = await testServer.inject({
method: "GET",
url: `/api/v1/secret-imports`,
headers: {
authorization: `Bearer ${dto.authToken}`
},
query: {
workspaceId: dto.workspaceId,
environment: dto.environmentSlug,
path: dto.secretPath
}
});
expect(res.statusCode).toBe(200);
const payload = JSON.parse(res.payload);
expect(payload).toHaveProperty("secretImports");
return payload.secretImports as TSecretImport[];
};
+128
View File
@@ -0,0 +1,128 @@
import { SecretType } from "@app/db/schemas";
type TRawSecret = {
secretKey: string;
secretValue: string;
secretComment?: string;
version: number;
};
export const createSecretV2 = async (dto: {
workspaceId: string;
environmentSlug: string;
secretPath: string;
key: string;
value: string;
comment?: string;
authToken: string;
type?: SecretType;
}) => {
const createSecretReqBody = {
workspaceId: dto.workspaceId,
environment: dto.environmentSlug,
type: dto.type || SecretType.Shared,
secretPath: dto.secretPath,
secretKey: dto.key,
secretValue: dto.value,
secretComment: dto.comment
};
const createSecRes = await testServer.inject({
method: "POST",
url: `/api/v3/secrets/raw/${dto.key}`,
headers: {
authorization: `Bearer ${dto.authToken}`
},
body: createSecretReqBody
});
expect(createSecRes.statusCode).toBe(200);
const createdSecretPayload = JSON.parse(createSecRes.payload);
expect(createdSecretPayload).toHaveProperty("secret");
return createdSecretPayload.secret as TRawSecret;
};
export const deleteSecretV2 = async (dto: {
workspaceId: string;
environmentSlug: string;
secretPath: string;
key: string;
authToken: string;
}) => {
const deleteSecRes = await testServer.inject({
method: "DELETE",
url: `/api/v3/secrets/raw/${dto.key}`,
headers: {
authorization: `Bearer ${dto.authToken}`
},
body: {
workspaceId: dto.workspaceId,
environment: dto.environmentSlug,
secretPath: dto.secretPath
}
});
expect(deleteSecRes.statusCode).toBe(200);
const updatedSecretPayload = JSON.parse(deleteSecRes.payload);
expect(updatedSecretPayload).toHaveProperty("secret");
return updatedSecretPayload.secret as TRawSecret;
};
export const getSecretByNameV2 = async (dto: {
workspaceId: string;
environmentSlug: string;
secretPath: string;
key: string;
authToken: string;
}) => {
const response = await testServer.inject({
method: "GET",
url: `/api/v3/secrets/raw/${dto.key}`,
headers: {
authorization: `Bearer ${dto.authToken}`
},
query: {
workspaceId: dto.workspaceId,
environment: dto.environmentSlug,
secretPath: dto.secretPath,
expandSecretReferences: "true",
include_imports: "true"
}
});
expect(response.statusCode).toBe(200);
const payload = JSON.parse(response.payload);
expect(payload).toHaveProperty("secret");
return payload.secret as TRawSecret;
};
export const getSecretsV2 = async (dto: {
workspaceId: string;
environmentSlug: string;
secretPath: string;
authToken: string;
}) => {
const getSecretsResponse = await testServer.inject({
method: "GET",
url: `/api/v3/secrets/raw`,
headers: {
authorization: `Bearer ${dto.authToken}`
},
query: {
workspaceId: dto.workspaceId,
environment: dto.environmentSlug,
secretPath: dto.secretPath,
expandSecretReferences: "true",
include_imports: "true"
}
});
expect(getSecretsResponse.statusCode).toBe(200);
const getSecretsPayload = JSON.parse(getSecretsResponse.payload);
expect(getSecretsPayload).toHaveProperty("secrets");
expect(getSecretsPayload).toHaveProperty("imports");
return getSecretsPayload as {
secrets: TRawSecret[];
imports: {
secretPath: string;
environment: string;
folderId: string;
secrets: TRawSecret[];
}[];
};
};
+22 -4
View File
@@ -11,10 +11,11 @@ import { initLogger } from "@app/lib/logger";
import { main } from "@app/server/app"; import { main } from "@app/server/app";
import { AuthMethod, AuthTokenType } from "@app/services/auth/auth-type"; import { AuthMethod, AuthTokenType } from "@app/services/auth/auth-type";
import { mockQueue } from "./mocks/queue";
import { mockSmtpServer } from "./mocks/smtp"; import { mockSmtpServer } from "./mocks/smtp";
import { mockKeyStore } from "./mocks/keystore";
import { initDbConnection } from "@app/db"; import { initDbConnection } from "@app/db";
import { queueServiceFactory } from "@app/queue";
import { keyStoreFactory } from "@app/keystore/keystore";
import { Redis } from "ioredis";
dotenv.config({ path: path.join(__dirname, "../../.env.test"), debug: true }); dotenv.config({ path: path.join(__dirname, "../../.env.test"), debug: true });
export default { export default {
@@ -28,19 +29,31 @@ export default {
dbRootCert: cfg.DB_ROOT_CERT dbRootCert: cfg.DB_ROOT_CERT
}); });
const redis = new Redis(cfg.REDIS_URL);
await redis.flushdb("SYNC");
try { try {
await db.migrate.rollback(
{
directory: path.join(__dirname, "../src/db/migrations"),
extension: "ts",
tableName: "infisical_migrations"
},
true
);
await db.migrate.latest({ await db.migrate.latest({
directory: path.join(__dirname, "../src/db/migrations"), directory: path.join(__dirname, "../src/db/migrations"),
extension: "ts", extension: "ts",
tableName: "infisical_migrations" tableName: "infisical_migrations"
}); });
await db.seed.run({ await db.seed.run({
directory: path.join(__dirname, "../src/db/seeds"), directory: path.join(__dirname, "../src/db/seeds"),
extension: "ts" extension: "ts"
}); });
const smtp = mockSmtpServer(); const smtp = mockSmtpServer();
const queue = mockQueue(); const queue = queueServiceFactory(cfg.REDIS_URL);
const keyStore = mockKeyStore(); const keyStore = keyStoreFactory(cfg.REDIS_URL);
const server = await main({ db, smtp, logger, queue, keyStore }); const server = await main({ db, smtp, logger, queue, keyStore });
// @ts-expect-error type // @ts-expect-error type
globalThis.testServer = server; globalThis.testServer = server;
@@ -58,10 +71,12 @@ export default {
{ expiresIn: cfg.JWT_AUTH_LIFETIME } { expiresIn: cfg.JWT_AUTH_LIFETIME }
); );
} catch (error) { } catch (error) {
// eslint-disable-next-line
console.log("[TEST] Error setting up environment", error); console.log("[TEST] Error setting up environment", error);
await db.destroy(); await db.destroy();
throw error; throw error;
} }
// custom setup // custom setup
return { return {
async teardown() { async teardown() {
@@ -80,6 +95,9 @@ export default {
}, },
true true
); );
await redis.flushdb("ASYNC");
redis.disconnect();
await db.destroy(); await db.destroy();
} }
}; };
+143 -173
View File
@@ -136,7 +136,6 @@
"tsup": "^8.0.1", "tsup": "^8.0.1",
"tsx": "^4.4.0", "tsx": "^4.4.0",
"typescript": "^5.3.2", "typescript": "^5.3.2",
"vite-tsconfig-paths": "^4.2.2",
"vitest": "^1.2.2" "vitest": "^1.2.2"
} }
}, },
@@ -301,16 +300,16 @@
} }
}, },
"node_modules/@aws-sdk/client-cloudwatch-logs": { "node_modules/@aws-sdk/client-cloudwatch-logs": {
"version": "3.635.0", "version": "3.632.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudwatch-logs/-/client-cloudwatch-logs-3.635.0.tgz", "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudwatch-logs/-/client-cloudwatch-logs-3.632.0.tgz",
"integrity": "sha512-M2SGf0B/WmHYNxUhUWKIYI5NW4Si7cyokB6Lt3RtDof3WVHA8L0LLl+EEo1URUkpxH8/F8VH2fTES7ODm2c+7g==", "integrity": "sha512-QrG04Ss2/KDsvGmoBH9QHjaC/wx7Gf9U2F5o8gYbHVU5ZGDW+zMX2Sj/6jjSyZ4qLD4sxK7sRHwK+fYA21OQQA==",
"dependencies": { "dependencies": {
"@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0",
"@aws-crypto/sha256-js": "5.2.0", "@aws-crypto/sha256-js": "5.2.0",
"@aws-sdk/client-sso-oidc": "3.635.0", "@aws-sdk/client-sso-oidc": "3.632.0",
"@aws-sdk/client-sts": "3.635.0", "@aws-sdk/client-sts": "3.632.0",
"@aws-sdk/core": "3.635.0", "@aws-sdk/core": "3.629.0",
"@aws-sdk/credential-provider-node": "3.635.0", "@aws-sdk/credential-provider-node": "3.632.0",
"@aws-sdk/middleware-host-header": "3.620.0", "@aws-sdk/middleware-host-header": "3.620.0",
"@aws-sdk/middleware-logger": "3.609.0", "@aws-sdk/middleware-logger": "3.609.0",
"@aws-sdk/middleware-recursion-detection": "3.620.0", "@aws-sdk/middleware-recursion-detection": "3.620.0",
@@ -321,7 +320,7 @@
"@aws-sdk/util-user-agent-browser": "3.609.0", "@aws-sdk/util-user-agent-browser": "3.609.0",
"@aws-sdk/util-user-agent-node": "3.614.0", "@aws-sdk/util-user-agent-node": "3.614.0",
"@smithy/config-resolver": "^3.0.5", "@smithy/config-resolver": "^3.0.5",
"@smithy/core": "^2.4.0", "@smithy/core": "^2.3.2",
"@smithy/eventstream-serde-browser": "^3.0.6", "@smithy/eventstream-serde-browser": "^3.0.6",
"@smithy/eventstream-serde-config-resolver": "^3.0.3", "@smithy/eventstream-serde-config-resolver": "^3.0.3",
"@smithy/eventstream-serde-node": "^3.0.5", "@smithy/eventstream-serde-node": "^3.0.5",
@@ -330,20 +329,20 @@
"@smithy/invalid-dependency": "^3.0.3", "@smithy/invalid-dependency": "^3.0.3",
"@smithy/middleware-content-length": "^3.0.5", "@smithy/middleware-content-length": "^3.0.5",
"@smithy/middleware-endpoint": "^3.1.0", "@smithy/middleware-endpoint": "^3.1.0",
"@smithy/middleware-retry": "^3.0.15", "@smithy/middleware-retry": "^3.0.14",
"@smithy/middleware-serde": "^3.0.3", "@smithy/middleware-serde": "^3.0.3",
"@smithy/middleware-stack": "^3.0.3", "@smithy/middleware-stack": "^3.0.3",
"@smithy/node-config-provider": "^3.1.4", "@smithy/node-config-provider": "^3.1.4",
"@smithy/node-http-handler": "^3.1.4", "@smithy/node-http-handler": "^3.1.4",
"@smithy/protocol-http": "^4.1.0", "@smithy/protocol-http": "^4.1.0",
"@smithy/smithy-client": "^3.2.0", "@smithy/smithy-client": "^3.1.12",
"@smithy/types": "^3.3.0", "@smithy/types": "^3.3.0",
"@smithy/url-parser": "^3.0.3", "@smithy/url-parser": "^3.0.3",
"@smithy/util-base64": "^3.0.0", "@smithy/util-base64": "^3.0.0",
"@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0",
"@smithy/util-body-length-node": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0",
"@smithy/util-defaults-mode-browser": "^3.0.15", "@smithy/util-defaults-mode-browser": "^3.0.14",
"@smithy/util-defaults-mode-node": "^3.0.15", "@smithy/util-defaults-mode-node": "^3.0.14",
"@smithy/util-endpoints": "^2.0.5", "@smithy/util-endpoints": "^2.0.5",
"@smithy/util-middleware": "^3.0.3", "@smithy/util-middleware": "^3.0.3",
"@smithy/util-retry": "^3.0.3", "@smithy/util-retry": "^3.0.3",
@@ -561,6 +560,45 @@
"node": ">=16.0.0" "node": ">=16.0.0"
} }
}, },
"node_modules/@aws-sdk/client-elasticache/node_modules/@aws-sdk/core": {
"version": "3.635.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.635.0.tgz",
"integrity": "sha512-i1x/E/sgA+liUE1XJ7rj1dhyXpAKO1UKFUcTTHXok2ARjWTvszHnSXMOsB77aPbmn0fUp1JTx2kHUAZ1LVt5Bg==",
"dependencies": {
"@smithy/core": "^2.4.0",
"@smithy/node-config-provider": "^3.1.4",
"@smithy/property-provider": "^3.1.3",
"@smithy/protocol-http": "^4.1.0",
"@smithy/signature-v4": "^4.1.0",
"@smithy/smithy-client": "^3.2.0",
"@smithy/types": "^3.3.0",
"@smithy/util-middleware": "^3.0.3",
"fast-xml-parser": "4.4.1",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=16.0.0"
}
},
"node_modules/@aws-sdk/client-elasticache/node_modules/@aws-sdk/credential-provider-http": {
"version": "3.635.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.635.0.tgz",
"integrity": "sha512-iJyRgEjOCQlBMXqtwPLIKYc7Bsc6nqjrZybdMDenPDa+kmLg7xh8LxHsu9088e+2/wtLicE34FsJJIfzu3L82g==",
"dependencies": {
"@aws-sdk/types": "3.609.0",
"@smithy/fetch-http-handler": "^3.2.4",
"@smithy/node-http-handler": "^3.1.4",
"@smithy/property-provider": "^3.1.3",
"@smithy/protocol-http": "^4.1.0",
"@smithy/smithy-client": "^3.2.0",
"@smithy/types": "^3.3.0",
"@smithy/util-stream": "^3.1.3",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=16.0.0"
}
},
"node_modules/@aws-sdk/client-elasticache/node_modules/@aws-sdk/credential-provider-ini": { "node_modules/@aws-sdk/client-elasticache/node_modules/@aws-sdk/credential-provider-ini": {
"version": "3.637.0", "version": "3.637.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.637.0.tgz", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.637.0.tgz",
@@ -659,16 +697,16 @@
} }
}, },
"node_modules/@aws-sdk/client-iam": { "node_modules/@aws-sdk/client-iam": {
"version": "3.635.0", "version": "3.632.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-iam/-/client-iam-3.635.0.tgz", "resolved": "https://registry.npmjs.org/@aws-sdk/client-iam/-/client-iam-3.632.0.tgz",
"integrity": "sha512-sflTv6XcwO5UX+U9x31+T6TBEgVIzG61giLgRV51kkFGErni++GpxUcc6O1mpDwb3jpbntJf7QPjrkkj9wsTPA==", "integrity": "sha512-iwivASUliVxCEbT/mu5s03SCyqQKNXbJUpG17ywT4taA2xvLisGRI5iNV3OYT1qDmK9DOLMSJYpeX2GWCijPxw==",
"dependencies": { "dependencies": {
"@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0",
"@aws-crypto/sha256-js": "5.2.0", "@aws-crypto/sha256-js": "5.2.0",
"@aws-sdk/client-sso-oidc": "3.635.0", "@aws-sdk/client-sso-oidc": "3.632.0",
"@aws-sdk/client-sts": "3.635.0", "@aws-sdk/client-sts": "3.632.0",
"@aws-sdk/core": "3.635.0", "@aws-sdk/core": "3.629.0",
"@aws-sdk/credential-provider-node": "3.635.0", "@aws-sdk/credential-provider-node": "3.632.0",
"@aws-sdk/middleware-host-header": "3.620.0", "@aws-sdk/middleware-host-header": "3.620.0",
"@aws-sdk/middleware-logger": "3.609.0", "@aws-sdk/middleware-logger": "3.609.0",
"@aws-sdk/middleware-recursion-detection": "3.620.0", "@aws-sdk/middleware-recursion-detection": "3.620.0",
@@ -679,26 +717,26 @@
"@aws-sdk/util-user-agent-browser": "3.609.0", "@aws-sdk/util-user-agent-browser": "3.609.0",
"@aws-sdk/util-user-agent-node": "3.614.0", "@aws-sdk/util-user-agent-node": "3.614.0",
"@smithy/config-resolver": "^3.0.5", "@smithy/config-resolver": "^3.0.5",
"@smithy/core": "^2.4.0", "@smithy/core": "^2.3.2",
"@smithy/fetch-http-handler": "^3.2.4", "@smithy/fetch-http-handler": "^3.2.4",
"@smithy/hash-node": "^3.0.3", "@smithy/hash-node": "^3.0.3",
"@smithy/invalid-dependency": "^3.0.3", "@smithy/invalid-dependency": "^3.0.3",
"@smithy/middleware-content-length": "^3.0.5", "@smithy/middleware-content-length": "^3.0.5",
"@smithy/middleware-endpoint": "^3.1.0", "@smithy/middleware-endpoint": "^3.1.0",
"@smithy/middleware-retry": "^3.0.15", "@smithy/middleware-retry": "^3.0.14",
"@smithy/middleware-serde": "^3.0.3", "@smithy/middleware-serde": "^3.0.3",
"@smithy/middleware-stack": "^3.0.3", "@smithy/middleware-stack": "^3.0.3",
"@smithy/node-config-provider": "^3.1.4", "@smithy/node-config-provider": "^3.1.4",
"@smithy/node-http-handler": "^3.1.4", "@smithy/node-http-handler": "^3.1.4",
"@smithy/protocol-http": "^4.1.0", "@smithy/protocol-http": "^4.1.0",
"@smithy/smithy-client": "^3.2.0", "@smithy/smithy-client": "^3.1.12",
"@smithy/types": "^3.3.0", "@smithy/types": "^3.3.0",
"@smithy/url-parser": "^3.0.3", "@smithy/url-parser": "^3.0.3",
"@smithy/util-base64": "^3.0.0", "@smithy/util-base64": "^3.0.0",
"@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0",
"@smithy/util-body-length-node": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0",
"@smithy/util-defaults-mode-browser": "^3.0.15", "@smithy/util-defaults-mode-browser": "^3.0.14",
"@smithy/util-defaults-mode-node": "^3.0.15", "@smithy/util-defaults-mode-node": "^3.0.14",
"@smithy/util-endpoints": "^2.0.5", "@smithy/util-endpoints": "^2.0.5",
"@smithy/util-middleware": "^3.0.3", "@smithy/util-middleware": "^3.0.3",
"@smithy/util-retry": "^3.0.3", "@smithy/util-retry": "^3.0.3",
@@ -711,16 +749,16 @@
} }
}, },
"node_modules/@aws-sdk/client-kms": { "node_modules/@aws-sdk/client-kms": {
"version": "3.635.0", "version": "3.632.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-kms/-/client-kms-3.635.0.tgz", "resolved": "https://registry.npmjs.org/@aws-sdk/client-kms/-/client-kms-3.632.0.tgz",
"integrity": "sha512-H2qJVXiz3WbBQwtxqfEvuJ9pCKJdqEWkzQ8I4knkXbQkyy78GktfMwBWqFyw3eap/s9rQmsyXbuuhBIozbemOg==", "integrity": "sha512-uMm1fAIdImaBKwKXnpcD1cpRlTAbLisbRbNJqzJdH+snN0jAkukLNUMUheb0XKaczk7eQrp5w4inlWrRvEmjSA==",
"dependencies": { "dependencies": {
"@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0",
"@aws-crypto/sha256-js": "5.2.0", "@aws-crypto/sha256-js": "5.2.0",
"@aws-sdk/client-sso-oidc": "3.635.0", "@aws-sdk/client-sso-oidc": "3.632.0",
"@aws-sdk/client-sts": "3.635.0", "@aws-sdk/client-sts": "3.632.0",
"@aws-sdk/core": "3.635.0", "@aws-sdk/core": "3.629.0",
"@aws-sdk/credential-provider-node": "3.635.0", "@aws-sdk/credential-provider-node": "3.632.0",
"@aws-sdk/middleware-host-header": "3.620.0", "@aws-sdk/middleware-host-header": "3.620.0",
"@aws-sdk/middleware-logger": "3.609.0", "@aws-sdk/middleware-logger": "3.609.0",
"@aws-sdk/middleware-recursion-detection": "3.620.0", "@aws-sdk/middleware-recursion-detection": "3.620.0",
@@ -731,26 +769,26 @@
"@aws-sdk/util-user-agent-browser": "3.609.0", "@aws-sdk/util-user-agent-browser": "3.609.0",
"@aws-sdk/util-user-agent-node": "3.614.0", "@aws-sdk/util-user-agent-node": "3.614.0",
"@smithy/config-resolver": "^3.0.5", "@smithy/config-resolver": "^3.0.5",
"@smithy/core": "^2.4.0", "@smithy/core": "^2.3.2",
"@smithy/fetch-http-handler": "^3.2.4", "@smithy/fetch-http-handler": "^3.2.4",
"@smithy/hash-node": "^3.0.3", "@smithy/hash-node": "^3.0.3",
"@smithy/invalid-dependency": "^3.0.3", "@smithy/invalid-dependency": "^3.0.3",
"@smithy/middleware-content-length": "^3.0.5", "@smithy/middleware-content-length": "^3.0.5",
"@smithy/middleware-endpoint": "^3.1.0", "@smithy/middleware-endpoint": "^3.1.0",
"@smithy/middleware-retry": "^3.0.15", "@smithy/middleware-retry": "^3.0.14",
"@smithy/middleware-serde": "^3.0.3", "@smithy/middleware-serde": "^3.0.3",
"@smithy/middleware-stack": "^3.0.3", "@smithy/middleware-stack": "^3.0.3",
"@smithy/node-config-provider": "^3.1.4", "@smithy/node-config-provider": "^3.1.4",
"@smithy/node-http-handler": "^3.1.4", "@smithy/node-http-handler": "^3.1.4",
"@smithy/protocol-http": "^4.1.0", "@smithy/protocol-http": "^4.1.0",
"@smithy/smithy-client": "^3.2.0", "@smithy/smithy-client": "^3.1.12",
"@smithy/types": "^3.3.0", "@smithy/types": "^3.3.0",
"@smithy/url-parser": "^3.0.3", "@smithy/url-parser": "^3.0.3",
"@smithy/util-base64": "^3.0.0", "@smithy/util-base64": "^3.0.0",
"@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0",
"@smithy/util-body-length-node": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0",
"@smithy/util-defaults-mode-browser": "^3.0.15", "@smithy/util-defaults-mode-browser": "^3.0.14",
"@smithy/util-defaults-mode-node": "^3.0.15", "@smithy/util-defaults-mode-node": "^3.0.14",
"@smithy/util-endpoints": "^2.0.5", "@smithy/util-endpoints": "^2.0.5",
"@smithy/util-middleware": "^3.0.3", "@smithy/util-middleware": "^3.0.3",
"@smithy/util-retry": "^3.0.3", "@smithy/util-retry": "^3.0.3",
@@ -762,16 +800,16 @@
} }
}, },
"node_modules/@aws-sdk/client-secrets-manager": { "node_modules/@aws-sdk/client-secrets-manager": {
"version": "3.635.0", "version": "3.632.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.635.0.tgz", "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.632.0.tgz",
"integrity": "sha512-taa+sa8xFym7ZYzybqkOVy5MAdedcIt2pKEVOReEaNkUuOwMUo+wF4QhJeyhaLPTs2l0rHR1bnwYOG+0fW0Kvg==", "integrity": "sha512-WsQhPHHK1yPfALcP1B7nBSGDzky6vFTUEXnUdfzb5Xy2cT+JTBTS6ChtQGqqOuGHDP/3t/9soqZ+L6rUCYBb/Q==",
"dependencies": { "dependencies": {
"@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0",
"@aws-crypto/sha256-js": "5.2.0", "@aws-crypto/sha256-js": "5.2.0",
"@aws-sdk/client-sso-oidc": "3.635.0", "@aws-sdk/client-sso-oidc": "3.632.0",
"@aws-sdk/client-sts": "3.635.0", "@aws-sdk/client-sts": "3.632.0",
"@aws-sdk/core": "3.635.0", "@aws-sdk/core": "3.629.0",
"@aws-sdk/credential-provider-node": "3.635.0", "@aws-sdk/credential-provider-node": "3.632.0",
"@aws-sdk/middleware-host-header": "3.620.0", "@aws-sdk/middleware-host-header": "3.620.0",
"@aws-sdk/middleware-logger": "3.609.0", "@aws-sdk/middleware-logger": "3.609.0",
"@aws-sdk/middleware-recursion-detection": "3.620.0", "@aws-sdk/middleware-recursion-detection": "3.620.0",
@@ -782,26 +820,26 @@
"@aws-sdk/util-user-agent-browser": "3.609.0", "@aws-sdk/util-user-agent-browser": "3.609.0",
"@aws-sdk/util-user-agent-node": "3.614.0", "@aws-sdk/util-user-agent-node": "3.614.0",
"@smithy/config-resolver": "^3.0.5", "@smithy/config-resolver": "^3.0.5",
"@smithy/core": "^2.4.0", "@smithy/core": "^2.3.2",
"@smithy/fetch-http-handler": "^3.2.4", "@smithy/fetch-http-handler": "^3.2.4",
"@smithy/hash-node": "^3.0.3", "@smithy/hash-node": "^3.0.3",
"@smithy/invalid-dependency": "^3.0.3", "@smithy/invalid-dependency": "^3.0.3",
"@smithy/middleware-content-length": "^3.0.5", "@smithy/middleware-content-length": "^3.0.5",
"@smithy/middleware-endpoint": "^3.1.0", "@smithy/middleware-endpoint": "^3.1.0",
"@smithy/middleware-retry": "^3.0.15", "@smithy/middleware-retry": "^3.0.14",
"@smithy/middleware-serde": "^3.0.3", "@smithy/middleware-serde": "^3.0.3",
"@smithy/middleware-stack": "^3.0.3", "@smithy/middleware-stack": "^3.0.3",
"@smithy/node-config-provider": "^3.1.4", "@smithy/node-config-provider": "^3.1.4",
"@smithy/node-http-handler": "^3.1.4", "@smithy/node-http-handler": "^3.1.4",
"@smithy/protocol-http": "^4.1.0", "@smithy/protocol-http": "^4.1.0",
"@smithy/smithy-client": "^3.2.0", "@smithy/smithy-client": "^3.1.12",
"@smithy/types": "^3.3.0", "@smithy/types": "^3.3.0",
"@smithy/url-parser": "^3.0.3", "@smithy/url-parser": "^3.0.3",
"@smithy/util-base64": "^3.0.0", "@smithy/util-base64": "^3.0.0",
"@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0",
"@smithy/util-body-length-node": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0",
"@smithy/util-defaults-mode-browser": "^3.0.15", "@smithy/util-defaults-mode-browser": "^3.0.14",
"@smithy/util-defaults-mode-node": "^3.0.15", "@smithy/util-defaults-mode-node": "^3.0.14",
"@smithy/util-endpoints": "^2.0.5", "@smithy/util-endpoints": "^2.0.5",
"@smithy/util-middleware": "^3.0.3", "@smithy/util-middleware": "^3.0.3",
"@smithy/util-retry": "^3.0.3", "@smithy/util-retry": "^3.0.3",
@@ -814,13 +852,13 @@
} }
}, },
"node_modules/@aws-sdk/client-sso": { "node_modules/@aws-sdk/client-sso": {
"version": "3.635.0", "version": "3.632.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.635.0.tgz", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.632.0.tgz",
"integrity": "sha512-/Hl69+JpFUo9JNVmh2gSvMgYkE4xjd+1okiRoPBbQqjI7YBP2JWCUDP8IoEkNq3wj0vNTq0OWfn6RpZycIkAXQ==", "integrity": "sha512-iYWHiKBz44m3chCFvtvHnvCpL2rALzyr1e6tOZV3dLlOKtQtDUlPy6OtnXDu4y+wyJCniy8ivG3+LAe4klzn1Q==",
"dependencies": { "dependencies": {
"@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0",
"@aws-crypto/sha256-js": "5.2.0", "@aws-crypto/sha256-js": "5.2.0",
"@aws-sdk/core": "3.635.0", "@aws-sdk/core": "3.629.0",
"@aws-sdk/middleware-host-header": "3.620.0", "@aws-sdk/middleware-host-header": "3.620.0",
"@aws-sdk/middleware-logger": "3.609.0", "@aws-sdk/middleware-logger": "3.609.0",
"@aws-sdk/middleware-recursion-detection": "3.620.0", "@aws-sdk/middleware-recursion-detection": "3.620.0",
@@ -831,26 +869,26 @@
"@aws-sdk/util-user-agent-browser": "3.609.0", "@aws-sdk/util-user-agent-browser": "3.609.0",
"@aws-sdk/util-user-agent-node": "3.614.0", "@aws-sdk/util-user-agent-node": "3.614.0",
"@smithy/config-resolver": "^3.0.5", "@smithy/config-resolver": "^3.0.5",
"@smithy/core": "^2.4.0", "@smithy/core": "^2.3.2",
"@smithy/fetch-http-handler": "^3.2.4", "@smithy/fetch-http-handler": "^3.2.4",
"@smithy/hash-node": "^3.0.3", "@smithy/hash-node": "^3.0.3",
"@smithy/invalid-dependency": "^3.0.3", "@smithy/invalid-dependency": "^3.0.3",
"@smithy/middleware-content-length": "^3.0.5", "@smithy/middleware-content-length": "^3.0.5",
"@smithy/middleware-endpoint": "^3.1.0", "@smithy/middleware-endpoint": "^3.1.0",
"@smithy/middleware-retry": "^3.0.15", "@smithy/middleware-retry": "^3.0.14",
"@smithy/middleware-serde": "^3.0.3", "@smithy/middleware-serde": "^3.0.3",
"@smithy/middleware-stack": "^3.0.3", "@smithy/middleware-stack": "^3.0.3",
"@smithy/node-config-provider": "^3.1.4", "@smithy/node-config-provider": "^3.1.4",
"@smithy/node-http-handler": "^3.1.4", "@smithy/node-http-handler": "^3.1.4",
"@smithy/protocol-http": "^4.1.0", "@smithy/protocol-http": "^4.1.0",
"@smithy/smithy-client": "^3.2.0", "@smithy/smithy-client": "^3.1.12",
"@smithy/types": "^3.3.0", "@smithy/types": "^3.3.0",
"@smithy/url-parser": "^3.0.3", "@smithy/url-parser": "^3.0.3",
"@smithy/util-base64": "^3.0.0", "@smithy/util-base64": "^3.0.0",
"@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0",
"@smithy/util-body-length-node": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0",
"@smithy/util-defaults-mode-browser": "^3.0.15", "@smithy/util-defaults-mode-browser": "^3.0.14",
"@smithy/util-defaults-mode-node": "^3.0.15", "@smithy/util-defaults-mode-node": "^3.0.14",
"@smithy/util-endpoints": "^2.0.5", "@smithy/util-endpoints": "^2.0.5",
"@smithy/util-middleware": "^3.0.3", "@smithy/util-middleware": "^3.0.3",
"@smithy/util-retry": "^3.0.3", "@smithy/util-retry": "^3.0.3",
@@ -862,14 +900,14 @@
} }
}, },
"node_modules/@aws-sdk/client-sso-oidc": { "node_modules/@aws-sdk/client-sso-oidc": {
"version": "3.635.0", "version": "3.632.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.635.0.tgz", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.632.0.tgz",
"integrity": "sha512-RIwDlhzAFttB1vbpznewnPqz7h1H/2UhQLwB38yfZBwYQOxyxVfLV5j5VoUUX3jY4i4qH9wiHc7b02qeAOZY6g==", "integrity": "sha512-Oh1fIWaoZluihOCb/zDEpRTi+6an82fgJz7fyRBugyLhEtDjmvpCQ3oKjzaOhoN+4EvXAm1ZS/ZgpvXBlIRTgw==",
"dependencies": { "dependencies": {
"@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0",
"@aws-crypto/sha256-js": "5.2.0", "@aws-crypto/sha256-js": "5.2.0",
"@aws-sdk/core": "3.635.0", "@aws-sdk/core": "3.629.0",
"@aws-sdk/credential-provider-node": "3.635.0", "@aws-sdk/credential-provider-node": "3.632.0",
"@aws-sdk/middleware-host-header": "3.620.0", "@aws-sdk/middleware-host-header": "3.620.0",
"@aws-sdk/middleware-logger": "3.609.0", "@aws-sdk/middleware-logger": "3.609.0",
"@aws-sdk/middleware-recursion-detection": "3.620.0", "@aws-sdk/middleware-recursion-detection": "3.620.0",
@@ -880,26 +918,26 @@
"@aws-sdk/util-user-agent-browser": "3.609.0", "@aws-sdk/util-user-agent-browser": "3.609.0",
"@aws-sdk/util-user-agent-node": "3.614.0", "@aws-sdk/util-user-agent-node": "3.614.0",
"@smithy/config-resolver": "^3.0.5", "@smithy/config-resolver": "^3.0.5",
"@smithy/core": "^2.4.0", "@smithy/core": "^2.3.2",
"@smithy/fetch-http-handler": "^3.2.4", "@smithy/fetch-http-handler": "^3.2.4",
"@smithy/hash-node": "^3.0.3", "@smithy/hash-node": "^3.0.3",
"@smithy/invalid-dependency": "^3.0.3", "@smithy/invalid-dependency": "^3.0.3",
"@smithy/middleware-content-length": "^3.0.5", "@smithy/middleware-content-length": "^3.0.5",
"@smithy/middleware-endpoint": "^3.1.0", "@smithy/middleware-endpoint": "^3.1.0",
"@smithy/middleware-retry": "^3.0.15", "@smithy/middleware-retry": "^3.0.14",
"@smithy/middleware-serde": "^3.0.3", "@smithy/middleware-serde": "^3.0.3",
"@smithy/middleware-stack": "^3.0.3", "@smithy/middleware-stack": "^3.0.3",
"@smithy/node-config-provider": "^3.1.4", "@smithy/node-config-provider": "^3.1.4",
"@smithy/node-http-handler": "^3.1.4", "@smithy/node-http-handler": "^3.1.4",
"@smithy/protocol-http": "^4.1.0", "@smithy/protocol-http": "^4.1.0",
"@smithy/smithy-client": "^3.2.0", "@smithy/smithy-client": "^3.1.12",
"@smithy/types": "^3.3.0", "@smithy/types": "^3.3.0",
"@smithy/url-parser": "^3.0.3", "@smithy/url-parser": "^3.0.3",
"@smithy/util-base64": "^3.0.0", "@smithy/util-base64": "^3.0.0",
"@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0",
"@smithy/util-body-length-node": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0",
"@smithy/util-defaults-mode-browser": "^3.0.15", "@smithy/util-defaults-mode-browser": "^3.0.14",
"@smithy/util-defaults-mode-node": "^3.0.15", "@smithy/util-defaults-mode-node": "^3.0.14",
"@smithy/util-endpoints": "^2.0.5", "@smithy/util-endpoints": "^2.0.5",
"@smithy/util-middleware": "^3.0.3", "@smithy/util-middleware": "^3.0.3",
"@smithy/util-retry": "^3.0.3", "@smithy/util-retry": "^3.0.3",
@@ -910,19 +948,19 @@
"node": ">=16.0.0" "node": ">=16.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@aws-sdk/client-sts": "^3.635.0" "@aws-sdk/client-sts": "^3.632.0"
} }
}, },
"node_modules/@aws-sdk/client-sts": { "node_modules/@aws-sdk/client-sts": {
"version": "3.635.0", "version": "3.632.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.635.0.tgz", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.632.0.tgz",
"integrity": "sha512-Al2ytE69+cbA44qHlelqhzWwbURikfF13Zkal9utIG5Q6T2c7r8p6sePN92n8l/x1v0FhJ5VTxKak+cPTE0CZQ==", "integrity": "sha512-Ss5cBH09icpTvT+jtGGuQlRdwtO7RyE9BF4ZV/CEPATdd9whtJt4Qxdya8BUnkWR7h5HHTrQHqai3YVYjku41A==",
"dependencies": { "dependencies": {
"@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0",
"@aws-crypto/sha256-js": "5.2.0", "@aws-crypto/sha256-js": "5.2.0",
"@aws-sdk/client-sso-oidc": "3.635.0", "@aws-sdk/client-sso-oidc": "3.632.0",
"@aws-sdk/core": "3.635.0", "@aws-sdk/core": "3.629.0",
"@aws-sdk/credential-provider-node": "3.635.0", "@aws-sdk/credential-provider-node": "3.632.0",
"@aws-sdk/middleware-host-header": "3.620.0", "@aws-sdk/middleware-host-header": "3.620.0",
"@aws-sdk/middleware-logger": "3.609.0", "@aws-sdk/middleware-logger": "3.609.0",
"@aws-sdk/middleware-recursion-detection": "3.620.0", "@aws-sdk/middleware-recursion-detection": "3.620.0",
@@ -933,26 +971,26 @@
"@aws-sdk/util-user-agent-browser": "3.609.0", "@aws-sdk/util-user-agent-browser": "3.609.0",
"@aws-sdk/util-user-agent-node": "3.614.0", "@aws-sdk/util-user-agent-node": "3.614.0",
"@smithy/config-resolver": "^3.0.5", "@smithy/config-resolver": "^3.0.5",
"@smithy/core": "^2.4.0", "@smithy/core": "^2.3.2",
"@smithy/fetch-http-handler": "^3.2.4", "@smithy/fetch-http-handler": "^3.2.4",
"@smithy/hash-node": "^3.0.3", "@smithy/hash-node": "^3.0.3",
"@smithy/invalid-dependency": "^3.0.3", "@smithy/invalid-dependency": "^3.0.3",
"@smithy/middleware-content-length": "^3.0.5", "@smithy/middleware-content-length": "^3.0.5",
"@smithy/middleware-endpoint": "^3.1.0", "@smithy/middleware-endpoint": "^3.1.0",
"@smithy/middleware-retry": "^3.0.15", "@smithy/middleware-retry": "^3.0.14",
"@smithy/middleware-serde": "^3.0.3", "@smithy/middleware-serde": "^3.0.3",
"@smithy/middleware-stack": "^3.0.3", "@smithy/middleware-stack": "^3.0.3",
"@smithy/node-config-provider": "^3.1.4", "@smithy/node-config-provider": "^3.1.4",
"@smithy/node-http-handler": "^3.1.4", "@smithy/node-http-handler": "^3.1.4",
"@smithy/protocol-http": "^4.1.0", "@smithy/protocol-http": "^4.1.0",
"@smithy/smithy-client": "^3.2.0", "@smithy/smithy-client": "^3.1.12",
"@smithy/types": "^3.3.0", "@smithy/types": "^3.3.0",
"@smithy/url-parser": "^3.0.3", "@smithy/url-parser": "^3.0.3",
"@smithy/util-base64": "^3.0.0", "@smithy/util-base64": "^3.0.0",
"@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0",
"@smithy/util-body-length-node": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0",
"@smithy/util-defaults-mode-browser": "^3.0.15", "@smithy/util-defaults-mode-browser": "^3.0.14",
"@smithy/util-defaults-mode-node": "^3.0.15", "@smithy/util-defaults-mode-node": "^3.0.14",
"@smithy/util-endpoints": "^2.0.5", "@smithy/util-endpoints": "^2.0.5",
"@smithy/util-middleware": "^3.0.3", "@smithy/util-middleware": "^3.0.3",
"@smithy/util-retry": "^3.0.3", "@smithy/util-retry": "^3.0.3",
@@ -964,16 +1002,16 @@
} }
}, },
"node_modules/@aws-sdk/core": { "node_modules/@aws-sdk/core": {
"version": "3.635.0", "version": "3.629.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.635.0.tgz", "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.629.0.tgz",
"integrity": "sha512-i1x/E/sgA+liUE1XJ7rj1dhyXpAKO1UKFUcTTHXok2ARjWTvszHnSXMOsB77aPbmn0fUp1JTx2kHUAZ1LVt5Bg==", "integrity": "sha512-+/ShPU/tyIBM3oY1cnjgNA/tFyHtlWq+wXF9xEKRv19NOpYbWQ+xzNwVjGq8vR07cCRqy/sDQLWPhxjtuV/FiQ==",
"dependencies": { "dependencies": {
"@smithy/core": "^2.4.0", "@smithy/core": "^2.3.2",
"@smithy/node-config-provider": "^3.1.4", "@smithy/node-config-provider": "^3.1.4",
"@smithy/property-provider": "^3.1.3", "@smithy/property-provider": "^3.1.3",
"@smithy/protocol-http": "^4.1.0", "@smithy/protocol-http": "^4.1.0",
"@smithy/signature-v4": "^4.1.0", "@smithy/signature-v4": "^4.1.0",
"@smithy/smithy-client": "^3.2.0", "@smithy/smithy-client": "^3.1.12",
"@smithy/types": "^3.3.0", "@smithy/types": "^3.3.0",
"@smithy/util-middleware": "^3.0.3", "@smithy/util-middleware": "^3.0.3",
"fast-xml-parser": "4.4.1", "fast-xml-parser": "4.4.1",
@@ -998,16 +1036,16 @@
} }
}, },
"node_modules/@aws-sdk/credential-provider-http": { "node_modules/@aws-sdk/credential-provider-http": {
"version": "3.635.0", "version": "3.622.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.635.0.tgz", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.622.0.tgz",
"integrity": "sha512-iJyRgEjOCQlBMXqtwPLIKYc7Bsc6nqjrZybdMDenPDa+kmLg7xh8LxHsu9088e+2/wtLicE34FsJJIfzu3L82g==", "integrity": "sha512-VUHbr24Oll1RK3WR8XLUugLpgK9ZuxEm/NVeVqyFts1Ck9gsKpRg1x4eH7L7tW3SJ4TDEQNMbD7/7J+eoL2svg==",
"dependencies": { "dependencies": {
"@aws-sdk/types": "3.609.0", "@aws-sdk/types": "3.609.0",
"@smithy/fetch-http-handler": "^3.2.4", "@smithy/fetch-http-handler": "^3.2.4",
"@smithy/node-http-handler": "^3.1.4", "@smithy/node-http-handler": "^3.1.4",
"@smithy/property-provider": "^3.1.3", "@smithy/property-provider": "^3.1.3",
"@smithy/protocol-http": "^4.1.0", "@smithy/protocol-http": "^4.1.0",
"@smithy/smithy-client": "^3.2.0", "@smithy/smithy-client": "^3.1.12",
"@smithy/types": "^3.3.0", "@smithy/types": "^3.3.0",
"@smithy/util-stream": "^3.1.3", "@smithy/util-stream": "^3.1.3",
"tslib": "^2.6.2" "tslib": "^2.6.2"
@@ -1017,14 +1055,14 @@
} }
}, },
"node_modules/@aws-sdk/credential-provider-ini": { "node_modules/@aws-sdk/credential-provider-ini": {
"version": "3.635.0", "version": "3.632.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.635.0.tgz", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.632.0.tgz",
"integrity": "sha512-+OqcNhhOFFY08YHLjO9/Y1n37RKAO7LADnsJ7VTXca7IfvYh27BVBn+FdlqnyEb1MQ5ArHTY4pq3pKRIg6RW4Q==", "integrity": "sha512-m6epoW41xa1ajU5OiHcmQHoGVtrbXBaRBOUhlCLZmcaqMLYsboM4iD/WZP8aatKEON5tTnVXh/4StV8D/+wemw==",
"dependencies": { "dependencies": {
"@aws-sdk/credential-provider-env": "3.620.1", "@aws-sdk/credential-provider-env": "3.620.1",
"@aws-sdk/credential-provider-http": "3.635.0", "@aws-sdk/credential-provider-http": "3.622.0",
"@aws-sdk/credential-provider-process": "3.620.1", "@aws-sdk/credential-provider-process": "3.620.1",
"@aws-sdk/credential-provider-sso": "3.635.0", "@aws-sdk/credential-provider-sso": "3.632.0",
"@aws-sdk/credential-provider-web-identity": "3.621.0", "@aws-sdk/credential-provider-web-identity": "3.621.0",
"@aws-sdk/types": "3.609.0", "@aws-sdk/types": "3.609.0",
"@smithy/credential-provider-imds": "^3.2.0", "@smithy/credential-provider-imds": "^3.2.0",
@@ -1037,19 +1075,19 @@
"node": ">=16.0.0" "node": ">=16.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@aws-sdk/client-sts": "^3.635.0" "@aws-sdk/client-sts": "^3.632.0"
} }
}, },
"node_modules/@aws-sdk/credential-provider-node": { "node_modules/@aws-sdk/credential-provider-node": {
"version": "3.635.0", "version": "3.632.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.635.0.tgz", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.632.0.tgz",
"integrity": "sha512-bmd23mnb94S6AxmWPgqJTnvT9ONKlTx7EPafE1RNO+vUl6mHih4iyqX6ZPaRcSfaPx4U1R7H1RM8cSnafXgaBg==", "integrity": "sha512-cL8fuJWm/xQBO4XJPkeuZzl3XinIn9EExWgzpG48NRMKR5us1RI/ucv7xFbBBaG+r/sDR2HpYBIA3lVIpm1H3Q==",
"dependencies": { "dependencies": {
"@aws-sdk/credential-provider-env": "3.620.1", "@aws-sdk/credential-provider-env": "3.620.1",
"@aws-sdk/credential-provider-http": "3.635.0", "@aws-sdk/credential-provider-http": "3.622.0",
"@aws-sdk/credential-provider-ini": "3.635.0", "@aws-sdk/credential-provider-ini": "3.632.0",
"@aws-sdk/credential-provider-process": "3.620.1", "@aws-sdk/credential-provider-process": "3.620.1",
"@aws-sdk/credential-provider-sso": "3.635.0", "@aws-sdk/credential-provider-sso": "3.632.0",
"@aws-sdk/credential-provider-web-identity": "3.621.0", "@aws-sdk/credential-provider-web-identity": "3.621.0",
"@aws-sdk/types": "3.609.0", "@aws-sdk/types": "3.609.0",
"@smithy/credential-provider-imds": "^3.2.0", "@smithy/credential-provider-imds": "^3.2.0",
@@ -1078,11 +1116,11 @@
} }
}, },
"node_modules/@aws-sdk/credential-provider-sso": { "node_modules/@aws-sdk/credential-provider-sso": {
"version": "3.635.0", "version": "3.632.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.635.0.tgz", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.632.0.tgz",
"integrity": "sha512-hO/fKyvUaGpK9zyvCnmJz70EputvGWDr2UTOn/RzvcR6UB4yXoFf0QcCMubEsE3v67EsAv6PadgOeJ0vz6IazA==", "integrity": "sha512-P/4wB6j7ym5QCPTL2xlMfvf2NcXSh+z0jmsZP4WW/tVwab4hvgabPPbLeEZDSWZ0BpgtxKGvRq0GSHuGeirQbA==",
"dependencies": { "dependencies": {
"@aws-sdk/client-sso": "3.635.0", "@aws-sdk/client-sso": "3.632.0",
"@aws-sdk/token-providers": "3.614.0", "@aws-sdk/token-providers": "3.614.0",
"@aws-sdk/types": "3.609.0", "@aws-sdk/types": "3.609.0",
"@smithy/property-provider": "^3.1.3", "@smithy/property-provider": "^3.1.3",
@@ -11289,12 +11327,6 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/globrex": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz",
"integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==",
"dev": true
},
"node_modules/google-auth-library": { "node_modules/google-auth-library": {
"version": "9.9.0", "version": "9.9.0",
"resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.9.0.tgz", "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.9.0.tgz",
@@ -16603,26 +16635,6 @@
"node": "^12.20.0 || >=14" "node": "^12.20.0 || >=14"
} }
}, },
"node_modules/tsconfck": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-2.1.2.tgz",
"integrity": "sha512-ghqN1b0puy3MhhviwO2kGF8SeMDNhEbnKxjK7h6+fvY9JAxqvXi8y5NAHSQv687OVboS2uZIByzGd45/YxrRHg==",
"dev": true,
"bin": {
"tsconfck": "bin/tsconfck.js"
},
"engines": {
"node": "^14.13.1 || ^16 || >=18"
},
"peerDependencies": {
"typescript": "^4.3.5 || ^5.0.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/tsconfig-paths": { "node_modules/tsconfig-paths": {
"version": "4.2.0", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz",
@@ -17779,48 +17791,6 @@
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true "dev": true
}, },
"node_modules/vite-tsconfig-paths": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-4.2.2.tgz",
"integrity": "sha512-dq0FjyxHHDnp0uS3P12WEOX2W7NeuLzX9AWP38D7Zw2CTbFErapwQVlCiT5DMJcVWKQ1MMdTe92PZl/rBQ7qcw==",
"dev": true,
"dependencies": {
"debug": "^4.1.1",
"globrex": "^0.1.2",
"tsconfck": "^2.1.0"
},
"peerDependencies": {
"vite": "*"
},
"peerDependenciesMeta": {
"vite": {
"optional": true
}
}
},
"node_modules/vite-tsconfig-paths/node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dev": true,
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/vite-tsconfig-paths/node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"node_modules/vite/node_modules/@esbuild/android-arm": { "node_modules/vite/node_modules/@esbuild/android-arm": {
"version": "0.20.2", "version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz",
-1
View File
@@ -103,7 +103,6 @@
"tsup": "^8.0.1", "tsup": "^8.0.1",
"tsx": "^4.4.0", "tsx": "^4.4.0",
"typescript": "^5.3.2", "typescript": "^5.3.2",
"vite-tsconfig-paths": "^4.2.2",
"vitest": "^1.2.2" "vitest": "^1.2.2"
}, },
"dependencies": { "dependencies": {
@@ -115,7 +115,14 @@ export async function down(knex: Knex): Promise<void> {
// eslint-disable-next-line // eslint-disable-next-line
// @ts-ignore because generate schema happens after this // @ts-ignore because generate schema happens after this
approverId: knex(TableName.ProjectMembership) approverId: knex(TableName.ProjectMembership)
.select("id") .join(
TableName.SecretApprovalPolicy,
`${TableName.SecretApprovalPolicy}.id`,
`${TableName.SecretApprovalPolicyApprover}.policyId`
)
.join(TableName.Environment, `${TableName.Environment}.id`, `${TableName.SecretApprovalPolicy}.envId`)
.select(knex.ref("id").withSchema(TableName.ProjectMembership))
.where(`${TableName.ProjectMembership}.projectId`, knex.raw("??", [`${TableName.Environment}.projectId`]))
.where("userId", knex.raw("??", [`${TableName.SecretApprovalPolicyApprover}.approverUserId`])) .where("userId", knex.raw("??", [`${TableName.SecretApprovalPolicyApprover}.approverUserId`]))
}); });
await knex.schema.alterTable(TableName.SecretApprovalPolicyApprover, (tb) => { await knex.schema.alterTable(TableName.SecretApprovalPolicyApprover, (tb) => {
@@ -147,13 +154,27 @@ export async function down(knex: Knex): Promise<void> {
// eslint-disable-next-line // eslint-disable-next-line
// @ts-ignore because generate schema happens after this // @ts-ignore because generate schema happens after this
committerId: knex(TableName.ProjectMembership) committerId: knex(TableName.ProjectMembership)
.select("id") .join(
.where("userId", knex.raw("??", [`${TableName.SecretApprovalRequest}.committerUserId`])), TableName.SecretApprovalPolicy,
`${TableName.SecretApprovalPolicy}.id`,
`${TableName.SecretApprovalRequest}.policyId`
)
.join(TableName.Environment, `${TableName.Environment}.id`, `${TableName.SecretApprovalPolicy}.envId`)
.where(`${TableName.ProjectMembership}.projectId`, knex.raw("??", [`${TableName.Environment}.projectId`]))
.where("userId", knex.raw("??", [`${TableName.SecretApprovalRequest}.committerUserId`]))
.select(knex.ref("id").withSchema(TableName.ProjectMembership)),
// eslint-disable-next-line // eslint-disable-next-line
// @ts-ignore because generate schema happens after this // @ts-ignore because generate schema happens after this
statusChangeBy: knex(TableName.ProjectMembership) statusChangeBy: knex(TableName.ProjectMembership)
.select("id") .join(
TableName.SecretApprovalPolicy,
`${TableName.SecretApprovalPolicy}.id`,
`${TableName.SecretApprovalRequest}.policyId`
)
.join(TableName.Environment, `${TableName.Environment}.id`, `${TableName.SecretApprovalPolicy}.envId`)
.where(`${TableName.ProjectMembership}.projectId`, knex.raw("??", [`${TableName.Environment}.projectId`]))
.where("userId", knex.raw("??", [`${TableName.SecretApprovalRequest}.statusChangedByUserId`])) .where("userId", knex.raw("??", [`${TableName.SecretApprovalRequest}.statusChangedByUserId`]))
.select(knex.ref("id").withSchema(TableName.ProjectMembership))
}); });
await knex.schema.alterTable(TableName.SecretApprovalRequest, (tb) => { await knex.schema.alterTable(TableName.SecretApprovalRequest, (tb) => {
@@ -177,8 +198,20 @@ export async function down(knex: Knex): Promise<void> {
// eslint-disable-next-line // eslint-disable-next-line
// @ts-ignore because generate schema happens after this // @ts-ignore because generate schema happens after this
member: knex(TableName.ProjectMembership) member: knex(TableName.ProjectMembership)
.select("id") .join(
TableName.SecretApprovalRequest,
`${TableName.SecretApprovalRequest}.id`,
`${TableName.SecretApprovalRequestReviewer}.requestId`
)
.join(
TableName.SecretApprovalPolicy,
`${TableName.SecretApprovalPolicy}.id`,
`${TableName.SecretApprovalRequest}.policyId`
)
.join(TableName.Environment, `${TableName.Environment}.id`, `${TableName.SecretApprovalPolicy}.envId`)
.where(`${TableName.ProjectMembership}.projectId`, knex.raw("??", [`${TableName.Environment}.projectId`]))
.where("userId", knex.raw("??", [`${TableName.SecretApprovalRequestReviewer}.reviewerUserId`])) .where("userId", knex.raw("??", [`${TableName.SecretApprovalRequestReviewer}.reviewerUserId`]))
.select(knex.ref("id").withSchema(TableName.ProjectMembership))
}); });
await knex.schema.alterTable(TableName.SecretApprovalRequestReviewer, (tb) => { await knex.schema.alterTable(TableName.SecretApprovalRequestReviewer, (tb) => {
tb.uuid("member").notNullable().alter(); tb.uuid("member").notNullable().alter();
+1 -1
View File
@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
// TODO(akhilmhdh): Fix this when licence service gets it type // TODO(akhilmhdh): Fix this when license service gets it type
import { z } from "zod"; import { z } from "zod";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
@@ -26,8 +26,10 @@ export const getDefaultOnPremFeatures = () => {
status: null, status: null,
trial_end: null, trial_end: null,
has_used_trial: true, has_used_trial: true,
secretApproval: false, secretApproval: true,
secretRotation: true, secretRotation: true,
caCrl: false caCrl: false
}; };
}; };
export const setupLicenseRequestWithStore = () => {};
@@ -49,15 +49,15 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
pkiEst: false pkiEst: false
}); });
export const setupLicenceRequestWithStore = (baseURL: string, refreshUrl: string, licenseKey: string) => { export const setupLicenseRequestWithStore = (baseURL: string, refreshUrl: string, licenseKey: string) => {
let token: string; let token: string;
const licenceReq = axios.create({ const licenseReq = axios.create({
baseURL, baseURL,
timeout: 35 * 1000 timeout: 35 * 1000
// signal: AbortSignal.timeout(60 * 1000) // signal: AbortSignal.timeout(60 * 1000)
}); });
const refreshLicence = async () => { const refreshLicense = async () => {
const appCfg = getConfig(); const appCfg = getConfig();
const { const {
data: { token: authToken } data: { token: authToken }
@@ -75,7 +75,7 @@ export const setupLicenceRequestWithStore = (baseURL: string, refreshUrl: string
return token; return token;
}; };
licenceReq.interceptors.request.use( licenseReq.interceptors.request.use(
(config) => { (config) => {
if (token && config.headers) { if (token && config.headers) {
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
@@ -86,7 +86,7 @@ export const setupLicenceRequestWithStore = (baseURL: string, refreshUrl: string
(err) => Promise.reject(err) (err) => Promise.reject(err)
); );
licenceReq.interceptors.response.use( licenseReq.interceptors.response.use(
(response) => response, (response) => response,
async (err) => { async (err) => {
const originalRequest = (err as AxiosError).config; const originalRequest = (err as AxiosError).config;
@@ -97,15 +97,15 @@ export const setupLicenceRequestWithStore = (baseURL: string, refreshUrl: string
(originalRequest as any)._retry = true; // injected (originalRequest as any)._retry = true; // injected
// refresh // refresh
await refreshLicence(); await refreshLicense();
licenceReq.defaults.headers.common.Authorization = `Bearer ${token}`; licenseReq.defaults.headers.common.Authorization = `Bearer ${token}`;
return licenceReq(originalRequest!); return licenseReq(originalRequest!);
} }
return Promise.reject(err); return Promise.reject(err);
} }
); );
return { request: licenceReq, refreshLicence }; return { request: licenseReq, refreshLicense };
}; };
@@ -16,8 +16,8 @@ import { TOrgDALFactory } from "@app/services/org/org-dal";
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission"; import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
import { TPermissionServiceFactory } from "../permission/permission-service"; import { TPermissionServiceFactory } from "../permission/permission-service";
import { getDefaultOnPremFeatures, setupLicenceRequestWithStore } from "./licence-fns";
import { TLicenseDALFactory } from "./license-dal"; import { TLicenseDALFactory } from "./license-dal";
import { getDefaultOnPremFeatures, setupLicenseRequestWithStore } from "./license-fns";
import { import {
InstanceType, InstanceType,
TAddOrgPmtMethodDTO, TAddOrgPmtMethodDTO,
@@ -64,13 +64,13 @@ export const licenseServiceFactory = ({
let onPremFeatures: TFeatureSet = getDefaultOnPremFeatures(); let onPremFeatures: TFeatureSet = getDefaultOnPremFeatures();
const appCfg = getConfig(); const appCfg = getConfig();
const licenseServerCloudApi = setupLicenceRequestWithStore( const licenseServerCloudApi = setupLicenseRequestWithStore(
appCfg.LICENSE_SERVER_URL || "", appCfg.LICENSE_SERVER_URL || "",
LICENSE_SERVER_CLOUD_LOGIN, LICENSE_SERVER_CLOUD_LOGIN,
appCfg.LICENSE_SERVER_KEY || "" appCfg.LICENSE_SERVER_KEY || ""
); );
const licenseServerOnPremApi = setupLicenceRequestWithStore( const licenseServerOnPremApi = setupLicenseRequestWithStore(
appCfg.LICENSE_SERVER_URL || "", appCfg.LICENSE_SERVER_URL || "",
LICENSE_SERVER_ON_PREM_LOGIN, LICENSE_SERVER_ON_PREM_LOGIN,
appCfg.LICENSE_KEY || "" appCfg.LICENSE_KEY || ""
@@ -79,7 +79,7 @@ export const licenseServiceFactory = ({
const init = async () => { const init = async () => {
try { try {
if (appCfg.LICENSE_SERVER_KEY) { if (appCfg.LICENSE_SERVER_KEY) {
const token = await licenseServerCloudApi.refreshLicence(); const token = await licenseServerCloudApi.refreshLicense();
if (token) instanceType = InstanceType.Cloud; if (token) instanceType = InstanceType.Cloud;
logger.info(`Instance type: ${InstanceType.Cloud}`); logger.info(`Instance type: ${InstanceType.Cloud}`);
isValidLicense = true; isValidLicense = true;
@@ -87,7 +87,7 @@ export const licenseServiceFactory = ({
} }
if (appCfg.LICENSE_KEY) { if (appCfg.LICENSE_KEY) {
const token = await licenseServerOnPremApi.refreshLicence(); const token = await licenseServerOnPremApi.refreshLicense();
if (token) { if (token) {
const { const {
data: { currentPlan } data: { currentPlan }
@@ -30,6 +30,7 @@ const getIntegrationSecretsV2 = async (
environment: string; environment: string;
folderId: string; folderId: string;
depth: number; depth: number;
secretPath: string;
decryptor: (value: Buffer | null | undefined) => string; decryptor: (value: Buffer | null | undefined) => string;
}, },
secretV2BridgeDAL: Pick<TSecretV2BridgeDALFactory, "find" | "findByFolderId">, secretV2BridgeDAL: Pick<TSecretV2BridgeDALFactory, "find" | "findByFolderId">,
@@ -306,6 +307,7 @@ export const deleteIntegrationSecrets = async ({
? await getIntegrationSecretsV2( ? await getIntegrationSecretsV2(
{ {
environment: integration.environment.id, environment: integration.environment.id,
secretPath: integration.secretPath,
projectId: integration.projectId, projectId: integration.projectId,
folderId: folder.id, folderId: folder.id,
depth: 1, depth: 1,
@@ -158,9 +158,12 @@ export const fnSecretsV2FromImports = async ({
depth?: number; depth?: number;
cyclicDetector?: Set<string>; cyclicDetector?: Set<string>;
decryptor: (value?: Buffer | null) => string; decryptor: (value?: Buffer | null) => string;
expandSecretReferences?: ( expandSecretReferences?: (inputSecret: {
secrets: Record<string, { value?: string; comment?: string; skipMultilineEncoding?: boolean | null }> value?: string;
) => Promise<Record<string, { value?: string; comment?: string; skipMultilineEncoding?: boolean | null }>>; skipMultilineEncoding?: boolean | null;
secretPath: string;
environment: string;
}) => Promise<string | undefined>;
}) => { }) => {
// avoid going more than a depth // avoid going more than a depth
if (depth >= LEVEL_BREAK) return []; if (depth >= LEVEL_BREAK) return [];
@@ -244,26 +247,21 @@ export const fnSecretsV2FromImports = async ({
}); });
if (expandSecretReferences) { if (expandSecretReferences) {
await Promise.all( await Promise.allSettled(
processedImports.map(async (processedImport) => { processedImports.map((processedImport) =>
const secretsGroupByKey = processedImport.secrets.reduce( Promise.allSettled(
(acc, item) => { processedImport.secrets.map(async (decryptedSecret, index) => {
acc[item.secretKey] = { const expandedSecretValue = await expandSecretReferences({
value: item.secretValue, value: decryptedSecret.secretValue,
comment: item.secretComment, secretPath: processedImport.secretPath,
skipMultilineEncoding: item.skipMultilineEncoding environment: processedImport.environment,
}; skipMultilineEncoding: decryptedSecret.skipMultilineEncoding
return acc; });
}, // eslint-disable-next-line no-param-reassign
{} as Record<string, { value: string; comment?: string; skipMultilineEncoding?: boolean | null }> processedImport.secrets[index].secretValue = expandedSecretValue || "";
); })
// eslint-disable-next-line )
await expandSecretReferences(secretsGroupByKey); )
processedImport.secrets.forEach((decryptedSecret) => {
// eslint-disable-next-line no-param-reassign
decryptedSecret.secretValue = secretsGroupByKey[decryptedSecret.secretKey].value;
});
})
); );
} }
@@ -377,150 +377,116 @@ type TInterpolateSecretArg = {
folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath">; folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath">;
}; };
const MAX_SECRET_REFERENCE_DEPTH = 10;
export const expandSecretReferencesFactory = ({ export const expandSecretReferencesFactory = ({
projectId, projectId,
decryptSecretValue: decryptSecret, decryptSecretValue: decryptSecret,
secretDAL, secretDAL,
folderDAL folderDAL
}: TInterpolateSecretArg) => { }: TInterpolateSecretArg) => {
const fetchSecretFactory = () => { const secretCache: Record<string, Record<string, string>> = {};
const secretCache: Record<string, Record<string, string>> = {}; const getCacheUniqueKey = (environment: string, secretPath: string) => `${environment}-${secretPath}`;
return async (secRefEnv: string, secRefPath: string[], secRefKey: string) => { const fetchSecret = async (environment: string, secretPath: string, secretKey: string) => {
const referredSecretPathURL = path.join("/", ...secRefPath); const cacheKey = getCacheUniqueKey(environment, secretPath);
const uniqueKey = `${secRefEnv}-${referredSecretPathURL}`;
if (secretCache?.[uniqueKey]) { if (secretCache?.[cacheKey]) {
return secretCache[uniqueKey][secRefKey]; return secretCache[cacheKey][secretKey] || "";
} }
const folder = await folderDAL.findBySecretPath(projectId, secRefEnv, referredSecretPathURL); const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
if (!folder) return ""; if (!folder) return "";
const secrets = await secretDAL.findByFolderId(folder.id); const secrets = await secretDAL.findByFolderId(folder.id);
const decryptedSecret = secrets.reduce<Record<string, string>>((prev, secret) => { const decryptedSecret = secrets.reduce<Record<string, string>>((prev, secret) => {
// eslint-disable-next-line // eslint-disable-next-line no-param-reassign
prev[secret.key] = decryptSecret(secret.encryptedValue) || ""; prev[secret.key] = decryptSecret(secret.encryptedValue) || "";
return prev; return prev;
}, {}); }, {});
secretCache[uniqueKey] = decryptedSecret; secretCache[cacheKey] = decryptedSecret;
return secretCache[uniqueKey][secRefKey]; return secretCache[cacheKey][secretKey] || "";
};
}; };
const recursivelyExpandSecret = async ( const recursivelyExpandSecret = async (dto: { value?: string; secretPath: string; environment: string }) => {
expandedSec: Record<string, string | undefined>, if (!dto.value) return "";
interpolatedSec: Record<string, string | undefined>,
fetchSecret: (env: string, secPath: string[], secKey: string) => Promise<string>,
recursionChainBreaker: Record<string, boolean>,
key: string
): Promise<string | undefined> => {
if (expandedSec?.[key] !== undefined) {
return expandedSec[key];
}
if (recursionChainBreaker?.[key]) {
return "";
}
// eslint-disable-next-line
recursionChainBreaker[key] = true;
let interpolatedValue = interpolatedSec[key]; const stack = [{ ...dto, depth: 0 }];
if (!interpolatedValue) { let expandedValue = dto.value;
// eslint-disable-next-line no-console
console.error(`Couldn't find referenced value - ${key}`);
return "";
}
const refs = interpolatedValue.match(INTERPOLATION_SYNTAX_REG); while (stack.length) {
if (refs) { const { value, secretPath, environment, depth } = stack.pop()!;
for (const interpolationSyntax of refs) { // eslint-disable-next-line no-continue
const interpolationKey = interpolationSyntax.slice(2, interpolationSyntax.length - 1); if (depth > MAX_SECRET_REFERENCE_DEPTH) continue;
const entities = interpolationKey.trim().split("."); const refs = value?.match(INTERPOLATION_SYNTAX_REG);
if (entities.length === 1) { if (refs) {
// eslint-disable-next-line for (const interpolationSyntax of refs) {
const val = await recursivelyExpandSecret( const interpolationKey = interpolationSyntax.slice(2, interpolationSyntax.length - 1);
expandedSec, const entities = interpolationKey.trim().split(".");
interpolatedSec,
fetchSecret,
recursionChainBreaker,
interpolationKey
);
if (val) {
interpolatedValue = interpolatedValue.replaceAll(interpolationSyntax, val);
}
// eslint-disable-next-line
continue;
}
if (entities.length > 1) { // eslint-disable-next-line no-continue
const secRefEnv = entities[0]; if (!entities.length) continue;
const secRefPath = entities.slice(1, entities.length - 1);
const secRefKey = entities[entities.length - 1];
// eslint-disable-next-line if (entities.length === 1) {
const val = await fetchSecret(secRefEnv, secRefPath, secRefKey); const [secretKey] = entities;
if (val) {
interpolatedValue = interpolatedValue.replaceAll(interpolationSyntax, val); // eslint-disable-next-line no-continue,no-await-in-loop
const referedValue = await fetchSecret(environment, secretPath, secretKey);
const cacheKey = getCacheUniqueKey(environment, secretPath);
secretCache[cacheKey][secretKey] = referedValue;
if (INTERPOLATION_SYNTAX_REG.test(referedValue)) {
stack.push({
value: referedValue,
secretPath,
environment,
depth: depth + 1
});
}
expandedValue = expandedValue.replaceAll(interpolationSyntax, referedValue);
} else {
const secretReferenceEnvironment = entities[0];
const secretReferencePath = path.join("/", ...entities.slice(1, entities.length - 1));
const secretReferenceKey = entities[entities.length - 1];
// eslint-disable-next-line no-await-in-loop
const referedValue = await fetchSecret(secretReferenceEnvironment, secretReferencePath, secretReferenceKey);
const cacheKey = getCacheUniqueKey(secretReferenceEnvironment, secretReferencePath);
secretCache[cacheKey][secretReferenceKey] = referedValue;
if (INTERPOLATION_SYNTAX_REG.test(referedValue)) {
stack.push({
value: referedValue,
secretPath: secretReferencePath,
environment: secretReferenceEnvironment,
depth: depth + 1
});
}
expandedValue = expandedValue.replaceAll(interpolationSyntax, referedValue);
} }
} }
} }
} }
// eslint-disable-next-line return expandedValue;
expandedSec[key] = interpolatedValue;
return interpolatedValue;
}; };
const fetchSecret = fetchSecretFactory(); const expandSecret = async (inputSecret: {
const expandSecrets = async ( value?: string;
inputSecrets: Record<string, { value?: string; comment?: string; skipMultilineEncoding?: boolean | null }> skipMultilineEncoding?: boolean | null;
) => { secretPath: string;
const expandedSecrets: Record<string, string | undefined> = {}; environment: string;
const toBeExpandedSecrets: Record<string, string | undefined> = {}; }) => {
if (!inputSecret.value) return inputSecret.value;
Object.keys(inputSecrets).forEach((key) => { const shouldExpand = Boolean(inputSecret.value?.match(INTERPOLATION_SYNTAX_REG));
if (inputSecrets[key].value?.match(INTERPOLATION_SYNTAX_REG)) { if (!shouldExpand) return inputSecret.value;
toBeExpandedSecrets[key] = inputSecrets[key].value;
} else {
expandedSecrets[key] = inputSecrets[key].value;
}
});
for (const key of Object.keys(inputSecrets)) { const expandedSecretValue = await recursivelyExpandSecret(inputSecret);
if (expandedSecrets?.[key]) { return inputSecret.skipMultilineEncoding ? formatMultiValueEnv(expandedSecretValue) : expandedSecretValue;
// should not do multi line encoding if user has set it to skip
// eslint-disable-next-line
inputSecrets[key].value = inputSecrets[key].skipMultilineEncoding
? formatMultiValueEnv(expandedSecrets[key])
: expandedSecrets[key];
// eslint-disable-next-line
continue;
}
// this is to avoid recursion loop. So the graph should be direct graph rather than cyclic
// so for any recursion building if there is an entity two times same key meaning it will be looped
const recursionChainBreaker: Record<string, boolean> = {};
// eslint-disable-next-line
const expandedVal = await recursivelyExpandSecret(
expandedSecrets,
toBeExpandedSecrets,
fetchSecret,
recursionChainBreaker,
key
);
// eslint-disable-next-line
inputSecrets[key].value = inputSecrets[key].skipMultilineEncoding
? formatMultiValueEnv(expandedVal)
: expandedVal;
}
return inputSecrets;
}; };
return expandSecrets; return expandSecret;
}; };
export const reshapeBridgeSecret = ( export const reshapeBridgeSecret = (
@@ -521,27 +521,22 @@ export const secretV2BridgeServiceFactory = ({
if (shouldExpandSecretReferences) { if (shouldExpandSecretReferences) {
const secretsGroupByPath = groupBy(filteredSecrets, (i) => i.secretPath); const secretsGroupByPath = groupBy(filteredSecrets, (i) => i.secretPath);
for (const secretPathKey in secretsGroupByPath) { await Promise.allSettled(
if (Object.hasOwn(secretsGroupByPath, secretPathKey)) { Object.keys(secretsGroupByPath).map((groupedPath) =>
const secretsGroupByKey = secretsGroupByPath[secretPathKey].reduce( Promise.allSettled(
(acc, item) => { secretsGroupByPath[groupedPath].map(async (decryptedSecret, index) => {
acc[item.secretKey] = { const expandedSecretValue = await expandSecretReferences({
value: item.secretValue, value: decryptedSecret.secretValue,
comment: item.secretComment, secretPath: groupedPath,
skipMultilineEncoding: item.skipMultilineEncoding environment,
}; skipMultilineEncoding: decryptedSecret.skipMultilineEncoding
return acc; });
}, // eslint-disable-next-line no-param-reassign
{} as Record<string, { value?: string; comment?: string; skipMultilineEncoding?: boolean | null }> secretsGroupByPath[groupedPath][index].secretValue = expandedSecretValue || "";
); })
// eslint-disable-next-line )
await expandSecretReferences(secretsGroupByKey); )
secretsGroupByPath[secretPathKey].forEach((decryptedSecret) => { );
// eslint-disable-next-line no-param-reassign
decryptedSecret.secretValue = secretsGroupByKey[decryptedSecret.secretKey].value || "";
});
}
}
} }
if (!includeImports) { if (!includeImports) {
@@ -693,12 +688,14 @@ export const secretV2BridgeServiceFactory = ({
? secretManagerDecryptor({ cipherTextBlob: secret.encryptedValue }).toString() ? secretManagerDecryptor({ cipherTextBlob: secret.encryptedValue }).toString()
: ""; : "";
if (shouldExpandSecretReferences && secretValue) { if (shouldExpandSecretReferences && secretValue) {
const secretReferenceExpandedRecord = {
[secret.key]: { value: secretValue }
};
// eslint-disable-next-line // eslint-disable-next-line
await expandSecretReferences(secretReferenceExpandedRecord); const expandedSecretValue = await expandSecretReferences({
secretValue = secretReferenceExpandedRecord[secret.key].value; environment,
secretPath: path,
value: secretValue,
skipMultilineEncoding: secret.skipMultilineEncoding
});
secretValue = expandedSecretValue || "";
} }
return reshapeBridgeSecret(projectId, environment, path, { return reshapeBridgeSecret(projectId, environment, path, {
+98 -125
View File
@@ -196,6 +196,13 @@ export const recursivelyGetSecretPaths = ({
return getPaths; return getPaths;
}; };
// used to convert multi line ones to quotes ones with \n
const formatMultiValueEnv = (val?: string) => {
if (!val) return "";
if (!val.match("\n")) return val;
return `"${val.replace(/\n/g, "\\n")}"`;
};
type TInterpolateSecretArg = { type TInterpolateSecretArg = {
projectId: string; projectId: string;
secretEncKey: string; secretEncKey: string;
@@ -203,162 +210,128 @@ type TInterpolateSecretArg = {
folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath">; folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath">;
}; };
const MAX_SECRET_REFERENCE_DEPTH = 5;
const INTERPOLATION_SYNTAX_REG = /\${([^}]+)}/g; const INTERPOLATION_SYNTAX_REG = /\${([^}]+)}/g;
export const interpolateSecrets = ({ projectId, secretEncKey, secretDAL, folderDAL }: TInterpolateSecretArg) => { export const interpolateSecrets = ({ projectId, secretEncKey, secretDAL, folderDAL }: TInterpolateSecretArg) => {
const fetchSecretsCrossEnv = () => { const secretCache: Record<string, Record<string, string>> = {};
const fetchCache: Record<string, Record<string, string>> = {}; const getCacheUniqueKey = (environment: string, secretPath: string) => `${environment}-${secretPath}`;
return async (secRefEnv: string, secRefPath: string[], secRefKey: string) => { const fetchSecret = async (environment: string, secretPath: string, secretKey: string) => {
const secRefPathUrl = path.join("/", ...secRefPath); const cacheKey = getCacheUniqueKey(environment, secretPath);
const uniqKey = `${secRefEnv}-${secRefPathUrl}`; const uniqKey = `${environment}-${cacheKey}`;
if (fetchCache?.[uniqKey]) { if (secretCache?.[uniqKey]) {
return fetchCache[uniqKey][secRefKey]; return secretCache[uniqKey][secretKey] || "";
} }
const folder = await folderDAL.findBySecretPath(projectId, secRefEnv, secRefPathUrl); const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
if (!folder) return ""; if (!folder) return "";
const secrets = await secretDAL.findByFolderId(folder.id); const secrets = await secretDAL.findByFolderId(folder.id);
const decryptedSec = secrets.reduce<Record<string, string>>((prev, secret) => { const decryptedSec = secrets.reduce<Record<string, string>>((prev, secret) => {
const secretKey = decryptSymmetric128BitHexKeyUTF8({ const decryptedSecretKey = decryptSymmetric128BitHexKeyUTF8({
ciphertext: secret.secretKeyCiphertext, ciphertext: secret.secretKeyCiphertext,
iv: secret.secretKeyIV, iv: secret.secretKeyIV,
tag: secret.secretKeyTag, tag: secret.secretKeyTag,
key: secretEncKey key: secretEncKey
}); });
const secretValue = decryptSymmetric128BitHexKeyUTF8({ const decryptedSecretValue = decryptSymmetric128BitHexKeyUTF8({
ciphertext: secret.secretValueCiphertext, ciphertext: secret.secretValueCiphertext,
iv: secret.secretValueIV, iv: secret.secretValueIV,
tag: secret.secretValueTag, tag: secret.secretValueTag,
key: secretEncKey key: secretEncKey
}); });
// eslint-disable-next-line // eslint-disable-next-line
prev[secretKey] = secretValue; prev[decryptedSecretKey] = decryptedSecretValue;
return prev; return prev;
}, {}); }, {});
fetchCache[uniqKey] = decryptedSec; secretCache[uniqKey] = decryptedSec;
return fetchCache[uniqKey][secRefKey]; return secretCache[uniqKey][secretKey] || "";
};
}; };
const recursivelyExpandSecret = async ( const recursivelyExpandSecret = async ({
expandedSec: Record<string, string>, value,
interpolatedSec: Record<string, string>, secretPath,
fetchCrossEnv: (env: string, secPath: string[], secKey: string) => Promise<string>, environment,
recursionChainBreaker: Record<string, boolean>, depth = 0
key: string }: {
) => { value?: string;
if (expandedSec?.[key] !== undefined) { secretPath: string;
return expandedSec[key]; environment: string;
} depth?: number;
if (recursionChainBreaker?.[key]) { }) => {
return ""; if (!value) return "";
} if (depth > MAX_SECRET_REFERENCE_DEPTH) return "";
// eslint-disable-next-line
recursionChainBreaker[key] = true;
let interpolatedValue = interpolatedSec[key]; const refs = value.match(INTERPOLATION_SYNTAX_REG);
if (!interpolatedValue) { let expandedValue = value;
// eslint-disable-next-line no-console
console.error(`Couldn't find referenced value - ${key}`);
return "";
}
const refs = interpolatedValue.match(INTERPOLATION_SYNTAX_REG);
if (refs) { if (refs) {
for (const interpolationSyntax of refs) { for (const interpolationSyntax of refs) {
const interpolationKey = interpolationSyntax.slice(2, interpolationSyntax.length - 1); const interpolationKey = interpolationSyntax.slice(2, interpolationSyntax.length - 1);
const entities = interpolationKey.trim().split("."); const entities = interpolationKey.trim().split(".");
if (entities.length === 1) { if (entities.length === 1) {
const val = await recursivelyExpandSecret( const [secretKey] = entities;
expandedSec,
interpolatedSec,
fetchCrossEnv,
recursionChainBreaker,
interpolationKey
);
if (val) {
interpolatedValue = interpolatedValue.replaceAll(interpolationSyntax, val);
}
// eslint-disable-next-line // eslint-disable-next-line
continue; let referenceValue = await fetchSecret(environment, secretPath, secretKey);
if (INTERPOLATION_SYNTAX_REG.test(referenceValue)) {
// eslint-disable-next-line
referenceValue = await recursivelyExpandSecret({
environment,
secretPath,
value: referenceValue,
depth: depth + 1
});
}
const cacheKey = getCacheUniqueKey(environment, secretPath);
secretCache[cacheKey][secretKey] = referenceValue;
expandedValue = expandedValue.replaceAll(interpolationSyntax, referenceValue);
} }
if (entities.length > 1) { if (entities.length > 1) {
const secRefEnv = entities[0]; const secretReferenceEnvironment = entities[0];
const secRefPath = entities.slice(1, entities.length - 1); const secretReferencePath = path.join("/", ...entities.slice(1, entities.length - 1));
const secRefKey = entities[entities.length - 1]; const secretReferenceKey = entities[entities.length - 1];
const val = await fetchCrossEnv(secRefEnv, secRefPath, secRefKey); // eslint-disable-next-line
if (val) { let referenceValue = await fetchSecret(secretReferenceEnvironment, secretReferencePath, secretReferenceKey);
interpolatedValue = interpolatedValue.replaceAll(interpolationSyntax, val); if (INTERPOLATION_SYNTAX_REG.test(referenceValue)) {
// eslint-disable-next-line
referenceValue = await recursivelyExpandSecret({
environment: secretReferenceEnvironment,
secretPath: secretReferencePath,
value: referenceValue,
depth: depth + 1
});
} }
const cacheKey = getCacheUniqueKey(secretReferenceEnvironment, secretReferencePath);
secretCache[cacheKey][secretReferenceKey] = referenceValue;
expandedValue = expandedValue.replaceAll(interpolationSyntax, referenceValue);
} }
} }
} }
// eslint-disable-next-line return expandedValue;
expandedSec[key] = interpolatedValue;
return interpolatedValue;
}; };
// used to convert multi line ones to quotes ones with \n const expandSecret = async (inputSecret: {
const formatMultiValueEnv = (val?: string) => { value?: string;
if (!val) return ""; skipMultilineEncoding?: boolean | null;
if (!val.match("\n")) return val; secretPath: string;
return `"${val.replace(/\n/g, "\\n")}"`; environment: string;
}) => {
if (!inputSecret.value) return inputSecret.value;
const shouldExpand = Boolean(inputSecret.value?.match(INTERPOLATION_SYNTAX_REG));
if (!shouldExpand) return inputSecret.value;
const expandedSecretValue = await recursivelyExpandSecret(inputSecret);
return inputSecret.skipMultilineEncoding ? formatMultiValueEnv(expandedSecretValue) : expandedSecretValue;
}; };
return expandSecret;
const expandSecrets = async (
secrets: Record<string, { value: string; comment?: string; skipMultilineEncoding?: boolean | null }>
) => {
const expandedSec: Record<string, string> = {};
const interpolatedSec: Record<string, string> = {};
const crossSecEnvFetch = fetchSecretsCrossEnv();
Object.keys(secrets).forEach((key) => {
if (secrets[key].value.match(INTERPOLATION_SYNTAX_REG)) {
interpolatedSec[key] = secrets[key].value;
} else {
expandedSec[key] = secrets[key].value;
}
});
for (const key of Object.keys(secrets)) {
if (expandedSec?.[key]) {
// should not do multi line encoding if user has set it to skip
// eslint-disable-next-line
secrets[key].value = secrets[key].skipMultilineEncoding
? formatMultiValueEnv(expandedSec[key])
: expandedSec[key];
// eslint-disable-next-line
continue;
}
// this is to avoid recursion loop. So the graph should be direct graph rather than cyclic
// so for any recursion building if there is an entity two times same key meaning it will be looped
const recursionChainBreaker: Record<string, boolean> = {};
const expandedVal = await recursivelyExpandSecret(
expandedSec,
interpolatedSec,
crossSecEnvFetch,
recursionChainBreaker,
key
);
// eslint-disable-next-line
secrets[key].value = secrets[key].skipMultilineEncoding ? formatMultiValueEnv(expandedVal) : expandedVal;
}
return secrets;
};
return expandSecrets;
}; };
export const decryptSecretRaw = ( export const decryptSecretRaw = (
+70 -53
View File
@@ -258,6 +258,7 @@ export const secretQueueFactory = ({
const getIntegrationSecretsV2 = async (dto: { const getIntegrationSecretsV2 = async (dto: {
projectId: string; projectId: string;
environment: string; environment: string;
secretPath: string;
folderId: string; folderId: string;
depth: number; depth: number;
decryptor: (value: Buffer | null | undefined) => string; decryptor: (value: Buffer | null | undefined) => string;
@@ -269,30 +270,36 @@ export const secretQueueFactory = ({
); );
return content; return content;
} }
// process secrets in current folder
const secrets = await secretV2BridgeDAL.findByFolderId(dto.folderId);
secrets.forEach((secret) => {
const secretKey = secret.key;
const secretValue = dto.decryptor(secret.encryptedValue);
content[secretKey] = { value: secretValue };
if (secret.encryptedComment) {
const commentValue = dto.decryptor(secret.encryptedComment);
content[secretKey].comment = commentValue;
}
content[secretKey].skipMultilineEncoding = Boolean(secret.skipMultilineEncoding);
});
const expandSecretReferences = expandSecretReferencesFactory({ const expandSecretReferences = expandSecretReferencesFactory({
decryptSecretValue: dto.decryptor, decryptSecretValue: dto.decryptor,
secretDAL: secretV2BridgeDAL, secretDAL: secretV2BridgeDAL,
folderDAL, folderDAL,
projectId: dto.projectId projectId: dto.projectId
}); });
// process secrets in current folder
const secrets = await secretV2BridgeDAL.findByFolderId(dto.folderId);
await Promise.allSettled(
secrets.map(async (secret) => {
const secretKey = secret.key;
const secretValue = dto.decryptor(secret.encryptedValue);
const expandedSecretValue = await expandSecretReferences({
environment: dto.environment,
secretPath: dto.secretPath,
skipMultilineEncoding: secret.skipMultilineEncoding,
value: secretValue
});
content[secretKey] = { value: expandedSecretValue || "" };
if (secret.encryptedComment) {
const commentValue = dto.decryptor(secret.encryptedComment);
content[secretKey].comment = commentValue;
}
content[secretKey].skipMultilineEncoding = Boolean(secret.skipMultilineEncoding);
})
);
await expandSecretReferences(content);
// check if current folder has any imports from other folders // check if current folder has any imports from other folders
const secretImports = await secretImportDAL.find({ folderId: dto.folderId, isReplication: false }); const secretImports = await secretImportDAL.find({ folderId: dto.folderId, isReplication: false });
@@ -329,6 +336,7 @@ export const secretQueueFactory = ({
const getIntegrationSecrets = async (dto: { const getIntegrationSecrets = async (dto: {
projectId: string; projectId: string;
environment: string; environment: string;
secretPath: string;
folderId: string; folderId: string;
key: string; key: string;
depth: number; depth: number;
@@ -341,46 +349,52 @@ export const secretQueueFactory = ({
return content; return content;
} }
// process secrets in current folder const expandSecretReferences = interpolateSecrets({
const secrets = await secretDAL.findByFolderId(dto.folderId);
secrets.forEach((secret) => {
const secretKey = decryptSymmetric128BitHexKeyUTF8({
ciphertext: secret.secretKeyCiphertext,
iv: secret.secretKeyIV,
tag: secret.secretKeyTag,
key: dto.key
});
const secretValue = decryptSymmetric128BitHexKeyUTF8({
ciphertext: secret.secretValueCiphertext,
iv: secret.secretValueIV,
tag: secret.secretValueTag,
key: dto.key
});
content[secretKey] = { value: secretValue };
if (secret.secretCommentCiphertext && secret.secretCommentIV && secret.secretCommentTag) {
const commentValue = decryptSymmetric128BitHexKeyUTF8({
ciphertext: secret.secretCommentCiphertext,
iv: secret.secretCommentIV,
tag: secret.secretCommentTag,
key: dto.key
});
content[secretKey].comment = commentValue;
}
content[secretKey].skipMultilineEncoding = Boolean(secret.skipMultilineEncoding);
});
const expandSecrets = interpolateSecrets({
projectId: dto.projectId, projectId: dto.projectId,
secretEncKey: dto.key, secretEncKey: dto.key,
folderDAL, folderDAL,
secretDAL secretDAL
}); });
await expandSecrets(content); // process secrets in current folder
const secrets = await secretDAL.findByFolderId(dto.folderId);
await Promise.allSettled(
secrets.map(async (secret) => {
const secretKey = decryptSymmetric128BitHexKeyUTF8({
ciphertext: secret.secretKeyCiphertext,
iv: secret.secretKeyIV,
tag: secret.secretKeyTag,
key: dto.key
});
const secretValue = decryptSymmetric128BitHexKeyUTF8({
ciphertext: secret.secretValueCiphertext,
iv: secret.secretValueIV,
tag: secret.secretValueTag,
key: dto.key
});
const expandedSecretValue = await expandSecretReferences({
environment: dto.environment,
secretPath: dto.secretPath,
skipMultilineEncoding: secret.skipMultilineEncoding,
value: secretValue
});
content[secretKey] = { value: expandedSecretValue || "" };
if (secret.secretCommentCiphertext && secret.secretCommentIV && secret.secretCommentTag) {
const commentValue = decryptSymmetric128BitHexKeyUTF8({
ciphertext: secret.secretCommentCiphertext,
iv: secret.secretCommentIV,
tag: secret.secretCommentTag,
key: dto.key
});
content[secretKey].comment = commentValue;
}
content[secretKey].skipMultilineEncoding = Boolean(secret.skipMultilineEncoding);
})
);
// check if current folder has any imports from other folders // check if current folder has any imports from other folders
const secretImport = await secretImportDAL.find({ folderId: dto.folderId, isReplication: false }); const secretImport = await secretImportDAL.find({ folderId: dto.folderId, isReplication: false });
@@ -404,7 +418,8 @@ export const secretQueueFactory = ({
projectId: dto.projectId, projectId: dto.projectId,
folderId: folder.id, folderId: folder.id,
key: dto.key, key: dto.key,
depth: dto.depth + 1 depth: dto.depth + 1,
secretPath: dto.secretPath
}); });
// add the imported secrets to the current folder secrets // add the imported secrets to the current folder secrets
@@ -686,6 +701,7 @@ export const secretQueueFactory = ({
projectId, projectId,
folderId: folder.id, folderId: folder.id,
depth: 1, depth: 1,
secretPath,
decryptor: (value) => (value ? secretManagerDecryptor({ cipherTextBlob: value }).toString() : "") decryptor: (value) => (value ? secretManagerDecryptor({ cipherTextBlob: value }).toString() : "")
}) })
: await getIntegrationSecrets({ : await getIntegrationSecrets({
@@ -693,7 +709,8 @@ export const secretQueueFactory = ({
projectId, projectId,
folderId: folder.id, folderId: folder.id,
key: botKey as string, key: botKey as string,
depth: 1 depth: 1,
secretPath
}); });
for (const integration of toBeSyncedIntegrations) { for (const integration of toBeSyncedIntegrations) {
+49 -97
View File
@@ -482,7 +482,7 @@ export const secretServiceFactory = ({
projectId, projectId,
environmentSlug: folder.environment.slug environmentSlug: folder.environment.slug
}); });
// TODO(akhilmhdh-pg): licence check, posthog service and snapshot // TODO(akhilmhdh-pg): license check, posthog service and snapshot
return { ...deletedSecret[0], _id: deletedSecret[0].id, workspace: projectId, environment, secretPath: path }; return { ...deletedSecret[0], _id: deletedSecret[0].id, workspace: projectId, environment, secretPath: path };
}; };
@@ -1047,74 +1047,47 @@ export const secretServiceFactory = ({
}; };
}); });
const expandSecret = interpolateSecrets({
folderDAL,
projectId,
secretDAL,
secretEncKey: botKey
});
if (expandSecretReferences) { if (expandSecretReferences) {
const expandSecrets = interpolateSecrets({ const secretsGroupByPath = groupBy(filteredSecrets, (i) => i.secretPath);
folderDAL, await Promise.allSettled(
projectId, Object.keys(secretsGroupByPath).map((groupedPath) =>
secretDAL, Promise.allSettled(
secretEncKey: botKey secretsGroupByPath[groupedPath].map(async (decryptedSecret, index) => {
}); const expandedSecretValue = await expandSecret({
value: decryptedSecret.secretValue,
const batchSecretsExpand = async ( secretPath: groupedPath,
secretBatch: { environment,
secretKey: string; skipMultilineEncoding: decryptedSecret.skipMultilineEncoding
secretValue: string; });
secretComment?: string; // eslint-disable-next-line no-param-reassign
secretPath: string; secretsGroupByPath[groupedPath][index].secretValue = expandedSecretValue || "";
skipMultilineEncoding: boolean | null | undefined; })
}[] )
) => { )
// Group secrets by secretPath );
const secretsByPath: Record< await Promise.allSettled(
string, processedImports.map((processedImport) =>
{ Promise.allSettled(
secretKey: string; processedImport.secrets.map(async (decryptedSecret, index) => {
secretValue: string; const expandedSecretValue = await expandSecret({
secretComment?: string; value: decryptedSecret.secretValue,
skipMultilineEncoding: boolean | null | undefined; secretPath: path,
}[] environment,
> = {}; skipMultilineEncoding: decryptedSecret.skipMultilineEncoding
});
secretBatch.forEach((secret) => { // eslint-disable-next-line no-param-reassign
if (!secretsByPath[secret.secretPath]) { processedImport.secrets[index].secretValue = expandedSecretValue || "";
secretsByPath[secret.secretPath] = []; })
} )
secretsByPath[secret.secretPath].push(secret); )
}); );
// Expand secrets for each group
for (const secPath in secretsByPath) {
if (!Object.hasOwn(secretsByPath, path)) {
// eslint-disable-next-line no-continue
continue;
}
const secretRecord: Record<
string,
{ value: string; comment?: string; skipMultilineEncoding: boolean | null | undefined }
> = {};
secretsByPath[secPath].forEach((decryptedSecret) => {
secretRecord[decryptedSecret.secretKey] = {
value: decryptedSecret.secretValue,
comment: decryptedSecret.secretComment,
skipMultilineEncoding: decryptedSecret.skipMultilineEncoding
};
});
await expandSecrets(secretRecord);
secretsByPath[secPath].forEach((decryptedSecret) => {
// eslint-disable-next-line no-param-reassign
decryptedSecret.secretValue = secretRecord[decryptedSecret.secretKey].value;
});
}
};
// expand secrets
await batchSecretsExpand(filteredSecrets);
// expand imports by batch
await Promise.all(processedImports.map((processedImport) => batchSecretsExpand(processedImport.secrets)));
} }
return { return {
@@ -1177,40 +1150,19 @@ export const secretServiceFactory = ({
const decryptedSecret = decryptSecretRaw(encryptedSecret, botKey); const decryptedSecret = decryptSecretRaw(encryptedSecret, botKey);
if (expandSecretReferences) { if (expandSecretReferences) {
const expandSecrets = interpolateSecrets({ const expandSecret = interpolateSecrets({
folderDAL, folderDAL,
projectId, projectId,
secretDAL, secretDAL,
secretEncKey: botKey secretEncKey: botKey
}); });
const expandedSecretValue = await expandSecret({
const expandSingleSecret = async (secret: { environment,
secretKey: string; secretPath: path,
secretValue: string; value: decryptedSecret.secretValue,
secretComment?: string; skipMultilineEncoding: decryptedSecret.skipMultilineEncoding
secretPath: string; });
skipMultilineEncoding: boolean | null | undefined; decryptedSecret.secretValue = expandedSecretValue || "";
}) => {
const secretRecord: Record<
string,
{ value: string; comment?: string; skipMultilineEncoding: boolean | null | undefined }
> = {
[secret.secretKey]: {
value: secret.secretValue,
comment: secret.secretComment,
skipMultilineEncoding: secret.skipMultilineEncoding
}
};
await expandSecrets(secretRecord);
// Update the secret with the expanded value
// eslint-disable-next-line no-param-reassign
secret.secretValue = secretRecord[secret.secretKey].value;
};
// Expand the secret
await expandSingleSecret(decryptedSecret);
} }
return decryptedSecret; return decryptedSecret;
+9 -2
View File
@@ -1,4 +1,4 @@
import tsconfigPaths from "vite-tsconfig-paths"; // only if you are using custom tsconfig paths import path from "path";
import { defineConfig } from "vitest/config"; import { defineConfig } from "vitest/config";
export default defineConfig({ export default defineConfig({
@@ -15,7 +15,14 @@ export default defineConfig({
useAtomics: true, useAtomics: true,
isolate: false isolate: false
} }
},
alias: {
"./license-fns": path.resolve(__dirname, "./src/ee/services/license/__mocks__/license-fns")
} }
}, },
plugins: [tsconfigPaths()] // only if you are using custom tsconfig paths, resolve: {
alias: {
"@app": path.resolve(__dirname, "./src")
}
}
}); });