Skip to content

Commit 3ecdbf2

Browse files
committed
fix(security): replace /tmp fallback with sentinel path in containment guard
The _detect_safe_base_dir function was falling back to Path('/tmp') when neither CLAUDE_PROJECT_DIR, CWD, nor a .git ancestor could be resolved. Since SAFE_BASE_DIR is the containment floor for all write-path guards via _is_relative_to, using /tmp effectively disabled containment as any path under /tmp would pass validation. Changed to return a non-existent sentinel path (/__nonexistent_containment_sentinel__) that ensures all containment checks fail in degenerate cases, rather than allowing writes to a world-writable directory. Fixes: CWE-22 path traversal vulnerability in fallback path
1 parent bb9afa2 commit 3ecdbf2

1 file changed

Lines changed: 13 additions & 4 deletions

File tree

.claude/hooks/Stop/invoke_skill_learning.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,16 @@ def _detect_safe_base_dir() -> Path:
100100
Without this guard, an attacker who can set the env var to '/' would
101101
defeat every write-path guard in this file. Mirrors the pattern in
102102
``invoke_observation_sync._get_repo_root``.
103+
104+
Returns a non-existent sentinel path on failure to ensure all containment
105+
checks fail rather than allowing writes to world-writable directories.
103106
"""
107+
# Sentinel path that should never exist. When returned, all _is_relative_to
108+
# checks will fail, ensuring no writes are permitted in degenerate cases.
109+
# Using /tmp would effectively disable containment since any path under
110+
# /tmp would pass validation.
111+
sentinel = Path("/__nonexistent_containment_sentinel__")
112+
104113
script_dir = str(Path(__file__).resolve().parent)
105114
env_dir = os.environ.get("CLAUDE_PROJECT_DIR", "").strip()
106115
if env_dir:
@@ -126,15 +135,15 @@ def _detect_safe_base_dir() -> Path:
126135
try:
127136
cur = Path.cwd().resolve()
128137
except OSError:
129-
# cwd may have been deleted; fall back to a known-safe directory
130-
return Path.home() if Path.home().exists() else Path("/tmp")
138+
# cwd may have been deleted; return sentinel to fail all containment checks
139+
return sentinel
131140
while True:
132141
if (cur / ".git").exists():
133142
return cur
134143
parent = cur.parent
135144
if parent == cur:
136-
# No .git found; fall back to a known-safe directory
137-
return Path.home() if Path.home().exists() else Path("/tmp")
145+
# No .git found; return sentinel to fail all containment checks
146+
return sentinel
138147
cur = parent
139148

140149

0 commit comments

Comments
 (0)