fix(dotenv): block connector endpoint workspace overrides (#70240)

* fix(dotenv): block connector endpoint workspace overrides

* docs(changelog): note dotenv endpoint blocklist

* fix(dotenv): block Matrix per-account scoped homeserver overrides
This commit is contained in:
Devin Robison
2026-04-22 10:58:32 -06:00
committed by GitHub
parent 8b8df813d0
commit 0623079e98
3 changed files with 70 additions and 1 deletions

View File

@@ -58,6 +58,7 @@ Docs: https://docs.openclaw.ai
- Gateway/restart: preserve one-shot continuation instructions across gateway restarts so agents can resume and reply back to the original chat after reboot. (#63406) Thanks @VACInc.
- Gateway/restart: write restart sentinel files atomically so interrupted writes cannot leave a truncated sentinel behind. (#70225) Thanks @obviyus.
- Pairing: remove stale pending requests for a device when that paired device is deleted, so an old repair approval cannot recreate the removed device from leftover state.
- Security/dotenv: block workspace `.env` overrides for Matrix, Mattermost, IRC, and Synology endpoint settings so cloned workspaces cannot redirect bundled connector traffic through local endpoint config. (#70240) Thanks @drobison00.
## 2026.4.21

View File

@@ -624,6 +624,9 @@ describe("workspace .env blocklist completeness", () => {
"OPENCLAW_ALLOW_INSECURE_PRIVATE_WS",
"OPENCLAW_BROWSER_EXECUTABLE_PATH",
"EXAMPLE_API_HOST",
"IRC_HOST",
"MATTERMOST_URL",
"MATRIX_HOMESERVER",
"MINIMAX_API_HOST",
"BROWSER_EXECUTABLE_PATH",
"PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH",
@@ -643,6 +646,8 @@ describe("workspace .env blocklist completeness", () => {
"OPENCLAW_NODE_EXEC_HOST",
"OPENCLAW_NODE_EXEC_FALLBACK",
"OPENCLAW_ALLOW_PROJECT_LOCAL_BIN",
"SYNOLOGY_CHAT_INCOMING_URL",
"SYNOLOGY_NAS_HOST",
];
await writeEnvFile(
@@ -684,6 +689,62 @@ describe("workspace .env blocklist completeness", () => {
});
});
it("blocks bundled connector endpoint vars from workspace .env", async () => {
await withIsolatedEnvAndCwd(async () => {
await withDotEnvFixture(async ({ cwdDir }) => {
await writeEnvFile(
path.join(cwdDir, ".env"),
[
"MATRIX_HOMESERVER=https://evil-matrix.example.com",
"MATTERMOST_URL=https://evil-mattermost.example.com",
"IRC_HOST=evil-irc.example.com",
"SYNOLOGY_CHAT_INCOMING_URL=https://evil-synology.example.com/incoming",
"SYNOLOGY_NAS_HOST=evil-synology.example.com",
"SAFE_PROVIDER_URL=https://allowed.example.com",
].join("\n"),
);
delete process.env.MATRIX_HOMESERVER;
delete process.env.MATTERMOST_URL;
delete process.env.IRC_HOST;
delete process.env.SYNOLOGY_CHAT_INCOMING_URL;
delete process.env.SYNOLOGY_NAS_HOST;
delete process.env.SAFE_PROVIDER_URL;
loadWorkspaceDotEnvFile(path.join(cwdDir, ".env"), { quiet: true });
expect(process.env.MATRIX_HOMESERVER).toBeUndefined();
expect(process.env.MATTERMOST_URL).toBeUndefined();
expect(process.env.IRC_HOST).toBeUndefined();
expect(process.env.SYNOLOGY_CHAT_INCOMING_URL).toBeUndefined();
expect(process.env.SYNOLOGY_NAS_HOST).toBeUndefined();
expect(process.env.SAFE_PROVIDER_URL).toBe("https://allowed.example.com");
});
});
});
it("blocks Matrix per-account scoped homeserver vars from workspace .env", async () => {
await withIsolatedEnvAndCwd(async () => {
await withDotEnvFixture(async ({ cwdDir }) => {
await writeEnvFile(
path.join(cwdDir, ".env"),
[
"MATRIX_DEFAULT_HOMESERVER=https://evil-default.example.com",
"MATRIX_OPS_HOMESERVER=https://evil-ops.example.com",
].join("\n"),
);
delete process.env.MATRIX_DEFAULT_HOMESERVER;
delete process.env.MATRIX_OPS_HOMESERVER;
loadWorkspaceDotEnvFile(path.join(cwdDir, ".env"), { quiet: true });
expect(process.env.MATRIX_DEFAULT_HOMESERVER).toBeUndefined();
expect(process.env.MATRIX_OPS_HOMESERVER).toBeUndefined();
});
});
});
it("blocks generic endpoint-routing suffixes from workspace .env", async () => {
await withIsolatedEnvAndCwd(async () => {
await withDotEnvFixture(async ({ cwdDir }) => {

View File

@@ -21,6 +21,9 @@ const BLOCKED_WORKSPACE_DOTENV_KEYS = new Set([
"CLAWHUB_URL",
"HTTP_PROXY",
"HTTPS_PROXY",
"IRC_HOST",
"MATTERMOST_URL",
"MATRIX_HOMESERVER",
"MINIMAX_API_HOST",
"NODE_TLS_REJECT_UNAUTHORIZED",
"NO_PROXY",
@@ -66,11 +69,15 @@ const BLOCKED_WORKSPACE_DOTENV_KEYS = new Set([
"OPENCLAW_TEST_TAILSCALE_BINARY",
"PI_CODING_AGENT_DIR",
"PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH",
"SYNOLOGY_CHAT_INCOMING_URL",
"SYNOLOGY_NAS_HOST",
"UV_PYTHON",
]);
// Block endpoint redirection for any service without overfitting per-provider names.
const BLOCKED_WORKSPACE_DOTENV_SUFFIXES = ["_API_HOST", "_BASE_URL"];
// `_HOMESERVER` covers Matrix's per-account scoped keys (MATRIX_<ACCOUNT>_HOMESERVER)
// in addition to the bare MATRIX_HOMESERVER listed above.
const BLOCKED_WORKSPACE_DOTENV_SUFFIXES = ["_API_HOST", "_BASE_URL", "_HOMESERVER"];
const BLOCKED_WORKSPACE_DOTENV_PREFIXES = [
"ANTHROPIC_API_KEY_",
"CLAWHUB_",