Skip to content

Commit 99d3d7c

Browse files
committed
fix(hermes): harden hermes_home() and widen session-read allowlist
Three small follow-ups from review of the prior commit: 1. Reject relative `$HERMES_HOME` and fall through to platform default. Coffee CLI changes process CWD per tab via the launchpad's dir picker, so a relative HERMES_HOME would resolve to different absolute paths on different surfaces (history scan vs. plugin install vs. skill mirror). Log a warning and keep the documented behavior. 2. Push `~/.hermes` into read_native_session's allowlist when it differs from the resolved hermes_home(). A user who exports HERMES_HOME to point at a fresh location can still open previously-collected sessions that haven't been migrated off the old dotdir path. 3. Note in ToolDescriptor::skill_dir that the Hermes branch uses `skill_dir_relative` as the source of truth (only the root swaps to hermes_home), so future layout changes upstream are one-field edits.
1 parent 80ae5e3 commit 99d3d7c

3 files changed

Lines changed: 29 additions & 5 deletions

File tree

src/server.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1923,18 +1923,26 @@ fn read_native_session(file_path: String) -> Result<String, String> {
19231923
// sessions but reading them back would 403). Path canonicalization
19241924
// already resolved symlinks, so this is a pure prefix check.
19251925
let home = dirs::home_dir().ok_or("Cannot determine home directory")?;
1926+
// Hermes Agent's data root is platform-dependent (`%LOCALAPPDATA%\hermes`
1927+
// on Windows, `~/.hermes` elsewhere, or `$HERMES_HOME` if set). See
1928+
// tools/hermes.rs::hermes_home. We also push the legacy `~/.hermes` when
1929+
// it's distinct from the resolved root so a user who exports HERMES_HOME
1930+
// mid-life can still read previously-collected sessions still sitting at
1931+
// the dotdir path.
1932+
let hermes_root = crate::tools::hermes::hermes_home();
1933+
let hermes_legacy = home.join(".hermes");
19261934
let mut allowed: Vec<std::path::PathBuf> = vec![
19271935
home.join(".claude"),
1928-
// Hermes Agent's data root is platform-dependent
1929-
// (`%LOCALAPPDATA%\hermes` on Windows, `~/.hermes` elsewhere, or
1930-
// `$HERMES_HOME` if set). See tools/hermes.rs::hermes_home.
1931-
crate::tools::hermes::hermes_home(),
1936+
hermes_root.clone(),
19321937
home.join(".codex").join("sessions"),
19331938
home.join(".gemini").join("tmp"),
19341939
home.join(".qwen").join("projects"),
19351940
home.join(".local").join("share").join("opencode"),
19361941
home.join(".openclaw").join("agents"),
19371942
];
1943+
if hermes_legacy != hermes_root {
1944+
allowed.push(hermes_legacy);
1945+
}
19381946
for tool in ["claude", "hermes", "codex", "gemini", "qwen", "opencode", "openclaw"] {
19391947
let cfg = crate::tool_config::get(tool).history_path;
19401948
if !cfg.is_empty() {

src/tools/hermes.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,19 @@ pub fn hermes_home() -> PathBuf {
5858
if let Ok(v) = std::env::var("HERMES_HOME") {
5959
let trimmed = v.trim();
6060
if !trimmed.is_empty() {
61-
return PathBuf::from(trimmed);
61+
let candidate = PathBuf::from(trimmed);
62+
if candidate.is_absolute() {
63+
return candidate;
64+
}
65+
// Relative HERMES_HOME would resolve against the current
66+
// process CWD — and Coffee CLI changes CWD per tab via the
67+
// launchpad's directory picker, so different surfaces (history
68+
// scan, allowlist, plugin install, skills mirror) would silently
69+
// resolve to different dirs. Reject + fall through to defaults.
70+
log::warn!(
71+
"[hermes] ignoring relative HERMES_HOME='{}' — must be absolute",
72+
trimmed
73+
);
6274
}
6375
}
6476
#[cfg(windows)]

src/tools/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,10 @@ impl ToolDescriptor {
200200
pub fn skill_dir(&self, home: &Path) -> Option<PathBuf> {
201201
let rel = self.skill_dir_relative?;
202202
if self.id == hermes::DESCRIPTOR.id {
203+
// `rel` is the source of truth even on Hermes — we just swap
204+
// the root from `home` to `hermes_home()`. If the upstream
205+
// layout ever moves the skills dir, updating
206+
// `skill_dir_relative` in hermes.rs is enough.
203207
Some(hermes::hermes_home().join(rel))
204208
} else {
205209
Some(join_relative(home, rel))

0 commit comments

Comments
 (0)