Visitar URL original
End Protected-Root and Profile Matches at a Path Component on Windows by ptr727 · Pull Request #4105 · ptr727/ProjectTemplate · GitHub
Skip to content

End Protected-Root and Profile Matches at a Path Component on Windows - #4105

Merged
ptr727 merged 4 commits into
developfrom
feature/protected-root-boundary
Nov 12, 2026
Merged

ptr727 merged 4 commits into
developfrom
feature/protected-root-boundary

Conversation

@ptr727

@ptr727 ptr727 commented Nov 12, 2026

Copy link
Copy Markdown
Owner

Summary

Answers a "Previously missed" finding in Copilot's review of promotion PR #4096, plus the one same-class sibling a local pass found in that file.

  • Test-ProtectedPath in host-setup/windows/install-agents.ps1 matched any directory whose text starts with a protected root. So a user-writable sibling such as C:\Program FilesPortable\npm was read as under Program Files, and its npm upgrade was refused. A match now needs the root itself, or the root plus a separator.
  • Hide-Home had the same unbounded match. A sibling profile such as the same user name with a suffix printed as %USERPROFILE%2\.... It now shortens only the profile or a path inside it, as the Linux hide_home already does.

Copilot's second finding in that round was declined with evidence on #4096: check actual Windows writability with an ACL. A non-writable prefix outside the protected roots makes npm install -g fail loudly, the tool is added to Failed, and the run exits 1.

Verification

  • New lifted-function tests cover the protected root, a child, and a sibling for Test-ProtectedPath, and an inside path and a sibling profile for Hide-Home. Each fails on the old prefix match.
  • tests/test_install_agents.py (66) passes. PSScriptAnalyzer and the prose gate are clean.
  • A sweep of both install-agents scripts found no other prefix comparison without a component boundary.
  • Two recorded local passes ran, and the second found nothing open.

🤖 Generated with Claude Code

ptr727 and others added 2 commits November 12, 2026 10:16
Test-ProtectedPath matched any directory starting with a protected root's text, so a
user-writable sibling such as C:\Program FilesPortable\npm read as under Program Files and its
npm upgrade was refused. A match now needs the root itself or the root plus a separator.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hide-Home shortened any path starting with the profile's text, so a sibling profile such as
the same name with a suffix printed as %USERPROFILE%2. It now shortens only the profile itself
or a path inside it, as the Linux hide_home already does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ptr727 ptr727 added the comments Permits the comment lines the pull request adds or edits, which the prose gate otherwise refuses label Nov 12, 2026
Copilot AI lite review requested due to automatic review settings November 12, 2026 18:20
@ptr727 ptr727 added the comments Permits the comment lines the pull request adds or edits, which the prose gate otherwise refuses label Nov 12, 2026
@ptr727

ptr727 commented Nov 12, 2026

Copy link
Copy Markdown
Owner Author

A recorded local strict-review pass covers head 2fbc99e72247aee853f56fc3d4dd5e43926eebf8, the content this pull request carries at that commit against develop, and it recorded 0 findings.

@coderabbitai

coderabbitai Bot commented Nov 12, 2026 •

Copy link
Copy Markdown

Explain Change →

Caution

Review failed

The pull request is closed.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

Copilot AI left a comment

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.

🔵 Needs a closer look

Approval is deferred because the Windows-specific path and ACL behavior is not validated by the available POSIX-only PowerShell tests, requiring native Windows execution to confirm separator, case, and root-boundary handling.

No open issues 🎉

What changed in this PR

This pull request makes Windows protected-root detection and home-profile masking stop at path-component boundaries, preventing similarly prefixed sibling paths from being treated as matches.

Changes:

  • Bounds Test-ProtectedPath matches to protected roots and their descendants.
  • Bounds Hide-Home masking to the profile directory and its descendants.
  • Adds focused PowerShell helper tests for root, child, sibling, and profile paths.
File Description
tests/​test_install_agents.py Adds PowerShell helper coverage for protected-root and profile paths, including matching siblings and descendants.
host-setup/​windows/​install-agents.ps1 Restricts protected-root detection and home-profile masking to complete path components.

Review effort: Lite

@codecov

codecov Bot commented Nov 12, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 76.58%. Comparing base (d0cbd27) to head (ea07297).
⚠️ Report is 1 commits behind head on develop.

Additional details and impacted files
@@           Coverage Diff            @@
##           develop    #4105   +/-   ##
========================================
  Coverage    76.58%   76.58%           
========================================
  Files           40       40           
  Lines        14136    14136           
========================================
  Hits         10826    10826           
  Misses        3310     3310           
Flag Coverage Δ
python-3.13 76.58% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

TestInstallAgentsWindows skips on Windows, since its stub tools are bash scripts and a hosted
runner is elevated. So the Test-ProtectedPath, Hide-Home, and Resolve-FinalPath tests never ran
on real Windows paths. They lift pure functions and need neither, so they move to a class that
runs wherever pwsh exists, the Windows CI runner included.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ptr727

