Skip to content

fix(path): add Windows PATH bootstrap dirs#44215

Open
aniruddhaadak80 wants to merge 2 commits intoopenclaw:mainfrom
aniruddhaadak80:fix/windows-path-bootstrap-dirs
Open

fix(path): add Windows PATH bootstrap dirs#44215
aniruddhaadak80 wants to merge 2 commits intoopenclaw:mainfrom
aniruddhaadak80:fix/windows-path-bootstrap-dirs

Conversation

@aniruddhaadak80
Copy link
Copy Markdown

Summary

  • Problem: PATH bootstrapping only seeded Unix-style directories, so minimal Windows environments could miss standard tool locations even when those directories existed.
  • Why it matters: Windows users can end up with shells that cannot find common executables like gh, node, or user-installed package manager shims.
  • What changed: added Windows-specific bootstrap candidates for pnpm, WindowsApps, roaming npm, Program Files nodejs, and SystemRoot paths, plus a focused regression test.
  • What did NOT change (scope boundary): no changes to explicit env.vars.PATH merging logic, process spawning, or non-Windows path ordering.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

User-visible / Behavior Changes

  • Windows PATH bootstrap now considers common native install locations instead of only Unix-oriented directories.

Security Impact (required)

  • New permissions/capabilities? (No)
  • Secrets/tokens handling changed? (No)
  • New/changed network calls? (No)
  • Command/tool execution surface changed? (No)
  • Data access scope changed? (No)

Repro + Verification

Environment

  • OS: Windows
  • Runtime/container: local Node/pnpm workspace
  • Model/provider: N/A
  • Integration/channel (if any): N/A
  • Relevant config (redacted): N/A

Steps

  1. Simulate a minimal Windows PATH environment.
  2. Ensure common Windows tool directories exist.
  3. Run ensureOpenClawCliOnPath.
  4. Verify those directories are prepended into PATH.

Expected

  • Windows bootstrap should include standard native tool directories when present.

Actual

  • The new regression test passes and the PATH helper now prepends those directories in the expected order.

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

  • Verified scenarios: ran pnpm exec vitest run src/infra/path-env.test.ts src/agents/bash-tools.test.ts -t "PATH|prepends common Windows tool dirs when present".
  • Additional gate: ran pnpm build:plugin-sdk:dts.
  • What I did not verify: end-to-end Windows registry PATH import behavior remains out of scope for this PR.

Compatibility / Migration

  • Backward compatible? (Yes)
  • Config/env changes? (No)
  • Migration needed? (No)

Failure Recovery (if this breaks)

  • Revert the helper + test changes.
  • Known bad symptoms reviewers should watch for: unexpected PATH ordering on Windows minimal shells.

Copilot AI review requested due to automatic review settings March 12, 2026 16:19
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 12, 2026

Greptile Summary

This PR extends the PATH bootstrap helper (candidateBinDirs) with a win32 branch that seeds common Windows tool directories — pnpm shims, WindowsApps aliases, roaming npm, Program Files nodejs (64-bit and 32-bit), System32, and SystemRoot — guarded by null-checks on the corresponding env vars and silently filtered by the existing isDirectory check. The implementation is consistent with the macOS/Linux patterns already present and is backward-compatible.

  • path-env.ts: Logic is correct and clean. Each env-var is guarded, directory existence is verified via isDirectory, and the ordering (user package manager tools → system directories) mirrors the existing Unix behavior.
  • path-env.test.ts: Two minor test hygiene gaps — the ProgramFiles(x86)/nodejs branch is not exercised, and HOMEBREW_PREFIX / HOMEBREW_BREW_FILE / XDG_BIN_HOME are not cleared before the new test (unlike the Linuxbrew test), which could make the test non-deterministic on macOS/Linux CI runners where those vars are set. Assertions use arrayContaining so failures won't surface today, but the isolation is incomplete.

