fix(secrets): allow storing values shorter than 8 characters

Keep the 8-character floor only for log redaction so short MinIO keys can be saved.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-19 15:13:03 +07:00
co-authored by Cursor
parent 14f6331fce
commit 2845971670
4 changed files with 7 additions and 9 deletions
+1
View File
@@ -1,6 +1,7 @@
import { inspect } from "node:util"; import { inspect } from "node:util";
export const REDACTED = "[secret]"; export const REDACTED = "[secret]";
/** Short values are stored, but skipped in log redaction to avoid false positives. */
export const MIN_SECRET_LENGTH = 8; export const MIN_SECRET_LENGTH = 8;
/** @type {Set<string>} */ /** @type {Set<string>} */
+3 -3
View File
@@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto";
import { db } from "./db.js"; import { db } from "./db.js";
import { assertOwner } from "./fs-store.js"; import { assertOwner } from "./fs-store.js";
import { decryptSecret, encryptSecret } from "./secrets.js"; import { decryptSecret, encryptSecret } from "./secrets.js";
import { MIN_SECRET_LENGTH, registerPlaintext } from "./secret-value.js"; import { registerPlaintext } from "./secret-value.js";
const MAX_NAME_LENGTH = 128; const MAX_NAME_LENGTH = 128;
const SECRET_NAME_RE = /^[A-Za-z0-9._-]+$/; const SECRET_NAME_RE = /^[A-Za-z0-9._-]+$/;
@@ -68,8 +68,8 @@ export async function getSecretById(id) {
* @param {{ owner: string, name: string, value: string }} opts * @param {{ owner: string, name: string, value: string }} opts
*/ */
export async function upsertSecret({ owner, name, value }) { export async function upsertSecret({ owner, name, value }) {
if (typeof value !== "string" || value.length < MIN_SECRET_LENGTH) { if (typeof value !== "string" || value.length === 0) {
const err = new Error(`value must be at least ${MIN_SECRET_LENGTH} characters`); const err = new Error("value is required");
err.statusCode = 400; err.statusCode = 400;
throw err; throw err;
} }
+2 -5
View File
@@ -6,7 +6,6 @@ import {
listSecrets, listSecrets,
upsertSecret, upsertSecret,
} from "../../secrets-store.js"; } from "../../secrets-store.js";
import { MIN_SECRET_LENGTH } from "../../secret-value.js";
/** /**
* @param {import("fastify").FastifyInstance} fastify * @param {import("fastify").FastifyInstance} fastify
@@ -37,10 +36,8 @@ export default async function secretsPlugin(fastify) {
} }
const value = String(body.value ?? ""); const value = String(body.value ?? "");
if (value.length < MIN_SECRET_LENGTH) { if (value.length === 0) {
return reply return reply.code(400).send({ error: "value is required" });
.code(400)
.send({ error: `value must be at least ${MIN_SECRET_LENGTH} characters` });
} }
try { try {
+1 -1
View File
@@ -180,11 +180,11 @@ export function SecretsPage() {
value={form.value} value={form.value}
onChange={(e) => setForm({ ...form, value: e.target.value })} onChange={(e) => setForm({ ...form, value: e.target.value })}
required required
minLength={8}
autoComplete="new-password" autoComplete="new-password"
/> />
<p className="text-xs opacity-60 mt-1"> <p className="text-xs opacity-60 mt-1">
Values are encrypted at rest and never shown again after save. Values are encrypted at rest and never shown again after save.
Values shorter than 8 characters are not redacted from logs.
</p> </p>
{upsert.isError ? ( {upsert.isError ? (
<p className="text-error text-sm">{errorMessage(upsert.error)}</p> <p className="text-error text-sm">{errorMessage(upsert.error)}</p>