项目文件夹

文件
2026-07-13 13:36:53 +08:00

113 行
3.8 KiB
TypeScript

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
import { join } from "path";
import { existsSync, mkdirSync, rmSync, writeFileSync } from "fs";
import { tmpdir } from "os";
/**
* getApiServerKey × secrets provider (hardening pass H2).
*
* The secrets provider's enumerable map is overlaid BENEATH the `.env` view at
* the top of getApiServerKey — process.env > .env > provider — so a
* `command`-provider user with API_SERVER_KEY in their vault stops getting 401s
* from claw3d.ts / mcp-servers.ts / index.ts, while the default env provider's
* behavior is byte-for-byte unchanged.
*
* These tests run the REAL chain (config.yaml → getSecretsProvider →
* CommandSecretsProvider → /bin/sh) against a temp HERMES_HOME, with a
* synthetic `echo` helper standing in for the vault.
*/
const TEST_DIR = join(tmpdir(), `hermes-test-secrets-key-${Date.now()}`);
const itPosix = process.platform === "win32" ? it.skip : it;
const ORIGINAL_API_SERVER_KEY = process.env.API_SERVER_KEY;
async function freshConfig(
home: string,
): Promise<typeof import("../src/main/config")> {
vi.resetModules();
process.env.HERMES_HOME = home;
return await import("../src/main/config");
}
beforeEach(() => {
mkdirSync(TEST_DIR, { recursive: true });
delete process.env.API_SERVER_KEY;
});
afterEach(() => {
delete process.env.HERMES_HOME;
if (ORIGINAL_API_SERVER_KEY === undefined) delete process.env.API_SERVER_KEY;
else process.env.API_SERVER_KEY = ORIGINAL_API_SERVER_KEY;
vi.resetModules();
rmSync(TEST_DIR, { recursive: true, force: true });
});
describe("getApiServerKey secrets-provider overlay", () => {
itPosix(
"resolves a vault-stored key via the command provider when .env is empty",
async () => {
writeFileSync(
join(TEST_DIR, "config.yaml"),
[
"secrets:",
" provider: command",
" command: echo API_SERVER_KEY=from-vault",
"",
].join("\n"),
);
// No .env file at all — previously this returned "" and consumers 401'd.
const { getApiServerKey } = await freshConfig(TEST_DIR);
expect(getApiServerKey()).toBe("from-vault");
// The vault value must NOT be migrated (written) to .env: it resolves as
// the canonical envProfile arm, and secrets never land on disk.
expect(existsSync(join(TEST_DIR, ".env"))).toBe(false);
},
);
it("env-provider no-regression: .env value wins and nothing changes", async () => {
// No secrets.* config → default env provider (its list() IS the .env map).
writeFileSync(join(TEST_DIR, "config.yaml"), "agent:\n enabled: true\n");
writeFileSync(join(TEST_DIR, ".env"), "API_SERVER_KEY=from-dotenv\n");
const { getApiServerKey } = await freshConfig(TEST_DIR);
expect(getApiServerKey()).toBe("from-dotenv");
});
itPosix(
".env wins over the command provider when both have the key",
async () => {
writeFileSync(
join(TEST_DIR, "config.yaml"),
[
"secrets:",
" provider: command",
" command: echo API_SERVER_KEY=from-vault",
"",
].join("\n"),
);
writeFileSync(join(TEST_DIR, ".env"), "API_SERVER_KEY=from-dotenv\n");
const { getApiServerKey } = await freshConfig(TEST_DIR);
expect(getApiServerKey()).toBe("from-dotenv");
},
);
itPosix("process.env wins over the command provider", async () => {
writeFileSync(
join(TEST_DIR, "config.yaml"),
[
"secrets:",
" provider: command",
" command: echo API_SERVER_KEY=from-vault",
"",
].join("\n"),
);
process.env.API_SERVER_KEY = "from-process-env";
const { getApiServerKey } = await freshConfig(TEST_DIR);
expect(getApiServerKey()).toBe("from-process-env");
});
});