Config: enforce source-specific SecretRef id validation

This commit is contained in:
joshavant
2026-02-22 14:36:07 -08:00
committed by Peter Steinberger
parent c3a4251a60
commit d00ed73026
2 changed files with 83 additions and 5 deletions

View File

@@ -56,4 +56,54 @@ describe("config secret refs schema", () => {
).toBe(true);
}
});
it("rejects env refs that are not env var names", () => {
const result = validateConfigObjectRaw({
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
apiKey: { source: "env", id: "/providers/openai/apiKey" },
models: [{ id: "gpt-5", name: "gpt-5" }],
},
},
},
});
expect(result.ok).toBe(false);
if (!result.ok) {
expect(
result.issues.some(
(issue) =>
issue.path.includes("models.providers.openai.apiKey") &&
issue.message.includes("Env secret reference id"),
),
).toBe(true);
}
});
it("rejects file refs that are not absolute JSON pointers", () => {
const result = validateConfigObjectRaw({
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
apiKey: { source: "file", id: "providers/openai/apiKey" },
models: [{ id: "gpt-5", name: "gpt-5" }],
},
},
},
});
expect(result.ok).toBe(false);
if (!result.ok) {
expect(
result.issues.some(
(issue) =>
issue.path.includes("models.providers.openai.apiKey") &&
issue.message.includes("absolute JSON pointer"),
),
).toBe(true);
}
});
});

View File

@@ -3,20 +3,48 @@ import { isSafeExecutableValue } from "../infra/exec-safety.js";
import { createAllowDenyChannelRulesSchema } from "./zod-schema.allowdeny.js";
import { sensitive } from "./zod-schema.sensitive.js";
const SECRET_REF_ID_PATTERN = /^[A-Za-z0-9_./:=-](?:[A-Za-z0-9_./:=~-]{0,127})$/;
const ENV_SECRET_REF_ID_PATTERN = /^[A-Z][A-Z0-9_]{0,127}$/;
const FILE_SECRET_REF_SEGMENT_PATTERN = /^(?:[^~]|~0|~1)*$/;
export const SecretRefSchema = z
function isValidFileSecretRefId(value: string): boolean {
if (!value.startsWith("/")) {
return false;
}
return value
.slice(1)
.split("/")
.every((segment) => FILE_SECRET_REF_SEGMENT_PATTERN.test(segment));
}
const EnvSecretRefSchema = z
.object({
source: z.enum(["env", "file"]),
source: z.literal("env"),
id: z
.string()
.regex(
SECRET_REF_ID_PATTERN,
"Secret reference id must match /^[A-Za-z0-9_./:=-](?:[A-Za-z0-9_./:=~-]{0,127})$/",
ENV_SECRET_REF_ID_PATTERN,
'Env secret reference id must match /^[A-Z][A-Z0-9_]{0,127}$/ (example: "OPENAI_API_KEY").',
),
})
.strict();
const FileSecretRefSchema = z
.object({
source: z.literal("file"),
id: z
.string()
.refine(
isValidFileSecretRefId,
'File secret reference id must be an absolute JSON pointer (example: "/providers/openai/apiKey").',
),
})
.strict();
export const SecretRefSchema = z.discriminatedUnion("source", [
EnvSecretRefSchema,
FileSecretRefSchema,
]);
export const SecretInputSchema = z.union([z.string(), SecretRefSchema]);
const SecretsEnvSourceSchema = z