fix(update): preserve proxy on npm cache failures - #533
Conversation
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
|
Addressed the CodeRabbit outside-diff cache-traversal finding in commit 1490ca9. The preflight now fails closed after 50,000 discovered entries or 10 seconds, with injectable budgets and checks around stat, directory reads, and entry iteration. Regressions cover both limits; the current 15,718-entry cache passes in about 317ms, and the retained 36,202-entry incident cache still rejects promptly on ownership mismatch. |
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
|
CodeRabbit follow-up is in 314eb53. I also adopted the outside-diff PID-helper consolidation because it keeps all replacement-process safety decisions on one implementation while preserving the direct path PID for killProxy. I did not add the optional errorIssue factory: that suggestion is presentation-only, the current error variants are already covered by the structured result contract, and avoiding an unrelated refactor keeps this incident fix focused. Verification: 55 focused tests passed; the full pre-push gate passed 4,964 tests, typecheck, GUI lint, and privacy scan. |
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
|
@Ingwannu Follow-up head a28d6f8 is pushed after the latest Codex and CodeRabbit findings. The exact staged diff passed the local guard, 74 focused tests, and two full pre-push gates with 5,074 tests / 24,933 assertions, typecheck, GUI lint, and privacy scan. The new fork workflow runs are awaiting maintainer approval: Cross-platform CI https://github.com/lidge-jun/opencodex/actions/runs/30262870035 and React Doctor https://github.com/lidge-jun/opencodex/actions/runs/30262869987. Please approve these exact-head runs when convenient. This requests CI execution only; the PR remains open and unmerged while the fresh automated reviews complete. |
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
|
@Ingwannu The follow-up head
The local pre-push gate passed 5,078 tests with typecheck, GUI lint, and privacy scan. I will keep monitoring the exact-head reviews and CI after approval. |
Two read-only audit lenses returned 30 contradictions; the verified ones are applied here and recorded in 006_corrections.md. Factual corrections: - claude-code#1124 closed 2025-05-16, not 2025-08-10 - 13 open enhancement issues, not 11 - #529 is merged, so issue #42 phase 2 is done - #418 has no PR behind it; it was miscounted as in-flight - #528 does not contain #424's current head, so it needs a rebase - #527's retarget is manual and independent of #526 Framing corrections: - stale-needs-info.yml is absent from the default branch, so bundle C had no real deadline - the debug switch #543's reporter asked for already exists - whether #545 is our defect is answerable from code, not owner judgment - #491 is an OAuth credential change, so it is a security boundary rather than a warm-up; #533 and #447 keep their security classification too Structure: bundle G added for roadmap honesty, bundle F rescoped to the security boundary, #498 split out of bundle B, cycle order rebuilt, and the loop archetype corrected to decision-elicitation.
This comment was marked as outdated.
This comment was marked as outdated.
Review — bugs, edge cases, leaks & securityUpdated verdict: changes requested. The original review found the security posture deliberately fail-closed and broadly sound, with one residual availability concern. A follow-up review of the current head found two additional real blockers that should be fixed before merge. Security
Bugs and edge cases
Leaks and resource hygiene
Non-blocking notes
RecommendationFix the two blocking issues first:
Then either document that clean nonzero installer failures require manual recovery, or add a narrowly proven |
|
Maintainer takeover follow-up opened as #557 against It addresses the two high blockers from the latest review on this PR:
I am leaving this PR open/draft and not merging the takeover PR automatically because the remaining nonzero-installer recovery policy is updater/security-adjacent and needs maintainer review. |
|
Follow-up on the takeover PR #557. Its first CI run failed on Cause: expect(launchersDuringInstall[0]?.endsWith("/bin/ocx.mjs")).toBe(true);The value under test comes from
If you prefer to keep this PR as the merge vehicle, please cherry-pick the same assertion fix here — otherwise Windows CI will keep failing on this branch. |
|
@lidge-jun When there is no near reply i would suggest we just maintainer-takeover this one so we can fix it for the users. |
|
Thanks for the follow-up. I confirm that #557 should be the sole merge vehicle, so I will not cherry-pick the Windows assertion fix back into #533 or continue parallel changes here. I have reviewed #557 and support keeping nonzero installer failures fail-closed with a documented manual recovery path. Please proceed with the maintainer takeover. GitHub currently reports #557 as conflicting with the latest Once #557 is merged, #533 can be closed as superseded. Please retain attribution to #533 and @WZBbiao; the attribution request in #557 still stands. |
|
CLOSE-RECOMMENDED once #557 lands — superseded, not rejected. Leaving this open for now. I verified the relationship rather than inferring it from the descriptions. So the design here is not in question. The pre-stop cache preflight, the bounded process-tree installer wrapper, the PID and generation checks before recovery, the validated current and retired launcher probing, and the captured-port restoration all carry forward intact. What this branch lacks are the two blockers accepted at the end of the review: no effective Both of those are #557's job now, and I have flagged there that its sanitizer does not yet close the second one — its path regexes stop at whitespace, so a Windows profile containing a space survives unredacted. That is a #557 fix, not something to reopen here. To be explicit about two things this thread asked for. Attribution stays: #557 carries your commits, and the merge will say so. And nobody is asking you to rebase 3,000 lines in parallel — that was the point of the takeover. What happens next: this stays open until #557 is corrected and merged, then closes as superseded. If you want to help, the highest-value thing is reviewing the sanitizer fix on #557 when it appears, since you know that code better than anyone. Thanks for carrying this as far as you did. 91 reviews is a lot of patience. |
|
Status update so this is not left ambiguous: staying open, with a stated closing condition. I am not closing this yet even though #557 supersedes it, because #557 still carries an unfixed blocker — its log sanitizer terminates every path regex at whitespace, so a Windows profile with a space in the name survives unredacted into persisted update-job state. I ran its regexes to confirm. Closing this now would leave the original closed and the successor blocked, which is the worst of both. The condition is simple: this closes as superseded when #557 lands. Not when #557 is corrected — when it merges. Your commits go in with it and attribution stays. Nothing is being asked of you here. If you want to help, reviewing the sanitizer fix on #557 when it appears would be worth more than anything on this branch, since you wrote the surrounding code. |
|
Closing in favour of #557, which supersedes this branch. Comparing by patch-id, all 14 update-recovery commits here appear in #557, and #557 adds two further hardening commits on top. Keeping both open means reviewing the same series twice, so this one closes and #557 remains the live vehicle. To be explicit about one thing that could have been misread: #737 ( |
Merge dev (768 commits, v2.10.0 line) into the update-recovery branch and
finish the two remaining review items on top of it:
- Log sanitizer now redacts space-containing profile paths: anchor-based
matching redacts through spaced Windows/POSIX profiles up to the npm/cache
anchors (.npm, _cacache, _npx, .opencodex-, ocx.mjs) instead of stopping
at the first space (Wibias reproduction: 'C:\Users\Alice Smith\.npm\...'
previously leaked whole).
- Policy A (maintainer decision, Wibias recommendation): a failed GUI npm
installer no longer triggers automatic proxy recovery. The failed
installer may still be mutating the global package tree, so the update
job stays failed and the log names the manual path ('ocx service
install' or 'ocx start --port N'). Observation steps (healthy probe,
pre-update PID identity, replacement detection) are kept; the launcher
discovery/restart machinery and recovery-tree-scan worker are removed.
- dev's hardened restart/service path is taken wholesale; branch-side
preflight, sanitizer, fail-closed recovery, and liveness capture are
re-applied on top.
- docs-site lifecycle reference (en/ja/ko/ru/zh-cn) documents the npm
cache pre-flight and the fail-closed manual recovery path.
Supersedes #533; original work by @WZBbiao (imported commits preserve the
wzb author identity, which GitHub does not link).
Summary
SIGKILL, and persisted failure text contains neither cache/entry paths nor Unix account identifiers.node launcher --versioncheck before attempting fallback.Incident
A real GUI update from 2.7.33 to 2.7.41 reproduced the failure. The updater stopped the proxy, then
npm install -gfailed withEACCESbecause the configured~/.npmcache contained hundreds of entries owned by a different Unix account. npm had already moved the previous package into a hidden transaction directory, leaving a partial install, and the failed update path exited without restoring the service.The preserved incident cache is rejected by this preflight for an ownership mismatch, while the current user-owned cache passes.
Verification
135 pass,0 fail)bun run typecheckbun run prepushand the repository pre-push hook (5059 pass,0 fail, 382 files; typecheck, GUI lint, and privacy scan passed)npm pack --dry-run --ignore-scripts --jsonconfirmed the preflight and installer-process runtime/type files are included in the 337-entry packageSIGTERMis terminated by the parent deadlineSecurity Review
This change touches the dependency-installation boundary. The preflight runs the configured npm executable with fixed
config get cachearguments, then runs the packaged scanner through the current Node/Bun executable with fixed arguments,shell: false, bounded stdin, a 10-second timeout, andSIGKILL. The scanner canonicalizes only the configured cache-root symlink, performs bounded read-onlylstat/directory traversal, and never follows nested symlinks or mutates cache contents. Failed-install recovery considers at most two current/newest packages and accepts them only when the package root, manifest, and launcher are owned by the current UID or root, are not symlinks, and the launcher is contained in a regular package directory whose manifest has the expected package name and pre-update version. Maintainer security review is explicitly requested for the process boundary, path handling, retired-package validation, PID identity checks, and fail-closed behavior.Checklist
Summary by CodeRabbit
ocx update, aborting before the proxy is stopped when ownership can’t be verified.ocx updateCLI docs (including translations) to describe the new pre-check.