fix(platform): fall back to USERPROFILE when HOME is unset#88
fix(platform): fall back to USERPROFILE when HOME is unset#88bingh0 wants to merge 2 commits intoDeusData:mainfrom
Conversation
On Windows, HOME is not set by default — the system uses USERPROFILE
instead. This caused `codebase-memory-mcp install` and all CLI commands
to fail with "error: HOME not set".
Add cbm_get_home_dir() helper in platform.{h,c} that tries HOME first,
then USERPROFILE. Replace all 17 raw getenv("HOME") callsites across
9 files with the new helper.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
QA Round 1 ReportVerdict: PASS — No issues found.What was reviewed
FindingsCorrectness
Includes
Edge cases
Build & tests
No issues to fix. The PR is clean and ready to merge. 🤖 Generated with Claude Code |
QA Round 2 ReportBuild & Tests
FindingsF1 — Test files still use raw
|
- Add explicit #include <stdlib.h> in platform.c for getenv() - Migrate test_integration.c to use cbm_get_home_dir() + cbm_tmpdir() Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
QA Round 2 Report - Deep ReviewDeep review focusing on thread safety, pointer lifetime, path separators, and edge cases. 1. Thread Safety — SAFEAll
2. Pointer Lifetime — SAFEAll callsites consume the returned 3. Path Separators — SAFE (cosmetic inconsistency)On Windows, 4. Edge Cases — PROPERLY HANDLED
5. Completeness — VERIFIED
VerdictNo issues found. PR is ready to merge. |
bingh0
left a comment
There was a problem hiding this comment.
QA Round 3 Report
Scope
Full review of all 10 changed files + security audit of the new cbm_get_home_dir() helper and all callsites.
Correctness — PASS
cbm_get_home_dir()logic is correct: checksHOMEfirst, falls back toUSERPROFILE, rejects empty strings viah[0], returnsNULLwhen both are absent.- All 17 production
getenv("HOME")callsites migrated — confirmed onlyplatform.c:225(inside the helper itself) remains. - All
/tmpfallbacks replaced withcbm_tmpdir()which is cross-platform (TEMP/TMPon Windows,/tmpon Unix). - Error messages consistently updated to mention
USERPROFILE. - All files that call
cbm_get_home_dir()includeplatform.h. All files that callcbm_tmpdir()includecompat.h. - Function is placed outside the
#ifdef _WIN32/#endifguard, so it compiles on all platforms. ✓
Thread Safety — PASS
getenv()is markedconcurrency-mt-unsafebut all callsites are serialized: CLI commands run on main thread, MCP server uses single event loop, HTTP handlers are serialized by Mongoose'smg_mgr_poll().- No pointer is stored across function boundaries — all callsites consume the return immediately in
snprintf()orstrcmp(). No risk of invalidation bysetenv().
Pointer Lifetime — PASS
cbm_get_home_dir()returns a pointer to the environment's internal storage (same as rawgetenv()). No callsite stores this pointer persistently or passes it to async callbacks. All usage is immediatesnprintf()into stack buffers. No lifetime regression.
Security Review
S1 — Pre-existing: No sanitization of name/project query params used in file paths (informational, not introduced by this PR)
Multiple HTTP handlers (handle_adr_get, handle_adr_save, handle_delete_project, handle_project_health, handle_layout) take a user-supplied name or project query parameter and interpolate it directly into a file path:
snprintf(db_path, sizeof(db_path), "%s/.cache/codebase-memory-mcp/%s.db", home, name);A name value like ../../etc/passwd would construct a path outside the intended directory. While cbm_store_open_path() would likely fail on a non-SQLite file, handle_delete_project calls unlink(db_path) which could delete arbitrary files. The .db suffix provides some mitigation (path becomes ../../etc/passwd.db), but it's worth noting.
This is pre-existing and NOT introduced by this PR, but the PR does change the home portion of these paths, so flagging for awareness. A future PR could add a check that name contains no / or \ characters.
S2 — Pre-existing: handle_browse allows arbitrary directory listing (informational)
The /api/browse?path=X endpoint lists any directory on the filesystem. The server binds to 127.0.0.1 only (line 11), which limits exposure, but any local process can enumerate the filesystem. This is pre-existing and by design (it's a file picker).
S3 — Pre-existing: Unescaped JSON string interpolation in handle_browse (informational)
pos += snprintf(buf + pos, ..., "\"%s\"", ent->d_name);Directory names containing " or \ would produce malformed JSON. Minor, pre-existing, not introduced by this PR.
S4 — cbm_get_home_dir() trusts environment variables — ACCEPTABLE
The function trusts HOME and USERPROFILE without validation. An attacker who can set environment variables already has process-level access, so this is standard practice and matches the behavior of getenv("HOME") it replaces. No regression.
S5 — No new attack surface introduced — PASS
This PR is a pure mechanical refactor: it centralizes existing getenv("HOME") calls into a helper and adds a USERPROFILE fallback. No new inputs, no new endpoints, no new file operations. The security posture is unchanged or marginally improved (consistent fallback behavior).
Minor Observations
M1 — test_ui.c still uses raw getenv("HOME") (5 instances)
These save/restore HOME for test isolation via setenv()/unsetenv(). This is intentional — they're manipulating the env var itself, not resolving the home directory. No action needed (correcting QA round 2's F1 which flagged these).
M2 — Mixed path separators on Windows (cosmetic)
USERPROFILE returns backslash paths (e.g., C:\Users\Alice), but paths are concatenated with forward slashes (%s/.cache/...). Windows APIs accept mixed separators, so this is functionally safe. Could normalize in a future PR if desired.
Verdict
PASS — No issues introduced by this PR. The change is a clean, mechanical refactor that correctly centralizes home directory resolution with proper cross-platform fallback. No new security surface. Pre-existing security observations (S1–S3) are noted for future hardening but are outside the scope of this PR.
🤖 Generated with Claude Code
Summary
Fixes #77 (Windows:
codebase-memory-mcp installfails with "error: HOME not set")cbm_get_home_dir()helper inplatform.{h,c}that triesHOMEfirst, thenUSERPROFILEgetenv("HOME")callsites across 9 files with the new helperUSERPROFILEas the Windows alternativeContext
On Windows,
HOMEis not set by default — the system usesUSERPROFILEinstead. This caused all CLI commands (install,uninstall,update,config) to fail immediately. Confirmed by @indy-singh in #77.Note: The v0.5.4 release includes dual MCP config writes (
~/.claude/.mcp.json+~/.claude.jsonfor Claude Code >=2.1.80, fixes #69), but this change doesn't appear to be onmainyet. This PR does not include that fix to avoid duplication.Test plan
scripts/build.sh— clean compile, no warningsscripts/test.sh— all tests pass (3 pre-existing snippet_fuzzy failures unrelated to this change)HOME= USERPROFILE=/tmp ./build/c/codebase-memory-mcp install --dry-run -y— verifies fallback worksHOME= USERPROFILE= ./build/c/codebase-memory-mcp install --dry-run -y— verifies improved error message🤖 Generated with Claude Code