Files
openclaw/src/acp/secret-file.ts
Vincent Koc 4062aa5e5d Gateway: add safer password-file input for gateway run (#39067)
* CLI: add gateway password-file option

* Docs: document safer gateway password input

* Update src/cli/gateway-cli/run.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Tests: clean up gateway password temp dirs

* CLI: restore gateway password warning flow

* Security: harden secret file reads

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-07 18:20:17 -08:00

44 lines
1.2 KiB
TypeScript

import fs from "node:fs";
import { resolveUserPath } from "../utils.js";
export const MAX_SECRET_FILE_BYTES = 16 * 1024;
export function readSecretFromFile(filePath: string, label: string): string {
const resolvedPath = resolveUserPath(filePath.trim());
if (!resolvedPath) {
throw new Error(`${label} file path is empty.`);
}
let stat: fs.Stats;
try {
stat = fs.lstatSync(resolvedPath);
} catch (err) {
throw new Error(`Failed to inspect ${label} file at ${resolvedPath}: ${String(err)}`, {
cause: err,
});
}
if (stat.isSymbolicLink()) {
throw new Error(`${label} file at ${resolvedPath} must not be a symlink.`);
}
if (!stat.isFile()) {
throw new Error(`${label} file at ${resolvedPath} must be a regular file.`);
}
if (stat.size > MAX_SECRET_FILE_BYTES) {
throw new Error(`${label} file at ${resolvedPath} exceeds ${MAX_SECRET_FILE_BYTES} bytes.`);
}
let raw = "";
try {
raw = fs.readFileSync(resolvedPath, "utf8");
} catch (err) {
throw new Error(`Failed to read ${label} file at ${resolvedPath}: ${String(err)}`, {
cause: err,
});
}
const secret = raw.trim();
if (!secret) {
throw new Error(`${label} file at ${resolvedPath} is empty.`);
}
return secret;
}