Confidence Score: 4/5

  • Safe to merge; the implementation is correct and the only gaps are minor test coverage holes that don't affect production behavior.
  • The production code change is small, well-guarded, and consistent with existing patterns. The two test hygiene issues (missing ProgramFiles(x86) coverage and un-cleared HOMEBREW vars) are low risk since the assertions use arrayContaining and non-existent directories are already filtered at runtime. No behavioral change on non-Windows platforms.
  • src/infra/path-env.test.ts — minor isolation gaps in the new Windows test case
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/infra/path-env.test.ts
Line: 250-255

Comment:
**`ProgramFiles(x86)` branch is not exercised**

The test sets `ProgramFiles` and `ProgramW6432` but never sets `ProgramFiles(x86)`. As a result, the `programFilesX86` branch in `path-env.ts` (line 115–117) has zero test coverage and `programFilesX86/nodejs` is never verified in the assertion. Consider adding the env-var and including its resolved dir in the `arrayContaining` check.

```suggestion
    process.env.PATH = nodejsDir;
    process.env.LOCALAPPDATA = localAppData;
    process.env.APPDATA = appData;
    process.env.ProgramFiles = programFiles;
    process.env.ProgramW6432 = programFiles;
    process.env["ProgramFiles(x86)"] = abs("C:/Program Files (x86)");
    process.env.SystemRoot = systemRoot;
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/infra/path-env.test.ts
Line: 256

Comment:
**HOMEBREW / XDG env vars not cleared before Windows test**

Every other test that cares about PATH ordering explicitly clears `HOMEBREW_PREFIX`, `HOMEBREW_BREW_FILE`, and `XDG_BIN_HOME` (see the Linuxbrew test at lines 206–208). This test skips those deletes. On a macOS/Linux CI runner where those variables are set, `resolveBrewPathDirs` could inject additional entries into `prepend`, making the actual PATH content non-deterministic and potentially masking ordering regressions. Since the assertion uses `arrayContaining`, failures won't surface today, but the test environment is not fully isolated.

Consider adding:
```suggestion
    delete process.env.OPENCLAW_PATH_BOOTSTRAPPED;
    delete process.env.HOMEBREW_PREFIX;
    delete process.env.HOMEBREW_BREW_FILE;
    delete process.env.XDG_BIN_HOME;
```

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: 696dc61

Comment on lines +250 to +255
process.env.PATH = nodejsDir;
process.env.LOCALAPPDATA = localAppData;
process.env.APPDATA = appData;
process.env.ProgramFiles = programFiles;
process.env.ProgramW6432 = programFiles;
process.env.SystemRoot = systemRoot;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ProgramFiles(x86) branch is not exercised

The test sets ProgramFiles and ProgramW6432 but never sets ProgramFiles(x86). As a result, the programFilesX86 branch in path-env.ts (line 115–117) has zero test coverage and programFilesX86/nodejs is never verified in the assertion. Consider adding the env-var and including its resolved dir in the arrayContaining check.

Suggested change
process.env.PATH = nodejsDir;
process.env.LOCALAPPDATA = localAppData;
process.env.APPDATA = appData;
process.env.ProgramFiles = programFiles;
process.env.ProgramW6432 = programFiles;
process.env.SystemRoot = systemRoot;
process.env.PATH = nodejsDir;
process.env.LOCALAPPDATA = localAppData;
process.env.APPDATA = appData;
process.env.ProgramFiles = programFiles;
process.env.ProgramW6432 = programFiles;
process.env["ProgramFiles(x86)"] = abs("C:/Program Files (x86)");
process.env.SystemRoot = systemRoot;
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/infra/path-env.test.ts
Line: 250-255

Comment:
**`ProgramFiles(x86)` branch is not exercised**

The test sets `ProgramFiles` and `ProgramW6432` but never sets `ProgramFiles(x86)`. As a result, the `programFilesX86` branch in `path-env.ts` (line 115–117) has zero test coverage and `programFilesX86/nodejs` is never verified in the assertion. Consider adding the env-var and including its resolved dir in the `arrayContaining` check.

```suggestion
    process.env.PATH = nodejsDir;
    process.env.LOCALAPPDATA = localAppData;
    process.env.APPDATA = appData;
    process.env.ProgramFiles = programFiles;
    process.env.ProgramW6432 = programFiles;
    process.env["ProgramFiles(x86)"] = abs("C:/Program Files (x86)");
    process.env.SystemRoot = systemRoot;
