fix(secrets): close user definition edge cases

Reject deleted status during user-secret definition creation and remove the stale key field from the UI update contract.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-07-01 21:36:33 +00:00
1 parent 8da5a0b44a
commit 7c7f9b1767
4 files changed
+19 -5

No files matched your search

@@ -1,5 +1,6 @@
import { describe, expect, it } from "vitest";
import {
createUserSecretDefinitionSchema,
createUserSecretDeclarationSchema,
createUserSecretValueSchema,
createSecretProviderConfigSchema,
@@ -63,6 +64,16 @@ describe("secret validators", () => {
});
});
it("does not allow user secret definitions to be created as deleted", () => {
expect(() =>
createUserSecretDefinitionSchema.parse({
key: "github_api_token",
name: "GitHub API token",
status: "deleted",
}),
).toThrow();
});
it("requires secret rotation payloads to include rotation input", () => {
expect(() => rotateSecretSchema.parse({})).toThrow(/requires value, externalRef/);
expect(() => rotateUserSecretValueSchema.parse({})).toThrow(/requires value, externalRef/);
+2 -1
View File
@@ -9,6 +9,7 @@ import {
const secretKeySchema = z.string().trim().min(1).max(120).regex(/^[a-zA-Z0-9_.-]+$/);
const secretVersionSelectorSchema = z.union([z.literal("latest"), z.number().int().positive()]);
const creatableSecretStatusSchema = z.enum(["active", "disabled", "archived"]);
export const envBindingPlainSchema = z.object({
type: z.literal("plain"),
@@ -142,7 +143,7 @@ export const createUserSecretDefinitionSchema = z.object({
key: secretKeySchema,
name: z.string().trim().min(1).max(160),
description: z.string().trim().max(500).optional().nullable(),
status: z.enum(SECRET_STATUSES).optional(),
status: creatableSecretStatusSchema.optional(),
provider: z.enum(SECRET_PROVIDERS).optional(),
providerConfigId: z.string().uuid().optional().nullable(),
managedMode: z.enum(SECRET_MANAGED_MODES).optional(),
+1 -2
View File
@@ -32,7 +32,7 @@ export interface CreateUserSecretDefinitionInput {
key: string;
name: string;
description?: string | null;
status?: SecretStatus;
status?: Exclude<SecretStatus, "deleted">;
provider?: SecretProvider;
managedMode?: SecretManagedMode;
providerConfigId?: string | null;
@@ -41,7 +41,6 @@ export interface CreateUserSecretDefinitionInput {
}
export interface UpdateUserSecretDefinitionInput {
key?: string;
name?: string;
description?: string | null;
status?: SecretStatus;
@@ -107,14 +107,17 @@ export function UserSecretDefinitionsTab({ companyId }: { companyId: string }) {
name: form.name.trim(),
description: form.description.trim() || null,
usageGuidance: form.usageGuidance.trim() || null,
status: form.status,
};
if (editing) {
return secretsApi.updateUserSecretDefinition(companyId, editing.id, sharedPayload);
return secretsApi.updateUserSecretDefinition(companyId, editing.id, {
...sharedPayload,
status: form.status,
});
}
return secretsApi.createUserSecretDefinition(companyId, {
...sharedPayload,
key: form.key.trim(),
status: form.status === "deleted" ? "active" : form.status,
});
},
onSuccess: (definition) => {