fix: harden secret-file readers

This commit is contained in:
Peter Steinberger
2026-03-10 23:40:10 +00:00
parent 208fb1aa35
commit 201420a7ee
26 changed files with 433 additions and 188 deletions

View File

@@ -1,3 +1,6 @@
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { describe, it, expect, beforeEach, afterEach } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
import {
@@ -97,6 +100,33 @@ describe("LINE accounts", () => {
expect(account.channelSecret).toBe("");
expect(account.tokenSource).toBe("none");
});
it.runIf(process.platform !== "win32")("rejects symlinked token and secret files", () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-line-account-"));
const tokenFile = path.join(dir, "token.txt");
const tokenLink = path.join(dir, "token-link.txt");
const secretFile = path.join(dir, "secret.txt");
const secretLink = path.join(dir, "secret-link.txt");
fs.writeFileSync(tokenFile, "file-token\n", "utf8");
fs.writeFileSync(secretFile, "file-secret\n", "utf8");
fs.symlinkSync(tokenFile, tokenLink);
fs.symlinkSync(secretFile, secretLink);
const cfg: OpenClawConfig = {
channels: {
line: {
tokenFile: tokenLink,
secretFile: secretLink,
},
},
};
const account = resolveLineAccount({ cfg });
expect(account.channelAccessToken).toBe("");
expect(account.channelSecret).toBe("");
expect(account.tokenSource).toBe("none");
fs.rmSync(dir, { recursive: true, force: true });
});
});
describe("resolveDefaultLineAccountId", () => {

View File

@@ -1,5 +1,5 @@
import fs from "node:fs";
import type { OpenClawConfig } from "../config/config.js";
import { tryReadSecretFileSync } from "../infra/secret-file.js";
import {
DEFAULT_ACCOUNT_ID,
normalizeAccountId as normalizeSharedAccountId,
@@ -16,14 +16,7 @@ import type {
export { DEFAULT_ACCOUNT_ID } from "../routing/account-id.js";
function readFileIfExists(filePath: string | undefined): string | undefined {
if (!filePath) {
return undefined;
}
try {
return fs.readFileSync(filePath, "utf-8").trim();
} catch {
return undefined;
}
return tryReadSecretFileSync(filePath, "LINE credential file", { rejectSymlink: true });
}
function resolveToken(params: {