import { lstatSync, realpathSync } from "node:fs"; import { dirname, isAbsolute, relative, resolve, sep } from "node:path"; export interface SecretInputPlan { command: "tea"; args: string[]; stdinFile?: string; } // Plan only: never reads secret bytes, spawns tea, or provisions a secret. // This is an inspectable contract, not upload authorization or a race-free open. // An operator must recheck metadata and approve the actual upload separately. export function planSecretInput(login: string, file: string, mode: "file" | "stdin", repositoryRoot: string): SecretInputPlan { try { if (!/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(login) || !isAbsolute(file) || (mode !== "file" && mode !== "stdin")) throw new Error(); const canonical = realpathSync(file); const local = relative(realpathSync(repositoryRoot), canonical); if (canonical !== resolve(file) || !(local === ".." || local.startsWith(`..${sep}`) || isAbsolute(local))) throw new Error(); const metadata = lstatSync(file); const directory = lstatSync(dirname(file)); const owner = process.getuid?.(); if (owner === undefined || !metadata.isFile() || metadata.isSymbolicLink() || metadata.size === 0 || metadata.uid !== owner || (metadata.mode & 0o777) !== 0o600 || !directory.isDirectory() || directory.uid !== owner || (directory.mode & 0o777) !== 0o700) throw new Error(); } catch { throw new Error("Invalid protected secret input; no provisioning was performed."); } return { command: "tea", args: ["actions", "secrets", "create", "--login", login, "--repo", "dmg/purpur-sentinel", ...(mode === "file" ? ["--file", file] : ["--stdin"]), "RELEASE_TOKEN"], ...(mode === "stdin" ? { stdinFile: file } : {}), }; }