Skip to content

fix(skills): rmtree scope guard + default pre_update_backup to true (#48200) - #48424

Merged
teknium1 merged 2 commits into
mainfrom
fix/48200-rmtree-guard-backup
Jun 18, 2026
Merged

fix(skills): rmtree scope guard + default pre_update_backup to true (#48200)#48424
teknium1 merged 2 commits into
mainfrom
fix/48200-rmtree-guard-backup

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Summary

hermes update can no longer silently wipe ~/.hermes/. Two independent defenses for the #48200 data-loss class:

  1. Scope guard on _rmtree_writable — refuses to remove anything that isn't a strict child of HERMES_HOME/skills/. The five call sites all pass a skill subdir or its .bak sibling; if any ever computes a path that escapes (bad join, missing default, mid-flight exception, degenerate dest collapsing to the skills root), the helper now raises a loud ValueError instead of shutil.rmtree(~/.hermes).
  2. pre_update_backup defaults to true — every hermes update now zips HERMES_HOME to backups/ before any destructive step. Opt out with --no-backup or pre_update_backup: false.

Changes

  • tools/skills_sync.py: strict-child scope guard in _rmtree_writable.
  • hermes_cli/config.py + hermes_cli/main.py: flip pre_update_backup default to true (incl. the missing-key fallback).
  • tests/tools/test_skills_sync.py, tests/hermes_cli/test_backup.py: regression tests for both.

Validation

_rmtree_writable target Result
skill subdir under skills/ removed
.bak sibling under skills/ removed
skills/ root itself refused
~/.hermes/ (HERMES_HOME) refused
skills.bak (the .with_suffix sibling, lands in HERMES_HOME) refused
/ refused

E2E verified against a real temp HERMES_HOME: legitimate removals succeed, every escape is refused, .env and MEMORY.md survive. pre_update_backup default confirmed True. 124 targeted tests pass.

Follow-up on top of the salvage

The salvaged guard allowed _rmtree_writable(SKILLS_DIR) itself ("full reset"). No caller passes the root, so allowing it only preserved the #48200 footgun — a dest collapsing to the root would wipe every installed skill. Tightened to a strict-child relationship and updated the test accordingly.

Salvaged from #48340 by @Kewe63 (commit cherry-picked, authorship preserved) + one follow-up hardening commit. Closes #48200.

Infographic

dont-wipe-hermes

Kewe63 and others added 2 commits June 18, 2026 05:46
…48200)

Defense-in-depth fix for the silent wipe of ~/.hermes/ documented in
#48200. A `hermes update --yes` run silently destroyed a user's
.env, MEMORY.md, kanban.db, custom skills, and scripts. Two changes:

1. `_rmtree_writable` in tools/skills_sync.py now refuses to rmtree
   anything outside SKILLS_DIR (the HERMES_HOME/skills/ root).
   All five call sites pass paths under SKILLS_DIR, so the guard is
   a no-op for current code and a loud, recoverable failure for
   any future regression (bad path join, malicious bundled
   manifest, stale path in scope after an exception).

2. The default `updates.pre_update_backup` flips from false to
   true in hermes_cli/config.py. A few minutes of zip per update
   is negligible compared to silent total data loss. Still
   overridable; --no-backup still works for one-off opt-out.

Five new tests in TestRmtreeWritableScopeGuard (root path,
hermes home, sibling dir, skills root itself, subdir) plus a
flipped `test_default_enabled_creates_backup` in test_backup.py.
178/178 tests pass in the two affected files. Public method
signatures unchanged, no test-stub blast radius.

Closes #48200
…-tree

The salvaged guard allowed _rmtree_writable(SKILLS_DIR) itself. No call
site ever passes the root — every site passes a skill subdir or its .bak
sibling — so allowing the root only preserves the #48200 footgun (a dest
that collapses to the root wipes every installed skill). Require a strict
strict-child relationship and update the test that documented the
nonexistent 'full reset' capability.
@github-actions

Copy link
Copy Markdown
Contributor

🔎 Lint report: fix/48200-rmtree-guard-backup vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 11012 on HEAD, 11011 on base (🆕 +1)

🆕 New issues (1):

Rule Count
unresolved-import 1
First entries
tests/tools/test_skills_sync.py:5: [unresolved-import] unresolved-import: Cannot resolve imported module `pytest`

✅ Fixed issues: none

Unchanged: 5770 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@alt-glitch alt-glitch added type/security Security vulnerability or hardening comp/cli CLI entry point, hermes_cli/, setup wizard tool/skills Skills system (list, view, manage) P0 Critical — data loss, security, crash loop labels Jun 18, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Related: #48200 (the P0 data-loss bug this fixes), #48271 (scope-guard-only fix), #48340 (same scope-guard + backup-default approach). This is the authoritative version of the #48200 fix — combining the strict-child scope guard on _rmtree_writable with flipping pre_update_backup to true by default. Reviewers can build on / supersede #48271 and #48340 from here.

@egilewski egilewski 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.

looks mergeable

Checked against current main (ef4b897a1843cd32c4f141f55db60f0f0602cc98) and PR head cc9a86ad3d4de23c98670dd0e88516b0d15aeb9a.

  • git merge-tree --write-tree refs/remotes/upstream/main refs/remotes/upstream/pr/48424 merged cleanly.
  • A synthetic current-main probe reproduced the dangerous path: _rmtree_writable(HERMES_HOME) removed the temp Hermes home and .env; the same probe on PR head raised ValueError and preserved .env.
  • /home/mac/hermes-agent/.venv/bin/python -B -m pytest -p no:cacheprovider -o addopts= tests/tools/test_skills_sync.py tests/hermes_cli/test_backup.py -q passed: 178 passed in 25.36s.
  • CodeRabbit clean-pass completed with no findings.

Worth improving

  • Refresh stale wording that still says pre-update backups are off/default false in _run_pre_update_backup docs, the --backup help text, and a couple of test comments.

Signed: GPT-5.5-xhigh in Codex

@teknium1
teknium1 merged commit 25c590c into main Jun 18, 2026
35 checks passed
@teknium1
teknium1 deleted the fix/48200-rmtree-guard-backup branch June 18, 2026 15:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard P0 Critical — data loss, security, crash loop tool/skills Skills system (list, view, manage) type/security Security vulnerability or hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

hermes update --yes wiped entire ~/.hermes/ directory (.env, MEMORY.md, kanban.db, skills, scripts — all gone)

4 participants