fix(tailscale): gate test binary override (#58468)

* fix(tailscale): gate test binary override

* fix(changelog): note tailscale override hardening

* fix(changelog): drop tailscale note from pr

* chore: add changelog for tailscale test binary gating

---------

Co-authored-by: Devin Robison <drobison@nvidia.com>
This commit is contained in:
Agustin Rivera
2026-04-02 11:39:10 -07:00
committed by GitHub
parent f32a5b30db
commit d631326c5e
5 changed files with 50 additions and 2 deletions

View File

@@ -236,6 +236,23 @@ describe("loadDotEnv", () => {
});
});
it("blocks OPENCLAW_TEST_TAILSCALE_BINARY from workspace .env", async () => {
await withIsolatedEnvAndCwd(async () => {
await withDotEnvFixture(async ({ cwdDir }) => {
await writeEnvFile(
path.join(cwdDir, ".env"),
"OPENCLAW_TEST_TAILSCALE_BINARY=/tmp/attacker-tailscale\n",
);
delete process.env.OPENCLAW_TEST_TAILSCALE_BINARY;
loadWorkspaceDotEnvFile(path.join(cwdDir, ".env"), { quiet: true });
expect(process.env.OPENCLAW_TEST_TAILSCALE_BINARY).toBeUndefined();
});
});
});
it("blocks pinned helper interpreter vars from workspace .env", async () => {
await withIsolatedEnvAndCwd(async () => {
await withDotEnvFixture(async ({ cwdDir }) => {

View File

@@ -34,6 +34,7 @@ const BLOCKED_WORKSPACE_DOTENV_KEYS = new Set([
"OPENCLAW_PINNED_WRITE_PYTHON",
"OPENCLAW_PROFILE",
"OPENCLAW_STATE_DIR",
"OPENCLAW_TEST_TAILSCALE_BINARY",
"OPENAI_API_KEY",
"OPENAI_API_KEYS",
"PI_CODING_AGENT_DIR",

View File

@@ -6,6 +6,7 @@ const {
ensureGoInstalled,
ensureTailscaledInstalled,
getTailnetHostname,
getTestTailscaleBinaryOverride,
enableTailscaleServe,
disableTailscaleServe,
ensureFunnel,
@@ -33,8 +34,9 @@ describe("tailscale helpers", () => {
let envSnapshot: ReturnType<typeof captureEnv>;
beforeEach(() => {
envSnapshot = captureEnv(["OPENCLAW_TEST_TAILSCALE_BINARY"]);
envSnapshot = captureEnv(["OPENCLAW_TEST_TAILSCALE_BINARY", "NODE_ENV", "VITEST"]);
process.env.OPENCLAW_TEST_TAILSCALE_BINARY = "tailscale";
process.env.VITEST ??= "true";
});
afterEach(() => {
@@ -69,6 +71,22 @@ describe("tailscale helpers", () => {
expect(host).toBe("noisy.tailnet.ts.net");
});
it("allows the test binary override in explicit test environments", () => {
process.env.OPENCLAW_TEST_TAILSCALE_BINARY = "/tmp/test-tailscale";
process.env.NODE_ENV = "test";
delete process.env.VITEST;
expect(getTestTailscaleBinaryOverride()).toBe("/tmp/test-tailscale");
});
it("ignores the test binary override outside test environments", () => {
process.env.OPENCLAW_TEST_TAILSCALE_BINARY = "/tmp/attacker-tailscale";
process.env.NODE_ENV = "production";
delete process.env.VITEST;
expect(getTestTailscaleBinaryOverride()).toBeNull();
});
it.each([
{
name: "ensureGoInstalled installs when missing and user agrees",

View File

@@ -149,8 +149,19 @@ export async function getTailnetHostname(exec: typeof runExec = runExec, detecte
*/
let cachedTailscaleBinary: string | null = null;
export function getTestTailscaleBinaryOverride(env: NodeJS.ProcessEnv = process.env): string | null {
const forcedBinary = env.OPENCLAW_TEST_TAILSCALE_BINARY?.trim();
if (!forcedBinary) {
return null;
}
if (env.VITEST || env.NODE_ENV === "test") {
return forcedBinary;
}
return null;
}
export async function getTailscaleBinary(): Promise<string> {
const forcedBinary = process.env.OPENCLAW_TEST_TAILSCALE_BINARY?.trim();
const forcedBinary = getTestTailscaleBinaryOverride();
if (forcedBinary) {
cachedTailscaleBinary = forcedBinary;
return forcedBinary;