```

How can I resolve this? If you propose a fix, please make it concise.

process.env.ProgramFiles = programFiles;
process.env.ProgramW6432 = programFiles;
process.env.SystemRoot = systemRoot;
delete process.env.OPENCLAW_PATH_BOOTSTRAPPED;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HOMEBREW / XDG env vars not cleared before Windows test

Every other test that cares about PATH ordering explicitly clears HOMEBREW_PREFIX, HOMEBREW_BREW_FILE, and XDG_BIN_HOME (see the Linuxbrew test at lines 206–208). This test skips those deletes. On a macOS/Linux CI runner where those variables are set, resolveBrewPathDirs could inject additional entries into prepend, making the actual PATH content non-deterministic and potentially masking ordering regressions. Since the assertion uses arrayContaining, failures won't surface today, but the test environment is not fully isolated.

Consider adding:

Suggested change
delete process.env.OPENCLAW_PATH_BOOTSTRAPPED;
delete process.env.OPENCLAW_PATH_BOOTSTRAPPED;
delete process.env.HOMEBREW_PREFIX;
delete process.env.HOMEBREW_BREW_FILE;
delete process.env.XDG_BIN_HOME;
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/infra/path-env.test.ts
Line: 256

Comment:
**HOMEBREW / XDG env vars not cleared before Windows test**

Every other test that cares about PATH ordering explicitly clears `HOMEBREW_PREFIX`, `HOMEBREW_BREW_FILE`, and `XDG_BIN_HOME` (see the Linuxbrew test at lines 206–208). This test skips those deletes. On a macOS/Linux CI runner where those variables are set, `resolveBrewPathDirs` could inject additional entries into `prepend`, making the actual PATH content non-deterministic and potentially masking ordering regressions. Since the assertion uses `arrayContaining`, failures won't surface today, but the test environment is not fully isolated.

Consider adding:
```suggestion
    delete process.env.OPENCLAW_PATH_BOOTSTRAPPED;
    delete process.env.HOMEBREW_PREFIX;
    delete process.env.HOMEBREW_BREW_FILE;
    delete process.env.XDG_BIN_HOME;
