fix(skills): guard _rmtree_writable against escaping SKILLS_DIR - #48271
Closed
jackjin1997 wants to merge 1 commit into
Closed
fix(skills): guard _rmtree_writable against escaping SKILLS_DIR#48271jackjin1997 wants to merge 1 commit into
jackjin1997 wants to merge 1 commit into
Conversation
_rmtree_writable() ran a bare shutil.rmtree() on whatever path it was given. Every caller passes a skill directory or its .bak sibling under SKILLS_DIR, but there is no boundary check: a degenerate dest that collapses to SKILLS_DIR itself (rel == '.') — or its .bak sibling, which lands in HERMES_HOME — would let a routine skill sync escalate into wiping the skills root or other HERMES_HOME contents. Add a scope guard that resolves the target and refuses to remove SKILLS_DIR itself or anything outside it before calling rmtree. A ..-traversal escape isn't possible (_compute_relative_dest builds dest via Path.relative_to), so guarding the root boundary is sufficient. This is defense-in-depth for the data-loss class in NousResearch#48200 (hardening recommendations 4/6); it is not claimed to be the sole root cause of the reported full ~/.hermes wipe. Related to NousResearch#48200
4 tasks
Contributor
Author
|
Closing this — it's been superseded. Current main already carries an equivalent scope guard in No point carrying a redundant second guard, so closing. Thanks @teknium1 — glad the direction was right. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Adds a scope guard to
_rmtree_writable()intools/skills_sync.pyso a skill sync can neverrmtreeSKILLS_DIRitself or any path outside it. This is defense-in-depth for the data-loss class reported in #48200 (hardening recommendations 4 and 6).Related Issue
Related to #48200
Scope note (intentionally not "Fixes"): #48200 reports a full
~/.hermes/wipe whose exact mechanism the reporter could not pin down (logs lost). This PR does not claim to be the sole root cause fix — it closes one concrete escalation path the reporter explicitly flagged (recs 4/6) and is valuable hardening on its own. The other recommendations (defaultpre_update_backup, an update timeout, post-update validation) are policy/feature decisions better left to maintainers.Root Cause (of the guarded path)
_rmtree_writable()ran a bareshutil.rmtree()on whatever it was handed. All callers pass a skill directory or its.baksibling computed via_compute_relative_dest()→SKILLS_DIR / rel. A..-traversal can't occur (Path.relative_toforbids it), but ifrelever collapses to.thendest == SKILLS_DIR, and:_rmtree_writable(dest)would wipe the entire skills root, andbackup = dest.with_suffix(".bak")becomesHERMES_HOME/skills.bak, so a stale-backup cleanuprmtreewould target a path insideHERMES_HOMEbut outsideskills/.There was no boundary check preventing either.
Changes Made
tools/skills_sync.py: resolve the target and raiseValueErrorif it equalsSKILLS_DIRor is not strictly inside it, before anyrmtree.tests/tools/test_skills_sync.py: 5 tests — removes a skill dir and a.baksibling insideSKILLS_DIR(still works); refusesSKILLS_DIRitself, askills.baksibling inHERMES_HOME, and theHERMES_HOMEroot.How to Test
Type of Change
Checklist
mainpytest tests/tools/test_skills_sync.pypasses (64: 59 existing + 5 new), no regressionsSKILLS_DIRto a tmp dir, no writes to real~/.hermes)SKILLS_DIR)AI Disclosure
This issue was investigated and the fix written with AI assistance.