Skip to content

Commit 330a9f9

Browse files
authored
fix(config): block workspace bundled-root dotenv overrides (#58170)
* wip(config): preserve bundled hooks root progress * test(config): cover bundled trust-root dotenv blocking
1 parent b9f8577 commit 330a9f9

3 files changed

Lines changed: 54 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ Docs: https://docs.openclaw.ai
242242
- Agents/compaction: trigger timeout recovery compaction before retrying high-context LLM timeouts so embedded runs stop repeating oversized requests. (#46417) thanks @joeykrug.
243243
- Agents/compaction: reconcile `sessions.json.compactionCount` after a late embedded auto-compaction success so persisted session counts catch up once the handler reports completion. (#45493) Thanks @jackal092927.
244244
- Agents/failover: classify Codex accountId token extraction failures as auth errors so model fallback continues to the next configured candidate. (#55206) Thanks @cosmicnet.
245+
- Hooks/plugins/skills: block workspace `.env` overrides for bundled root directories so workspace startup cannot redirect bundled trust roots away from the packaged defaults. Thanks @nexrin and @vincentkoc.
245246
- Plugins/runtime: reuse only compatible active plugin registries across tools, providers, web search, and channel bootstrap, align `/tools/invoke` plugin loading with the session workspace, and retry outbound channel recovery when the pinned channel surface changes so plugin tools and channels stop disappearing or re-registering from mismatched runtime loads. Thanks @gumadeiras.
246247
- Talk/macOS: stop direct system-voice failures from replaying system speech, use app-locale fallback for shared watchdog timing, and add regression coverage for the macOS fallback route and language-aware timeout policy. (#53511) thanks @hongsw.
247248
- Discord/gateway cleanup: keep late Carbon reconnect-exhausted errors suppressed through startup/dispose cleanup so Discord monitor shutdown no longer crashes on late gateway close events. (#55373) Thanks @Takhoffman.

src/infra/dotenv.test.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,31 @@ describe("loadDotEnv", () => {
236236
});
237237
});
238238

239+
it("blocks bundled trust-root vars from workspace .env", async () => {
240+
await withIsolatedEnvAndCwd(async () => {
241+
await withDotEnvFixture(async ({ cwdDir }) => {
242+
await writeEnvFile(
243+
path.join(cwdDir, ".env"),
244+
[
245+
"OPENCLAW_BUNDLED_HOOKS_DIR=./attacker-hooks",
246+
"OPENCLAW_BUNDLED_PLUGINS_DIR=./attacker-plugins",
247+
"OPENCLAW_BUNDLED_SKILLS_DIR=./attacker-skills",
248+
].join("\n"),
249+
);
250+
251+
delete process.env.OPENCLAW_BUNDLED_HOOKS_DIR;
252+
delete process.env.OPENCLAW_BUNDLED_PLUGINS_DIR;
253+
delete process.env.OPENCLAW_BUNDLED_SKILLS_DIR;
254+
255+
loadWorkspaceDotEnvFile(path.join(cwdDir, ".env"), { quiet: true });
256+
257+
expect(process.env.OPENCLAW_BUNDLED_HOOKS_DIR).toBeUndefined();
258+
expect(process.env.OPENCLAW_BUNDLED_PLUGINS_DIR).toBeUndefined();
259+
expect(process.env.OPENCLAW_BUNDLED_SKILLS_DIR).toBeUndefined();
260+
});
261+
});
262+
});
263+
239264
it("still allows trusted global .env to set non-workspace runtime vars", async () => {
240265
await withIsolatedEnvAndCwd(async () => {
241266
await withDotEnvFixture(async ({ cwdDir, stateDir }) => {
@@ -348,6 +373,32 @@ describe("loadCliDotEnv", () => {
348373
});
349374
});
350375

376+
it("blocks bundled trust-root vars from workspace .env during CLI startup", async () => {
377+
await withIsolatedEnvAndCwd(async () => {
378+
await withDotEnvFixture(async ({ cwdDir }) => {
379+
await writeEnvFile(
380+
path.join(cwdDir, ".env"),
381+
[
382+
"OPENCLAW_BUNDLED_HOOKS_DIR=./attacker-hooks",
383+
"OPENCLAW_BUNDLED_PLUGINS_DIR=./attacker-plugins",
384+
"OPENCLAW_BUNDLED_SKILLS_DIR=./attacker-skills",
385+
].join("\n"),
386+
);
387+
388+
delete process.env.OPENCLAW_BUNDLED_HOOKS_DIR;
389+
delete process.env.OPENCLAW_BUNDLED_PLUGINS_DIR;
390+
delete process.env.OPENCLAW_BUNDLED_SKILLS_DIR;
391+
vi.spyOn(process, "cwd").mockReturnValue(cwdDir);
392+
393+
loadCliDotEnv({ quiet: true });
394+
395+
expect(process.env.OPENCLAW_BUNDLED_HOOKS_DIR).toBeUndefined();
396+
expect(process.env.OPENCLAW_BUNDLED_PLUGINS_DIR).toBeUndefined();
397+
expect(process.env.OPENCLAW_BUNDLED_SKILLS_DIR).toBeUndefined();
398+
});
399+
});
400+
});
401+
351402
it("blocks workspace .env takeover vars before loading the global fallback", async () => {
352403
await withIsolatedEnvAndCwd(async () => {
353404
await withDotEnvFixture(async ({ base, cwdDir, stateDir }) => {

src/infra/dotenv.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ const BLOCKED_WORKSPACE_DOTENV_KEYS = new Set([
1717
"NODE_TLS_REJECT_UNAUTHORIZED",
1818
"NO_PROXY",
1919
"OPENCLAW_AGENT_DIR",
20+
"OPENCLAW_BUNDLED_HOOKS_DIR",
2021
"OPENCLAW_BUNDLED_PLUGINS_DIR",
22+
"OPENCLAW_BUNDLED_SKILLS_DIR",
2123
"OPENCLAW_CONFIG_PATH",
2224
"OPENCLAW_GATEWAY_PASSWORD",
2325
"OPENCLAW_GATEWAY_SECRET",

0 commit comments

Comments
 (0)