mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 09:50:42 +00:00
fix: block workspace CLOUDSDK_PYTHON override and always set trusted interpreter for gcloud (#74492)
* fix: address issue * docs: add changelog entry for PR merge
This commit is contained in:
committed by
GitHub
parent
cba0a348dc
commit
86251f4391
@@ -22,6 +22,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- fix: block workspace CLOUDSDK_PYTHON override and always set trusted interpreter for gcloud. (#74492) Thanks @pgondhi987.
|
||||
- fix(infra): block ambient Homebrew env vars from brew resolution. (#74463) Thanks @pgondhi987.
|
||||
- Thinking/providers: resolve bundled provider thinking profiles through lightweight provider policy artifacts when startup-lazy providers are not active, so OpenAI Codex GPT-5.x keeps xhigh available in Gateway session validation. Fixes #74796. Thanks @maxschachere.
|
||||
- Security/Windows: ignore workspace `.env` system-path variables and resolve stale-process `taskkill.exe` from the validated Windows install root, preventing repository-local env files from redirecting cleanup helpers. Thanks @pgondhi987.
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
ensureTailscaleEndpoint,
|
||||
resetGmailSetupUtilsCachesForTest,
|
||||
resolvePythonExecutablePath,
|
||||
runGcloud,
|
||||
} from "./gmail-setup-utils.js";
|
||||
|
||||
const itUnix = process.platform === "win32" ? it.skip : it;
|
||||
@@ -63,6 +64,89 @@ describe("resolvePythonExecutablePath", () => {
|
||||
);
|
||||
});
|
||||
|
||||
describe("runGcloud", () => {
|
||||
itUnix(
|
||||
"overrides an inherited CLOUDSDK_PYTHON value with a resolved interpreter",
|
||||
async () => {
|
||||
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-gcloud-python-"));
|
||||
try {
|
||||
const realPython = path.join(tmp, "python-real");
|
||||
await fs.writeFile(realPython, "#!/bin/sh\nexit 0\n", "utf-8");
|
||||
await fs.chmod(realPython, 0o755);
|
||||
|
||||
const shimDir = path.join(tmp, "shims");
|
||||
await fs.mkdir(shimDir, { recursive: true });
|
||||
const shim = path.join(shimDir, "python3");
|
||||
await fs.writeFile(shim, "#!/bin/sh\nexit 0\n", "utf-8");
|
||||
await fs.chmod(shim, 0o755);
|
||||
|
||||
await withEnvAsync(
|
||||
{
|
||||
CLOUDSDK_PYTHON: path.join(tmp, "evil", "python"),
|
||||
PATH: `${shimDir}${path.delimiter}/usr/bin`,
|
||||
},
|
||||
async () => {
|
||||
runCommandWithTimeoutMock
|
||||
.mockResolvedValueOnce({
|
||||
stdout: `${realPython}\n`,
|
||||
stderr: "",
|
||||
code: 0,
|
||||
signal: null,
|
||||
killed: false,
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
stdout: "",
|
||||
stderr: "",
|
||||
code: 0,
|
||||
signal: null,
|
||||
killed: false,
|
||||
});
|
||||
|
||||
await runGcloud(["config", "list"]);
|
||||
|
||||
expect(runCommandWithTimeoutMock).toHaveBeenLastCalledWith(
|
||||
["gcloud", "config", "list"],
|
||||
{
|
||||
timeoutMs: 120_000,
|
||||
env: { CLOUDSDK_PYTHON: realPython },
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
} finally {
|
||||
await fs.rm(tmp, { recursive: true, force: true });
|
||||
}
|
||||
},
|
||||
60_000,
|
||||
);
|
||||
|
||||
itUnix("unsets inherited CLOUDSDK_PYTHON when no trusted interpreter is found", async () => {
|
||||
await withEnvAsync(
|
||||
{
|
||||
CLOUDSDK_PYTHON: "/tmp/attacker-python",
|
||||
PATH: "",
|
||||
},
|
||||
async () => {
|
||||
runCommandWithTimeoutMock.mockResolvedValueOnce({
|
||||
stdout: "",
|
||||
stderr: "",
|
||||
code: 0,
|
||||
signal: null,
|
||||
killed: false,
|
||||
});
|
||||
|
||||
await runGcloud(["config", "list"]);
|
||||
|
||||
expect(runCommandWithTimeoutMock).toHaveBeenCalledTimes(1);
|
||||
expect(runCommandWithTimeoutMock).toHaveBeenCalledWith(["gcloud", "config", "list"], {
|
||||
timeoutMs: 120_000,
|
||||
env: { CLOUDSDK_PYTHON: undefined },
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("ensureTailscaleEndpoint", () => {
|
||||
it("includes stdout and exit code when tailscale serve fails", async () => {
|
||||
runCommandWithTimeoutMock
|
||||
|
||||
@@ -145,14 +145,10 @@ export async function resolvePythonExecutablePath(): Promise<string | undefined>
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async function gcloudEnv(): Promise<NodeJS.ProcessEnv | undefined> {
|
||||
if (process.env.CLOUDSDK_PYTHON) {
|
||||
return undefined;
|
||||
}
|
||||
async function gcloudEnv(): Promise<NodeJS.ProcessEnv> {
|
||||
const pythonPath = await resolvePythonExecutablePath();
|
||||
if (!pythonPath) {
|
||||
return undefined;
|
||||
}
|
||||
// Always override inherited CLOUDSDK_PYTHON so gcloud cannot select a
|
||||
// workspace-controlled interpreter.
|
||||
return { CLOUDSDK_PYTHON: pythonPath };
|
||||
}
|
||||
|
||||
|
||||
@@ -209,6 +209,7 @@ describe("loadDotEnv", () => {
|
||||
"OPENCLAW_STATE_DIR=./evil-state",
|
||||
"OPENCLAW_CONFIG_PATH=./evil-config.json",
|
||||
"ANTHROPIC_BASE_URL=https://evil.example.com/v1",
|
||||
"CLOUDSDK_PYTHON=./attacker-python",
|
||||
"EXAMPLE_API_HOST=https://evil-api.example.com",
|
||||
"MINIMAX_API_HOST=https://evil.example.com",
|
||||
"HTTP_PROXY=http://evil-proxy:8080",
|
||||
@@ -225,6 +226,7 @@ describe("loadDotEnv", () => {
|
||||
delete process.env.NODE_OPTIONS;
|
||||
delete process.env.OPENCLAW_CONFIG_PATH;
|
||||
delete process.env.ANTHROPIC_BASE_URL;
|
||||
delete process.env.CLOUDSDK_PYTHON;
|
||||
delete process.env.EXAMPLE_API_HOST;
|
||||
delete process.env.MINIMAX_API_HOST;
|
||||
delete process.env.HTTP_PROXY;
|
||||
@@ -241,6 +243,7 @@ describe("loadDotEnv", () => {
|
||||
expect(process.env.OPENCLAW_STATE_DIR).toBe(stateDir);
|
||||
expect(process.env.OPENCLAW_CONFIG_PATH).toBeUndefined();
|
||||
expect(process.env.ANTHROPIC_BASE_URL).toBeUndefined();
|
||||
expect(process.env.CLOUDSDK_PYTHON).toBeUndefined();
|
||||
expect(process.env.EXAMPLE_API_HOST).toBeUndefined();
|
||||
expect(process.env.MINIMAX_API_HOST).toBeUndefined();
|
||||
expect(process.env.HTTP_PROXY).toBeUndefined();
|
||||
|
||||
@@ -22,6 +22,7 @@ const BLOCKED_WORKSPACE_DOTENV_KEYS = new Set([
|
||||
"CLAWHUB_CONFIG_PATH",
|
||||
"CLAWHUB_TOKEN",
|
||||
"CLAWHUB_URL",
|
||||
"CLOUDSDK_PYTHON",
|
||||
"HTTP_PROXY",
|
||||
"HTTPS_PROXY",
|
||||
"HOMEBREW_BREW_FILE",
|
||||
|
||||
Reference in New Issue
Block a user