fix(uninstall): refuse to remove current working directory during cleanup#90813
Conversation
|
Codex review: needs real behavior proof before merge. Reviewed June 6, 2026, 1:09 AM ET / 05:09 UTC. Summary PR surface: Source +3, Tests +35, Docs +6. Total +44 across 3 files. Reproducibility: yes. from source inspection, though not live-executed in this read-only review. Current main's removePath reaches fs.rm after only empty/root/home guards, and uninstall passes resolved state/workspace paths into that shared cleanup path. Review metrics: 1 noteworthy metric.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land this shared cleanup guard once final-head proof covers the cwd and ancestor cases, then close the linked data-loss issue as fixed by the merged PR. Do we have a high-confidence way to reproduce the issue? Yes from source inspection, though not live-executed in this read-only review. Current main's removePath reaches fs.rm after only empty/root/home guards, and uninstall passes resolved state/workspace paths into that shared cleanup path. Is this the best way to solve the issue? Yes, this is the best code boundary: guarding removePath protects uninstall, reset, and shared cleanup callers instead of patching one CLI stanza. The remaining issue is proof freshness for the second commit, not the implementation shape. AGENTS.md: found and applied where relevant. Codex review notes: model gpt-5.5, reasoning high; reviewed against ab7c92282522. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +3, Tests +35, Docs +6. Total +44 across 3 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
…anup (openclaw#90813) * fix(uninstall): refuse to remove current working directory during cleanup * fix(uninstall): guard cleanup ancestors of cwd --------- Co-authored-by: sallyom <somalley@redhat.com>
…anup (openclaw#90813) * fix(uninstall): refuse to remove current working directory during cleanup * fix(uninstall): guard cleanup ancestors of cwd --------- Co-authored-by: sallyom <somalley@redhat.com>
…anup (openclaw#90813) * fix(uninstall): refuse to remove current working directory during cleanup * fix(uninstall): guard cleanup ancestors of cwd --------- Co-authored-by: sallyom <somalley@redhat.com>
Summary
Fixes #90806.
openclaw uninstall --all --yes --non-interactivecould delete the caller's current working directory when the resolved workspace or state path matchedprocess.cwd()or contained it. TheremovePathhelper already rejected empty strings, filesystem root, and the home directory, but it did not guard against the active working directory.Changes:
src/commands/cleanup-utils.ts:isUnsafeRemovalTargetnow refuses targets that are or containprocess.cwd().src/commands/cleanup-utils.test.ts: regression tests provingremovePathrejects cwd and a directory containing cwd withok: falseand logs an error.docs/install/uninstall.md: moves--dry-run --allahead of the non-interactive stanza and adds a caution note.Verification
node scripts/run-vitest.mjs src/commands/cleanup-utils.test.ts --runpasses (10/10).node scripts/run-oxlint.mjs src/commands/cleanup-utils.ts src/commands/cleanup-utils.test.tspasses.pnpm exec oxfmt --write src/commands/cleanup-utils.ts src/commands/cleanup-utils.test.ts docs/install/uninstall.mdapplied.8cfb48d7d9e6c264ce0a407bea3efeb604f5ecf7passed: built the CLI on Node.js v24.16.0, ranopenclaw uninstall --all --yes --non-interactivefrom inside a configured workspace, observed the unsafe-path refusal, and verified the workspace still existed.Real behavior proof
Behavior addressed:
openclaw uninstall --all --yes --non-interactivecould delete the current working directory (#90806).Real environment tested: Local OpenClaw source checkout on branch
fix/uninstall-cwd-guard, Linux, Node.js 22.Exact steps or command run after this patch:
Evidence after fix: Terminal capture from running the patched
removePathagainst the current working directory:Observed result after fix:
removePath(process.cwd())returns{ ok: false }instead of deleting the directory.Refusing to remove unsafe path: ....Additional maintainer proof: Command-level uninstall proof in Crabbox local-container.
Real environment tested: Crabbox local-container via Podman,
node:24-bookworm, Node.js v24.16.0, PR head8cfb48d7d9e6c264ce0a407bea3efeb604f5ecf7.Exact steps or command run after this patch:
node scripts/crabbox-wrapper.mjs run \ --provider local-container \ --local-container-runtime podman \ --local-container-image node:24-bookworm \ --fresh-pr openclaw/openclaw#90813 \ --idle-timeout 30m \ --ttl 60m \ --timing-json \ --shell -- 'set -euo pipefail; repo=$(pwd); echo PROOF_NODE=$(node -v); echo PROOF_HEAD=$(git rev-parse HEAD); git merge-base --is-ancestor 8cfb48d7d9e6c264ce0a407bea3efeb604f5ecf7 HEAD; env CI=1 corepack pnpm install --frozen-lockfile >/tmp/openclaw-install.log; env CI=1 corepack pnpm build >/tmp/openclaw-build.log; tmp=$(mktemp -d); home=$tmp/home; state=$tmp/state; workspace=$tmp/workspace; nested=$workspace/nested; mkdir -p "$home" "$state" "$nested"; printf "{\"agents\":{\"defaults\":{\"workspace\":\"%s\"}}}\n" "$workspace" > "$state/openclaw.json"; echo PROOF_WORKSPACE=$workspace; echo PROOF_CWD=$nested; set +e; output=$(cd "$nested" && env HOME="$home" OPENCLAW_STATE_DIR="$state" OPENCLAW_CONFIG_PATH="$state/openclaw.json" OPENCLAW_WORKSPACE_DIR="$workspace" node "$repo/dist/entry.js" uninstall --all --yes --non-interactive 2>&1); status=$?; set -e; echo PROOF_EXIT=$status; printf "%s\n" "$output"; printf "%s\n" "$output" | grep -F "Refusing to remove unsafe path: $workspace"; test -d "$workspace"; test -d "$nested"; echo PROOF_WORKSPACE_STILL_EXISTS=1; rm -rf "$tmp"'Evidence after fix: Terminal capture from running the built CLI against a workspace that contains the command's current working directory:
Observed result after fix:
openclaw uninstall --all --yes --non-interactiverefuses to remove the configured workspace when the command runs from a nested directory inside that workspace.Refusing to remove unsafe path: ....What was not tested: A packaged global npm install. The proof built this PR's CLI from source in an isolated container and exercised the same
uninstall --all --yes --non-interactivecommand path.