fix(secrets): skip deleted optional user secrets

Let optional user-secret bindings resolve to null when their definition has been deleted, matching the pre-dispatch gate's optional-binding behavior.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-07-01 21:24:38 +00:00
1 parent b04069d14d
commit 8da5a0b44a
2 files changed
+24 -2

No files matched your search

+17 -1
View File
@@ -535,7 +535,23 @@ describeEmbeddedPostgres("secretService", () => {
});
await db
.update(userSecretDefinitions)
.set({ status: "active" })
.set({ status: "deleted", deletedAt: new Date() })
.where(eq(userSecretDefinitions.id, definition.id));
await expect(
svc.resolveEnvBindings(companyId, optionalEnv, {
consumerType: "agent",
consumerId: "agent-optional",
actorType: "agent",
actorId: "agent-optional",
responsibleUserId: "user-2",
}),
).resolves.toMatchObject({
env: {},
manifest: [],
});
await db
.update(userSecretDefinitions)
.set({ status: "active", deletedAt: null })
.where(eq(userSecretDefinitions.id, definition.id));
const resolved = await svc.resolveEnvBindings(companyId, env, {
+7 -1
View File
@@ -2252,8 +2252,14 @@ export function secretService(db: Db) {
context?: SecretConsumerContext,
): Promise<RuntimeSecretResolution | null> => {
const responsibleUserId = input.responsibleUserId ?? context?.responsibleUserId ?? null;
const definition = await resolveUserSecretDefinition(companyId, input);
const optionalBinding = input.allowMissingOverride || input.required === false;
let definition: typeof userSecretDefinitions.$inferSelect;
try {
definition = await resolveUserSecretDefinition(companyId, input);
} catch (error) {
if (optionalBinding && error instanceof HttpError && error.status === 404) return null;
throw error;
}
if (definition.status !== "active") {
if (optionalBinding) return null;
throw unprocessable("User secret definition is not active");