```

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 696dc61097

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/infra/path-env.ts
Comment on lines +104 to +107
prepend.push(
path.join(localAppData, "pnpm"),
path.join(localAppData, "Microsoft", "WindowsApps"),
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid prepending WindowsApps ahead of existing PATH

Adding %LOCALAPPDATA%\\Microsoft\\WindowsApps to the prepend list can change command resolution in non-minimal Windows setups, because mergePath places all prepend entries before the existing PATH. On machines where Windows App Execution Aliases are enabled (for example python.exe in WindowsApps) and a real interpreter already exists later in PATH, this change causes OpenClaw subprocesses to resolve the alias/stub instead of the intended executable, which can break tool execution unexpectedly.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends PATH bootstrapping in ensureOpenClawCliOnPath to better support Windows environments by adding common Windows tool locations to the candidate directories, and adds a Vitest case to validate the new behavior.

Changes:

  • Add Windows-specific PATH candidate directories (LOCALAPPDATA pnpm/WindowsApps, APPDATA npm, Program Files nodejs, SystemRoot/System32).
  • Extend the PATH-bootstrapping test suite to cover Windows tool directory prepending.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/infra/path-env.ts Adds Windows-specific candidate PATH entries to the bootstrap logic.
src/infra/path-env.test.ts Adds a Windows-focused test and snapshots additional Windows env vars.

expect(parts).toEqual(
expect.arrayContaining([pnpmDir, windowsAppsDir, npmDir, nodejsDir, system32Dir, systemRoot]),
);
expect(parts.indexOf(pnpmDir)).toBeLessThan(parts.indexOf(nodejsDir));
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test claims to verify that common Windows tool dirs are prepended, but it only asserts ordering for pnpmDir vs nodejsDir. Because arrayContaining ignores order, the test would still pass if some of the other Windows dirs (e.g. windowsAppsDir, npmDir, system32Dir, systemRoot) were appended after nodejsDir. Consider asserting that each of these expected directories appears before the original PATH entry (and optionally that System32 comes before SystemRoot).

Suggested change
expect(parts.indexOf(pnpmDir)).toBeLessThan(parts.indexOf(nodejsDir));
const originalPathIndex = parts.indexOf(nodejsDir);
expect(originalPathIndex).toBeGreaterThanOrEqual(0);
const pnpmIndex = parts.indexOf(pnpmDir);
const windowsAppsIndex = parts.indexOf(windowsAppsDir);
const npmIndex = parts.indexOf(npmDir);
const system32Index = parts.indexOf(system32Dir);
const systemRootIndex = parts.indexOf(systemRoot);
expect(pnpmIndex).toBeGreaterThanOrEqual(0);
expect(windowsAppsIndex).toBeGreaterThanOrEqual(0);
expect(npmIndex).toBeGreaterThanOrEqual(0);
expect(system32Index).toBeGreaterThanOrEqual(0);
expect(systemRootIndex).toBeGreaterThanOrEqual(0);
// All common tool dirs should be prepended before the original PATH entry.
expect(pnpmIndex).toBeLessThan(originalPathIndex);
expect(windowsAppsIndex).toBeLessThan(originalPathIndex);
expect(npmIndex).toBeLessThan(originalPathIndex);
expect(system32Index).toBeLessThan(originalPathIndex);
expect(systemRootIndex).toBeLessThan(originalPathIndex);
// Optionally, ensure System32 comes before SystemRoot.
expect(system32Index).toBeLessThan(systemRootIndex);

Copilot uses AI. Check for mistakes.
@katoue

This comment was marked as spam.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 09fb203251

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

platform: "win32",
});

const parts = (process.env.PATH ?? "").split(path.delimiter);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Parse simulated Windows PATH with semicolon delimiter

This test case forces platform: "win32", and ensureOpenClawCliOnPath now merges PATH entries with ";" on that platform (src/infra/path-env.ts:151), but the assertion still splits using path.delimiter. In the Ubuntu checks lane (.github/workflows/ci.yml), path.delimiter is ":", so Windows-style entries like C:/... get split at the drive colon and the expected directories cannot be found, making this regression test fail on non-Windows CI.

Useful? React with 👍 / 👎.

@openclaw-barnacle
Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle Bot added the stale Marked as stale due to inactivity label Apr 28, 2026
@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented Apr 28, 2026

Codex review: found issues before merge.

Summary
The PR adds a win32 PATH delimiter path, prepends common Windows tool directories in ensureOpenClawCliOnPath, adds a Windows regression test, and adds gateway push schema labels.

Reproducibility: yes. Source inspection shows current main still has no win32-specific bootstrap branch, and the PR test failure is source-reproducible because it splits a semicolon-joined simulated Windows PATH with the host delimiter.

Next step before merge
Maintainer security/ownership review is needed before repair because the remaining choice is Windows command-resolution precedence for user-profile shims.

Security
Needs attention: The diff broadens command-resolution precedence by prepending user-writable Windows shim directories.

Review findings

  • [P1] Append user-writable Windows shims — src/infra/path-env.ts:109-116
  • [P1] Split the Windows PATH with semicolons — src/infra/path-env.test.ts:265
  • [P2] Drop the stale gateway push label hunk — src/config/schema.labels.ts:80-84
Review details

Best possible solution:

Revise or replace the PR so Windows bootstrap follows the existing PATH hardening policy: trusted runtime/system roots may be searched ahead of PATH, user-profile package-manager shims should be appended or explicitly opted in, tests should use deterministic Windows delimiters, and unrelated schema-label changes should be dropped.

Do we have a high-confidence way to reproduce the issue?

Yes. Source inspection shows current main still has no win32-specific bootstrap branch, and the PR test failure is source-reproducible because it splits a semicolon-joined simulated Windows PATH with the host delimiter.

Is this the best way to solve the issue?

No. Windows bootstrap support is the right direction, but this diff is not the best fix because it elevates user-writable Windows shim directories ahead of trusted paths and mixes in stale unrelated labels.

Full review comments:

  • [P1] Append user-writable Windows shims — src/infra/path-env.ts:109-116
    The win32 branch adds %LOCALAPPDATA%\pnpm, WindowsApps, and %APPDATA%\npm to prepend, so OpenClaw subprocesses would resolve user-profile shims before the existing PATH and trusted system dirs. Current main deliberately appends user/package-manager dirs to avoid command hijacking, so this weakens command resolution.
    Confidence: 0.92
  • [P1] Split the Windows PATH with semicolons — src/infra/path-env.test.ts:265
    This test forces platform: "win32", and production now joins PATH entries with ;, but the assertion splits with path.delimiter. On Linux/macOS CI that is :, so C:/... entries are split at drive-letter colons and the regression test fails independent of the intended behavior.
    Confidence: 0.9
  • [P2] Drop the stale gateway push label hunk — src/config/schema.labels.ts:80-84
    These gateway.push.* labels are unrelated to PATH bootstrapping and current main already contains them under the gateway labels, so keeping this hunk creates avoidable conflicts and review noise.
    Confidence: 0.82

Overall correctness: patch is incorrect
Overall confidence: 0.9

Security concerns:

  • [medium] User-writable PATH entries gain command precedence — src/infra/path-env.ts:109
    Adding %LOCALAPPDATA%\pnpm, WindowsApps, and %APPDATA%\npm to the prepended PATH set lets user-profile shims shadow existing or system binaries for OpenClaw subprocesses, weakening the current PATH-hijack hardening model.
    Confidence: 0.9

Acceptance criteria:

  • pnpm test src/infra/path-env.test.ts
  • pnpm check:changed

What I checked:

  • current-main-windows-gap: Current candidateBinDirs has no platform === "win32" branch; Windows gets the runtime dir plus existing Unix-oriented bootstrap behavior only. (src/infra/path-env.ts:52, a90be474f441)
  • current-main-path-hardening: Current main explicitly prepends only immutable OS directories and appends user-writable/package-manager directories to prevent PATH hijacking. (src/infra/path-env.ts:95, a90be474f441)
  • pr-prepends-user-writable-windows-dirs: The PR adds %LOCALAPPDATA%\pnpm, WindowsApps, and %APPDATA%\npm to the prepend list, giving user-profile shims command precedence. (src/infra/path-env.ts:109, 09fb2032517c)
  • path-is-security-boundary: Host exec env sanitization treats PATH as a security boundary because it controls command resolution and safe-bin checks. (src/infra/host-env-security.ts:186, a90be474f441)
  • windows-trusted-dir-policy: Adjacent Windows system binary resolution includes only system-managed directories and explicitly excludes user-profile paths such as %LOCALAPPDATA%. (src/infra/resolve-system-bin.ts:49, a90be474f441)
  • test-delimiter-bug: The PR production code joins simulated win32 PATH entries with ;, but the new test still splits with host path.delimiter, which breaks on non-Windows runners with C:/... paths. (src/infra/path-env.test.ts:265, 09fb2032517c)

Likely related people:

  • Peter Steinberger: Local blame and git log -S attribute the current PATH hardening comments, Windows trusted-dir policy, and existing gateway push labels to b37fba7c07a769639a5d042dc8d193b39ffb092e. (role: current-main path/security implementation owner; confidence: medium; commits: b37fba7c07a7; files: src/infra/path-env.ts, src/infra/resolve-system-bin.ts, src/infra/host-env-security.ts)
  • BradGroux: The open Microsoft/Windows tracker in the provided context groups Windows platform PRs and issues, so this may be useful routing context even though it is not line-level code ownership. (role: adjacent Windows platform tracker maintainer; confidence: low)

Remaining risk / open question:

  • Changing Windows PATH precedence is security-sensitive because user-profile shims can shadow trusted binaries.
  • The branch is stale against current main and includes an unrelated schema-label hunk that is already present on main.

Codex review notes: model gpt-5.5, reasoning high; reviewed against a90be474f441.

@openclaw-barnacle openclaw-barnacle Bot removed the stale Marked as stale due to inactivity label Apr 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants