mirror of https://github.com/openclaw/openclaw.git
fix(exec): block risky host env overrides (#58209)
* fix(exec): block risky host env overrides * fix(exec): block GOPRIVATE host env overrides
This commit is contained in:
parent
57c47d8c7f
commit
eb8de6715f
|
|
@ -53,6 +53,7 @@ Docs: https://docs.openclaw.ai
|
|||
- Agents/sandbox: honor `tools.sandbox.tools.alsoAllow`, let explicit sandbox re-allows remove matching built-in default-deny tools, and keep sandbox explain/error guidance aligned with the effective sandbox tool policy. (#54492) Thanks @ngutman.
|
||||
- Memory/QMD: preserve explicit `start_line` and `end_line` metadata from mcporter query results so `memory search` hits keep the real snippet offsets instead of falling back to the snippet header. (#47960) Thanks @vincentkoc.
|
||||
- LINE/ACP: add current-conversation binding and inbound binding-routing parity so `/acp spawn ... --thread here`, configured ACP bindings, and active conversation-bound ACP sessions work on LINE like the other conversation channels.
|
||||
- Host exec/env: block additional request-scoped env overrides that can redirect Docker endpoints, trust roots, compiler include paths, package resolution, or Python environment roots during approved host runs. Thanks @tdjackey and @vincentkoc.
|
||||
- LINE/markdown: preserve underscores inside Latin, Cyrillic, and CJK words when stripping markdown, while still removing standalone `_italic_` markers on the shared text-runtime path used by LINE and TTS. (#47465) Thanks @jackjin1997.
|
||||
- TTS/Microsoft: auto-switch the default Edge voice to Chinese for CJK-dominant text without overriding explicitly selected Microsoft voices. (#52355) Thanks @extrasmall0.
|
||||
- Agents/context pruning: count supplementary-plane CJK characters with the shared code-point-aware estimator so context pruning stops underestimating Japanese and Chinese text that uses Extension B ideographs. (#39985) Thanks @Edward-Qiang-2024.
|
||||
|
|
|
|||
|
|
@ -85,10 +85,36 @@ enum HostEnvSecurityPolicy {
|
|||
"PIP_INDEX_URL",
|
||||
"PIP_PYPI_URL",
|
||||
"PIP_EXTRA_INDEX_URL",
|
||||
"PIP_CONFIG_FILE",
|
||||
"PIP_FIND_LINKS",
|
||||
"PIP_TRUSTED_HOST",
|
||||
"UV_INDEX",
|
||||
"UV_INDEX_URL",
|
||||
"UV_EXTRA_INDEX_URL",
|
||||
"UV_DEFAULT_INDEX",
|
||||
"DOCKER_HOST",
|
||||
"DOCKER_TLS_VERIFY",
|
||||
"DOCKER_CERT_PATH",
|
||||
"DOCKER_CONTEXT",
|
||||
"LIBRARY_PATH",
|
||||
"CPATH",
|
||||
"C_INCLUDE_PATH",
|
||||
"CPLUS_INCLUDE_PATH",
|
||||
"OBJC_INCLUDE_PATH",
|
||||
"NODE_EXTRA_CA_CERTS",
|
||||
"SSL_CERT_FILE",
|
||||
"SSL_CERT_DIR",
|
||||
"REQUESTS_CA_BUNDLE",
|
||||
"CURL_CA_BUNDLE",
|
||||
"GOPROXY",
|
||||
"GONOSUMCHECK",
|
||||
"GONOSUMDB",
|
||||
"GONOPROXY",
|
||||
"GOPRIVATE",
|
||||
"GOENV",
|
||||
"GOPATH",
|
||||
"PYTHONUSERBASE",
|
||||
"VIRTUAL_ENV",
|
||||
"LUA_PATH",
|
||||
"LUA_CPATH",
|
||||
"GEM_HOME",
|
||||
|
|
|
|||
|
|
@ -78,10 +78,36 @@
|
|||
"PIP_INDEX_URL",
|
||||
"PIP_PYPI_URL",
|
||||
"PIP_EXTRA_INDEX_URL",
|
||||
"PIP_CONFIG_FILE",
|
||||
"PIP_FIND_LINKS",
|
||||
"PIP_TRUSTED_HOST",
|
||||
"UV_INDEX",
|
||||
"UV_INDEX_URL",
|
||||
"UV_EXTRA_INDEX_URL",
|
||||
"UV_DEFAULT_INDEX",
|
||||
"DOCKER_HOST",
|
||||
"DOCKER_TLS_VERIFY",
|
||||
"DOCKER_CERT_PATH",
|
||||
"DOCKER_CONTEXT",
|
||||
"LIBRARY_PATH",
|
||||
"CPATH",
|
||||
"C_INCLUDE_PATH",
|
||||
"CPLUS_INCLUDE_PATH",
|
||||
"OBJC_INCLUDE_PATH",
|
||||
"NODE_EXTRA_CA_CERTS",
|
||||
"SSL_CERT_FILE",
|
||||
"SSL_CERT_DIR",
|
||||
"REQUESTS_CA_BUNDLE",
|
||||
"CURL_CA_BUNDLE",
|
||||
"GOPROXY",
|
||||
"GONOSUMCHECK",
|
||||
"GONOSUMDB",
|
||||
"GONOPROXY",
|
||||
"GOPRIVATE",
|
||||
"GOENV",
|
||||
"GOPATH",
|
||||
"PYTHONUSERBASE",
|
||||
"VIRTUAL_ENV",
|
||||
"LUA_PATH",
|
||||
"LUA_CPATH",
|
||||
"GEM_HOME",
|
||||
|
|
|
|||
|
|
@ -236,10 +236,36 @@ describe("sanitizeHostExecEnv", () => {
|
|||
PIP_INDEX_URL: "https://example.invalid/simple",
|
||||
PIP_PYPI_URL: "https://example.invalid/simple",
|
||||
PIP_EXTRA_INDEX_URL: "https://example.invalid/simple",
|
||||
PIP_CONFIG_FILE: "/tmp/evil-pip.conf",
|
||||
PIP_FIND_LINKS: "https://example.invalid/wheels",
|
||||
PIP_TRUSTED_HOST: "example.invalid",
|
||||
UV_INDEX: "https://example.invalid/simple",
|
||||
UV_INDEX_URL: "https://example.invalid/simple",
|
||||
UV_DEFAULT_INDEX: "https://example.invalid/simple",
|
||||
UV_EXTRA_INDEX_URL: "https://example.invalid/simple",
|
||||
DOCKER_HOST: "tcp://example.invalid:2376",
|
||||
DOCKER_TLS_VERIFY: "1",
|
||||
DOCKER_CERT_PATH: "/tmp/evil-docker-certs",
|
||||
DOCKER_CONTEXT: "evil-remote",
|
||||
LIBRARY_PATH: "/tmp/evil-lib",
|
||||
CPATH: "/tmp/evil-headers",
|
||||
C_INCLUDE_PATH: "/tmp/evil-c-headers",
|
||||
CPLUS_INCLUDE_PATH: "/tmp/evil-cpp-headers",
|
||||
OBJC_INCLUDE_PATH: "/tmp/evil-objc-headers",
|
||||
NODE_EXTRA_CA_CERTS: "/tmp/evil-ca.pem",
|
||||
SSL_CERT_FILE: "/tmp/evil-cert.pem",
|
||||
SSL_CERT_DIR: "/tmp/evil-cert-dir",
|
||||
REQUESTS_CA_BUNDLE: "/tmp/evil-requests-ca.pem",
|
||||
CURL_CA_BUNDLE: "/tmp/evil-curl-ca.pem",
|
||||
GOPROXY: "https://example.invalid/proxy",
|
||||
GONOSUMCHECK: "example.invalid/*",
|
||||
GONOSUMDB: "example.invalid/*",
|
||||
GONOPROXY: "example.invalid/*",
|
||||
GOPRIVATE: "example.invalid/*",
|
||||
GOENV: "/tmp/evil-goenv",
|
||||
GOPATH: "/tmp/evil-go",
|
||||
PYTHONUSERBASE: "/tmp/evil-python-userbase",
|
||||
VIRTUAL_ENV: "/tmp/evil-venv",
|
||||
SHELLOPTS: "xtrace",
|
||||
PS4: "$(touch /tmp/pwned)",
|
||||
CLASSPATH: "/tmp/evil-classpath",
|
||||
|
|
@ -277,10 +303,36 @@ describe("sanitizeHostExecEnv", () => {
|
|||
expect(env.PIP_INDEX_URL).toBeUndefined();
|
||||
expect(env.PIP_PYPI_URL).toBeUndefined();
|
||||
expect(env.PIP_EXTRA_INDEX_URL).toBeUndefined();
|
||||
expect(env.PIP_CONFIG_FILE).toBeUndefined();
|
||||
expect(env.PIP_FIND_LINKS).toBeUndefined();
|
||||
expect(env.PIP_TRUSTED_HOST).toBeUndefined();
|
||||
expect(env.UV_INDEX).toBeUndefined();
|
||||
expect(env.UV_INDEX_URL).toBeUndefined();
|
||||
expect(env.UV_DEFAULT_INDEX).toBeUndefined();
|
||||
expect(env.UV_EXTRA_INDEX_URL).toBeUndefined();
|
||||
expect(env.DOCKER_HOST).toBeUndefined();
|
||||
expect(env.DOCKER_TLS_VERIFY).toBeUndefined();
|
||||
expect(env.DOCKER_CERT_PATH).toBeUndefined();
|
||||
expect(env.DOCKER_CONTEXT).toBeUndefined();
|
||||
expect(env.LIBRARY_PATH).toBeUndefined();
|
||||
expect(env.CPATH).toBeUndefined();
|
||||
expect(env.C_INCLUDE_PATH).toBeUndefined();
|
||||
expect(env.CPLUS_INCLUDE_PATH).toBeUndefined();
|
||||
expect(env.OBJC_INCLUDE_PATH).toBeUndefined();
|
||||
expect(env.NODE_EXTRA_CA_CERTS).toBeUndefined();
|
||||
expect(env.SSL_CERT_FILE).toBeUndefined();
|
||||
expect(env.SSL_CERT_DIR).toBeUndefined();
|
||||
expect(env.REQUESTS_CA_BUNDLE).toBeUndefined();
|
||||
expect(env.CURL_CA_BUNDLE).toBeUndefined();
|
||||
expect(env.GOPROXY).toBeUndefined();
|
||||
expect(env.GONOSUMCHECK).toBeUndefined();
|
||||
expect(env.GONOSUMDB).toBeUndefined();
|
||||
expect(env.GONOPROXY).toBeUndefined();
|
||||
expect(env.GOPRIVATE).toBeUndefined();
|
||||
expect(env.GOENV).toBeUndefined();
|
||||
expect(env.GOPATH).toBeUndefined();
|
||||
expect(env.PYTHONUSERBASE).toBeUndefined();
|
||||
expect(env.VIRTUAL_ENV).toBeUndefined();
|
||||
expect(env.SAFE).toBe("ok");
|
||||
expect(env.HOME).toBe("/tmp/trusted-home");
|
||||
expect(env.ZDOTDIR).toBe("/tmp/trusted-zdotdir");
|
||||
|
|
@ -369,12 +421,29 @@ describe("isDangerousHostEnvOverrideVarName", () => {
|
|||
expect(isDangerousHostEnvOverrideVarName("GRADLE_USER_HOME")).toBe(true);
|
||||
expect(isDangerousHostEnvOverrideVarName("gradle_user_home")).toBe(true);
|
||||
expect(isDangerousHostEnvOverrideVarName("PIP_INDEX_URL")).toBe(true);
|
||||
expect(isDangerousHostEnvOverrideVarName("pip_config_file")).toBe(true);
|
||||
expect(isDangerousHostEnvOverrideVarName("PIP_FIND_LINKS")).toBe(true);
|
||||
expect(isDangerousHostEnvOverrideVarName("pip_trusted_host")).toBe(true);
|
||||
expect(isDangerousHostEnvOverrideVarName("pip_pypi_url")).toBe(true);
|
||||
expect(isDangerousHostEnvOverrideVarName("PIP_EXTRA_INDEX_URL")).toBe(true);
|
||||
expect(isDangerousHostEnvOverrideVarName("UV_INDEX")).toBe(true);
|
||||
expect(isDangerousHostEnvOverrideVarName("UV_INDEX_URL")).toBe(true);
|
||||
expect(isDangerousHostEnvOverrideVarName("uv_default_index")).toBe(true);
|
||||
expect(isDangerousHostEnvOverrideVarName("UV_EXTRA_INDEX_URL")).toBe(true);
|
||||
expect(isDangerousHostEnvOverrideVarName("DOCKER_HOST")).toBe(true);
|
||||
expect(isDangerousHostEnvOverrideVarName("docker_context")).toBe(true);
|
||||
expect(isDangerousHostEnvOverrideVarName("NODE_EXTRA_CA_CERTS")).toBe(true);
|
||||
expect(isDangerousHostEnvOverrideVarName("ssl_cert_file")).toBe(true);
|
||||
expect(isDangerousHostEnvOverrideVarName("REQUESTS_CA_BUNDLE")).toBe(true);
|
||||
expect(isDangerousHostEnvOverrideVarName("curl_ca_bundle")).toBe(true);
|
||||
expect(isDangerousHostEnvOverrideVarName("LIBRARY_PATH")).toBe(true);
|
||||
expect(isDangerousHostEnvOverrideVarName("c_include_path")).toBe(true);
|
||||
expect(isDangerousHostEnvOverrideVarName("GOPROXY")).toBe(true);
|
||||
expect(isDangerousHostEnvOverrideVarName("gonosumdb")).toBe(true);
|
||||
expect(isDangerousHostEnvOverrideVarName("GOPRIVATE")).toBe(true);
|
||||
expect(isDangerousHostEnvOverrideVarName("goenv")).toBe(true);
|
||||
expect(isDangerousHostEnvOverrideVarName("PYTHONUSERBASE")).toBe(true);
|
||||
expect(isDangerousHostEnvOverrideVarName("virtual_env")).toBe(true);
|
||||
expect(isDangerousHostEnvOverrideVarName("CLASSPATH")).toBe(true);
|
||||
expect(isDangerousHostEnvOverrideVarName("classpath")).toBe(true);
|
||||
expect(isDangerousHostEnvOverrideVarName("GOFLAGS")).toBe(true);
|
||||
|
|
@ -404,27 +473,79 @@ describe("sanitizeHostExecEnvWithDiagnostics", () => {
|
|||
PIP_INDEX_URL: "https://example.invalid/simple",
|
||||
PIP_PYPI_URL: "https://example.invalid/simple",
|
||||
PIP_EXTRA_INDEX_URL: "https://example.invalid/simple",
|
||||
PIP_CONFIG_FILE: "/tmp/evil-pip.conf",
|
||||
PIP_FIND_LINKS: "https://example.invalid/wheels",
|
||||
PIP_TRUSTED_HOST: "example.invalid",
|
||||
UV_INDEX: "https://example.invalid/simple",
|
||||
UV_INDEX_URL: "https://example.invalid/simple",
|
||||
UV_DEFAULT_INDEX: "https://example.invalid/simple",
|
||||
UV_EXTRA_INDEX_URL: "https://example.invalid/simple",
|
||||
DOCKER_HOST: "tcp://example.invalid:2376",
|
||||
DOCKER_TLS_VERIFY: "1",
|
||||
DOCKER_CERT_PATH: "/tmp/evil-docker-certs",
|
||||
DOCKER_CONTEXT: "evil-remote",
|
||||
LIBRARY_PATH: "/tmp/evil-lib",
|
||||
CPATH: "/tmp/evil-headers",
|
||||
C_INCLUDE_PATH: "/tmp/evil-c-headers",
|
||||
CPLUS_INCLUDE_PATH: "/tmp/evil-cpp-headers",
|
||||
OBJC_INCLUDE_PATH: "/tmp/evil-objc-headers",
|
||||
NODE_EXTRA_CA_CERTS: "/tmp/evil-ca.pem",
|
||||
SSL_CERT_FILE: "/tmp/evil-cert.pem",
|
||||
SSL_CERT_DIR: "/tmp/evil-cert-dir",
|
||||
REQUESTS_CA_BUNDLE: "/tmp/evil-requests-ca.pem",
|
||||
CURL_CA_BUNDLE: "/tmp/evil-curl-ca.pem",
|
||||
GOPROXY: "https://example.invalid/proxy",
|
||||
GONOSUMCHECK: "example.invalid/*",
|
||||
GONOSUMDB: "example.invalid/*",
|
||||
GONOPROXY: "example.invalid/*",
|
||||
GOPRIVATE: "example.invalid/*",
|
||||
GOENV: "/tmp/evil-goenv",
|
||||
GOPATH: "/tmp/evil-go",
|
||||
PYTHONUSERBASE: "/tmp/evil-python-userbase",
|
||||
VIRTUAL_ENV: "/tmp/evil-venv",
|
||||
SAFE_KEY: "ok",
|
||||
"BAD-KEY": "bad",
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.rejectedOverrideBlockedKeys).toEqual([
|
||||
"C_INCLUDE_PATH",
|
||||
"CLASSPATH",
|
||||
"CMAKE_C_COMPILER",
|
||||
"CPATH",
|
||||
"CPLUS_INCLUDE_PATH",
|
||||
"CURL_CA_BUNDLE",
|
||||
"CXX",
|
||||
"DOCKER_CERT_PATH",
|
||||
"DOCKER_CONTEXT",
|
||||
"DOCKER_HOST",
|
||||
"DOCKER_TLS_VERIFY",
|
||||
"GOENV",
|
||||
"GONOPROXY",
|
||||
"GONOSUMCHECK",
|
||||
"GONOSUMDB",
|
||||
"GOPATH",
|
||||
"GOPRIVATE",
|
||||
"GOPROXY",
|
||||
"LIBRARY_PATH",
|
||||
"NODE_EXTRA_CA_CERTS",
|
||||
"OBJC_INCLUDE_PATH",
|
||||
"PATH",
|
||||
"PIP_CONFIG_FILE",
|
||||
"PIP_EXTRA_INDEX_URL",
|
||||
"PIP_FIND_LINKS",
|
||||
"PIP_INDEX_URL",
|
||||
"PIP_PYPI_URL",
|
||||
"PIP_TRUSTED_HOST",
|
||||
"PYTHONUSERBASE",
|
||||
"REQUESTS_CA_BUNDLE",
|
||||
"SSL_CERT_DIR",
|
||||
"SSL_CERT_FILE",
|
||||
"UV_DEFAULT_INDEX",
|
||||
"UV_EXTRA_INDEX_URL",
|
||||
"UV_INDEX",
|
||||
"UV_INDEX_URL",
|
||||
"VIRTUAL_ENV",
|
||||
]);
|
||||
expect(result.rejectedOverrideInvalidKeys).toEqual(["BAD-KEY"]);
|
||||
expect(result.env.SAFE_KEY).toBe("ok");
|
||||
|
|
@ -435,10 +556,36 @@ describe("sanitizeHostExecEnvWithDiagnostics", () => {
|
|||
expect(result.env.PIP_INDEX_URL).toBeUndefined();
|
||||
expect(result.env.PIP_PYPI_URL).toBeUndefined();
|
||||
expect(result.env.PIP_EXTRA_INDEX_URL).toBeUndefined();
|
||||
expect(result.env.PIP_CONFIG_FILE).toBeUndefined();
|
||||
expect(result.env.PIP_FIND_LINKS).toBeUndefined();
|
||||
expect(result.env.PIP_TRUSTED_HOST).toBeUndefined();
|
||||
expect(result.env.UV_INDEX).toBeUndefined();
|
||||
expect(result.env.UV_INDEX_URL).toBeUndefined();
|
||||
expect(result.env.UV_DEFAULT_INDEX).toBeUndefined();
|
||||
expect(result.env.UV_EXTRA_INDEX_URL).toBeUndefined();
|
||||
expect(result.env.DOCKER_HOST).toBeUndefined();
|
||||
expect(result.env.DOCKER_TLS_VERIFY).toBeUndefined();
|
||||
expect(result.env.DOCKER_CERT_PATH).toBeUndefined();
|
||||
expect(result.env.DOCKER_CONTEXT).toBeUndefined();
|
||||
expect(result.env.LIBRARY_PATH).toBeUndefined();
|
||||
expect(result.env.CPATH).toBeUndefined();
|
||||
expect(result.env.C_INCLUDE_PATH).toBeUndefined();
|
||||
expect(result.env.CPLUS_INCLUDE_PATH).toBeUndefined();
|
||||
expect(result.env.OBJC_INCLUDE_PATH).toBeUndefined();
|
||||
expect(result.env.NODE_EXTRA_CA_CERTS).toBeUndefined();
|
||||
expect(result.env.SSL_CERT_FILE).toBeUndefined();
|
||||
expect(result.env.SSL_CERT_DIR).toBeUndefined();
|
||||
expect(result.env.REQUESTS_CA_BUNDLE).toBeUndefined();
|
||||
expect(result.env.CURL_CA_BUNDLE).toBeUndefined();
|
||||
expect(result.env.GOPROXY).toBeUndefined();
|
||||
expect(result.env.GONOSUMCHECK).toBeUndefined();
|
||||
expect(result.env.GONOSUMDB).toBeUndefined();
|
||||
expect(result.env.GONOPROXY).toBeUndefined();
|
||||
expect(result.env.GOPRIVATE).toBeUndefined();
|
||||
expect(result.env.GOENV).toBeUndefined();
|
||||
expect(result.env.GOPATH).toBeUndefined();
|
||||
expect(result.env.PYTHONUSERBASE).toBeUndefined();
|
||||
expect(result.env.VIRTUAL_ENV).toBeUndefined();
|
||||
});
|
||||
|
||||
it("allows Windows-style override names while still rejecting invalid keys", () => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue