From 208c5578e32cd3bd8eb55200087ee86ba2988d6b Mon Sep 17 00:00:00 2001 From: Dotta Date: Sat, 9 May 2026 16:44:59 -0500 Subject: [PATCH] Reject updates to deleted secrets --- server/src/__tests__/secrets-service.test.ts | 23 ++++++++++++++++++++ server/src/routes/secrets.ts | 4 ++++ server/src/services/secrets.ts | 1 + 3 files changed, 28 insertions(+) diff --git a/server/src/__tests__/secrets-service.test.ts b/server/src/__tests__/secrets-service.test.ts index a67764babe7..af7ac8a7f0c 100644 --- a/server/src/__tests__/secrets-service.test.ts +++ b/server/src/__tests__/secrets-service.test.ts @@ -401,6 +401,29 @@ describeEmbeddedPostgres("secretService", () => { ).rejects.toThrow(/not found/i); }); + it("rejects updates to already soft-deleted external reference secrets", async () => { + const companyId = await seedCompany(); + const svc = secretService(db); + const awsVault = await svc.createProviderConfig(companyId, { + provider: "aws_secrets_manager", + displayName: "AWS production", + config: { region: "us-east-1", namespace: "prod-use1" }, + }); + const deleted = await svc.create(companyId, { + name: "Deleted patch target", + key: "deleted-patch-target", + provider: "aws_secrets_manager", + providerConfigId: awsVault.id, + managedMode: "external_reference", + externalRef: "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/deleted-patch-target", + }); + await svc.update(deleted.id, { status: "deleted" }); + + await expect(svc.update(deleted.id, { status: "active" })).rejects.toThrow( + /not found/i, + ); + }); + it("allows re-importing a remote secret after the prior external reference is soft-deleted", async () => { const companyId = await seedCompany(); const svc = secretService(db); diff --git a/server/src/routes/secrets.ts b/server/src/routes/secrets.ts index cba2fe09d7b..be9d503f3de 100644 --- a/server/src/routes/secrets.ts +++ b/server/src/routes/secrets.ts @@ -386,6 +386,10 @@ export function secretRoutes(db: Db) { return; } assertCompanyAccess(req, existing.companyId); + if (existing.status === "deleted") { + res.status(404).json({ error: "Secret not found" }); + return; + } const updated = await svc.update(id, { name: req.body.name, diff --git a/server/src/services/secrets.ts b/server/src/services/secrets.ts index e47226d5de1..63e4698e728 100644 --- a/server/src/services/secrets.ts +++ b/server/src/services/secrets.ts @@ -1685,6 +1685,7 @@ export function secretService(db: Db) { ) => { const secret = await getById(secretId); if (!secret) throw notFound("Secret not found"); + if (secret.status === "deleted") throw notFound("Secret not found"); if (patch.name && patch.name !== secret.name) { const duplicate = await getByName(secret.companyId, patch.name);