mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:30:42 +00:00
fix(gateway): drop stale service env on reinstall
This commit is contained in:
@@ -413,4 +413,38 @@ describe("runDaemonInstall", () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("does not reuse stale service control env during forced reinstall", async () => {
|
||||
service.isLoaded.mockResolvedValue(true);
|
||||
service.readCommand.mockResolvedValue({
|
||||
programArguments: ["openclaw", "gateway", "run"],
|
||||
environment: {
|
||||
OPENCLAW_STATE_DIR: "/tmp/openclaw-doctor-manual",
|
||||
OPENCLAW_CONFIG_PATH: "/tmp/openclaw-doctor-manual/openclaw.json",
|
||||
OPENCLAW_GATEWAY_TOKEN: "stale-service-token",
|
||||
PATH: "/tmp/doctor-bin:/usr/bin",
|
||||
NODE_OPTIONS: "--require /tmp/evil.js",
|
||||
OPENAI_API_KEY: "service-openai-key",
|
||||
},
|
||||
} as never);
|
||||
|
||||
await runDaemonInstall({ json: true, force: true });
|
||||
|
||||
expect(buildGatewayInstallPlanMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
env: expect.objectContaining({
|
||||
OPENAI_API_KEY: "service-openai-key",
|
||||
}),
|
||||
}),
|
||||
);
|
||||
const [firstArg] =
|
||||
(buildGatewayInstallPlanMock.mock.calls.at(0) as [Record<string, unknown>] | undefined) ?? [];
|
||||
const env = firstArg?.env as Record<string, string | undefined>;
|
||||
expect(env.OPENCLAW_STATE_DIR).toBeUndefined();
|
||||
expect(env.OPENCLAW_CONFIG_PATH).toBeUndefined();
|
||||
expect(env.OPENCLAW_GATEWAY_TOKEN).toBeUndefined();
|
||||
expect(env.NODE_OPTIONS).toBeUndefined();
|
||||
expect(env.PATH).not.toContain("/tmp/doctor-bin");
|
||||
expect(installDaemonServiceAndEmitMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,6 +9,11 @@ import { readConfigFileSnapshotForWrite } from "../../config/io.js";
|
||||
import { resolveGatewayPort } from "../../config/paths.js";
|
||||
import { resolveGatewayService } from "../../daemon/service.js";
|
||||
import { isNonFatalSystemdInstallProbeError } from "../../daemon/systemd.js";
|
||||
import {
|
||||
isDangerousHostEnvOverrideVarName,
|
||||
isDangerousHostEnvVarName,
|
||||
normalizeEnvVarKey,
|
||||
} from "../../infra/host-env-security.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { formatCliCommand } from "../command-format.js";
|
||||
import { buildDaemonServiceSnapshot, installDaemonServiceAndEmit } from "./response.js";
|
||||
@@ -26,8 +31,32 @@ function mergeInstallInvocationEnv(params: {
|
||||
if (!params.existingServiceEnv || Object.keys(params.existingServiceEnv).length === 0) {
|
||||
return params.env;
|
||||
}
|
||||
const preservedServiceEnv: NodeJS.ProcessEnv = {};
|
||||
for (const [rawKey, rawValue] of Object.entries(params.existingServiceEnv)) {
|
||||
const key = normalizeEnvVarKey(rawKey, { portable: true });
|
||||
if (!key) {
|
||||
continue;
|
||||
}
|
||||
const upper = key.toUpperCase();
|
||||
if (
|
||||
upper === "HOME" ||
|
||||
upper === "PATH" ||
|
||||
upper === "TMPDIR" ||
|
||||
upper.startsWith("OPENCLAW_")
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
if (isDangerousHostEnvVarName(key) || isDangerousHostEnvOverrideVarName(key)) {
|
||||
continue;
|
||||
}
|
||||
const value = rawValue.trim();
|
||||
if (!value) {
|
||||
continue;
|
||||
}
|
||||
preservedServiceEnv[key] = value;
|
||||
}
|
||||
return {
|
||||
...params.existingServiceEnv,
|
||||
...preservedServiceEnv,
|
||||
...params.env,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user