ptr727 commented Nov 12, 2026

Copy link
Copy Markdown
Owner Author

A recorded local strict-review pass covers head 4c400bb2aaaf286ef7c234ad1fd93af257586204, the content this pull request carries at that commit against develop, and it recorded 0 findings.

On the Windows runner the link test resolved into the user-writable directory, and the
protected rule read it correctly, but the resolved path is spelled differently from the
temp path Python made. So comparing the whole string failed. The test now checks that the
target ends in the directory the link points at.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ptr727

ptr727 commented Nov 12, 2026

Copy link
Copy Markdown
Owner Author

A recorded local strict-review pass covers head ea0729736be81da8237efee568df048d3b49f554, the content this pull request carries at that commit against develop, and it recorded 0 findings.

@ptr727

ptr727 commented Nov 12, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@ptr727

ptr727 commented Nov 12, 2026

Copy link
Copy Markdown
Owner Author

/review

@coderabbitai

coderabbitai Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Action not completed

Pull request is closed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@ptr727
ptr727 merged commit ea87999 into develop Nov 12, 2026
13 checks passed
@ptr727
ptr727 deleted the feature/protected-root-boundary branch November 12, 2026 19:40
ptr727 added a commit that referenced this pull request Nov 12, 2026
…Step (#4111)

## Summary

Answers CodeRabbit's review of promotion PR #4096 on the
`install-agents` scripts. The guiding rule is unchanged: run only a
command that upgrades exactly the tool's own package.

- **bun's directories.** `BUN_INSTALL` and `BUN_INSTALL_GLOBAL_DIR` are
honored on both platforms through one helper each: `bun_global_dir` on
Linux, `Get-BunDirectory` on Windows. A moved bun carries no `.bun`
segment, so it is recognized by these directories. On Windows that
includes the bin directory, where bun's copied shim sits.
`bunfig.toml`'s `install.globalDir` is deliberately not read.
- **mise (Linux).** mise's own listing is asked before any path shape,
so a tool under a relocated `MISE_DATA_DIR` reads as mise.
- **mise shims (Linux).** A command whose real path is the `mise`
executable is resolved through `mise which` before it is classified. So
a Homebrew-installed mise no longer leads to `brew upgrade mise`. A
failed lookup is reported, and nothing runs.
- **herdr post step.** One step, `herdr_after_upgrade` /
`Invoke-HerdrAfterUpgrade`, reinstalls herdr's integrations and gives
the restart guidance. It now runs after both the self-updater and a
successful manager upgrade.
- **Scoop roots (Windows).** With `SCOOP` or `SCOOP_GLOBAL` unset,
`Get-ScoopRoot` reads `root_path` and `global_path` from scoop's own
`config.json`, then falls back to the defaults.
- **Test.** The unwritable-npm test skips when run as root.

Two other CodeRabbit findings on #4096 were answered there. The
protected-root boundary was already fixed by #4105. Windows bun
shim-target ownership was declined with evidence.

## Verification

- `tests/test_install_agents.py` passes, including the PowerShell cases
under `pwsh`.
- The new tests cover each case, and the bun-relocation and mise-shim
tests fail on the previous code.
- shellcheck, shfmt, PSScriptAnalyzer and the prose gate are clean.
- Three recorded local passes ran. The last found nothing open.

## Pre-existing Findings, Not Filed

- `host-setup/linux/install-agents.sh` `manager_plan` (`pre-existing`,
from #4095): the Node-runtime exception covers only node. A tool
pip-installed into a mise-managed Python would get `mise upgrade
python`. Among the managed tools only cswap could reach it, and only if
installed with pip instead of the documented `uv tool install`.
- `host-setup/linux/install-agents.sh` (`pre-existing`, consolidation):
`mise_key` runs from both classification and the plan, so `mise ls
--json` runs twice per mise tool. It is harmless, and each run is
bounded by `timeout 30`.

## Not Verified

- Real package-manager installs, a `verification` gap. The tests use
stub managers and modeled layouts. Neither real bun, mise and scoop
installs, nor PowerShell's handling of their shims, has been exercised.
The maintainer's Windows 11 retest after this release covers the
npm-installed opencode case.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
ptr727 added a commit that referenced this pull request Nov 12, 2026
… Dead Bound (#4112)

## Summary

Answers the attested local review of promotion PR #4096 at 6ba6179.
Copilot reached its round cap there, so the policy hands that head to an
attested local pass.

- **herdr report vs upgrade.** The report now shows herdr's
outdated-integration and restart notes for a manager-owned herdr too, on
both platforms, but only when the manager plan will run. That matches
the post step `--upgrade` runs since #4111.
- **`Hide-Home` in `install-tools.ps1`.** It had the unbounded profile
match that #4105 fixed only in `install-agents.ps1`, so it now gets the
same component boundary. The two copies are textually identical, as
`host-setup/README.md` requires, and a test holds them equal.
- **One path-containment helper.** The new `Test-PathUnder -Path -Root
[-Below]` in `install-agents.ps1` replaces seven hand-written "at or
under this root" checks, each of which normalized separators in its own
way. Behavior is unchanged, including a scoop root written with a
trailing separator or on an absent drive. `Hide-Home` stays
self-contained so its two copies remain identical.
- **`Resolve-FinalPath`.** Its depth bound and comment were dead code: a
link loop throws from `ResolveLinkTarget` first, and the threat model
rules out guarding against it. They are removed.

## Verification

- `tests/test_install_agents.py` passes, including the PowerShell cases
under `pwsh`. `tests/test_install_tools.py` and
`tests/test_cli_usage.py` pass too.
- New tests cover the manager-owned herdr notes, the identical
`Hide-Home` copies, `Test-PathUnder`, and a scoop root with a trailing
separator. The scoop-root test fails on the intermediate commit.
- shellcheck, shfmt, PSScriptAnalyzer and the prose gate are clean.
- Three recorded local passes ran. The last found nothing open.

## Not Verified

- Real package-manager installs, a `verification` gap. The pure path
helpers, `Test-PathUnder` included, run on the native Windows CI runner,
which passed on this head. Real scoop, bun and npm installs have not
been exercised. The maintainer's Windows 11 retest after this release
covers the npm-installed opencode case.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
ptr727 added a commit that referenced this pull request Nov 12, 2026
… Upgrades (#4096)

## Summary

Promotes the agent-setup follow-ups and their review fixes, plus one
change from another hub session, from `develop` to `main`. Both follow a
native Windows 11 test of the one-step agent setup released as 2.0.1414.

- #4094:
- The Claude safety kit puts `"$defaults"` first in each `autoMode` list
that exists (`environment`, `allow`, `soft_deny`, `hard_deny`). Claude
Code's "learn rules" offer had written a list without it, which silently
replaced Claude Code's built-in rules. `--report` flags such a list as
not current.
  - The kit report's archive note names the commit.
- The Codex and opencode rules lines say when only an agent's config
directory makes it count as installed.
- `merge-and-release` step 7 also refreshes the Codex and opencode
rules, only where the kit reads current.
- #4095: an agent tool installed through npm, bun, scoop, Homebrew or
mise is upgraded through that manager, one package only. The manager is
asked whether it owns the tool, with every link resolved, before any
path shape is read, and the recorded package must own the file. A Node
runtime is never upgraded on an agent's behalf. Nothing elevates: choco,
a global scoop app, an unwritable npm prefix and Nix are reported with
their command. winget and apt stay with `upgrade-host`.

- #4102: answers this promotion's Copilot review. On Linux, npm is asked
before a `.bun` path, as on Windows, and a Nix agent under
`/nix/store/.../node_modules` is read as Nix before the generic npm
shape.

- #4105: answers this promotion's second Copilot round. On Windows,
`Test-ProtectedPath` and `Hide-Home` match only at a path component, so
`C:\Program FilesPortable` is no longer read as protected and a sibling
profile is no longer shortened. The pure path-helper tests now also run
on the native Windows CI runner.

- #4111: answers CodeRabbit's review of this promotion. bun's
`BUN_INSTALL` and `BUN_INSTALL_GLOBAL_DIR` are honored, including bun's
copied Windows shim. A mise shim is followed through `mise which`, and
mise's own listing is asked before any path shape. One herdr post step
runs after every successful upgrade. Scoop's configured roots are read
from its `config.json`.

- #4112: answers this promotion's attested local review. herdr's report
notes now match its post step for a manager-owned herdr.
`install-tools.ps1`'s `Hide-Home` gets #4105's component boundary. One
`Test-PathUnder` helper replaces seven hand-written containment checks.
A dead depth bound is removed.

- #4109, from another hub session: `dotnet-codestyle` now requires a
`global.json` pinning SDK `10.0.100` with `latestFeature` and
`allowPrerelease: false`. It leaves `LangVersion` unset (netstandard
generator projects excepted) and forbids `EnablePreviewFeatures`. The
audit gains the `csharp.sdk.globaljson` check, with a
`catalog/snippets/configs/global.json` snippet and its `spec/files.json`
C# baseline entry. `CODESTYLE.md`'s .NET summary line is updated.
Follow-ups #4106 and #4107 are filed and not in this promotion.

## Pre-existing Findings, Filed

- `host-setup/windows/install-tools.ps1:616` (`pre-existing`,
correctness): `Write-InstallFailure`'s profile replacement in log lines
does not stop at a path component. Filed as #4113.

## Not Verified

- Real package managers. #4095's tests use stub managers on PATH. The
Windows npm, scoop and bun layouts are modeled from their documented
formats and have not been run against real installs. The maintainer's
Windows 11 retest after this release covers the npm-installed opencode
case.

Closes #4103

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comments Permits the comment lines the pull request adds or edits, which the prose gate otherwise refuses

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants