Skip to content

fix(tools): make memory.include_list read-only + harden temp include-cache path (#474, #473) - #489

Draft
TYRMars wants to merge 1 commit into
mainfrom
claude/vibrant-dijkstra-wddus3
Draft

fix(tools): make memory.include_list read-only + harden temp include-cache path (#474, #473)#489
TYRMars wants to merge 1 commit into
mainfrom
claude/vibrant-dijkstra-wddus3

Conversation

@TYRMars

@TYRMars TYRMars commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Auto-resolving open issues. Two self-contained security/correctness bugs in the memory include-cache plumbing (packages/tools/src/memory-tools.ts), each with a regression test. The branch is constrained to claude/vibrant-dijkstra-wddus3, so both land here.

Fixes #474memory.include_list (a read-only, ungated tool) performed a network git clone

MemoryIncludeListTool advertises itself as read-only (description "No mutations", category: "read", not approval-gated). But its invoke() called resolveInclude(d, cache), which for an uncached git+ directive does fs.mkdir + git clone --depth 1 into the local cache.

Consequences of merely listing directives:

  • writes to disk (the "No mutations" tool mutates the filesystem);
  • network egress to a directive-named host (directive URLs come from MEMORY.md content, i.e. can be attacker-influenced) — with no approval gate;
  • can stall a turn up to the git timeout (GIT_TIMEOUT_MS, ~60s) per uncached entry.

Fix: resolveInclude now takes an opts.allowClone flag (default true, so include_add / include_refresh keep fetching as before). include_list passes allowClone: false, so an uncached git include is reported resolves: false with "git include not cached; run memory.include_refresh …" instead of being fetched. Local-path includes and already-cached git includes still resolve normally.

Fixes #473 — predictable, world-readable temp include-cache path

With no userRoot configured, includeCacheRoot fell back to a single fixed path $TMPDIR/jarvis-include-cache. That path is:

  • predictable — another local user could pre-create ("squat") it, or read what we clone into it;
  • world-readable — private include repos cloned there are exposed to any local user.

Fix: the temp fallback is namespaced by uid ($TMPDIR/jarvis-include-cache-<uid>), and the cache dir is created with mode 0o700 in resolveInclude, so cloned repos stay out of other users' reach. The userRoot-present path (<userRoot>/.jarvis/include-cache) is unchanged.

Tests

packages/tools/src/memory-tools.test.ts (3 new, network-free):

  • resolveInclude with allowClone: false throws not cached for a git include and leaves the cache dir untouched;
  • include_list reports an uncached git include as resolves: false and never creates/populates the cache;
  • includeCacheRoot temp fallback is uid-namespaced (not the bare shared path); the userRoot path is unchanged.

pnpm --filter @jarvis/tools test → 270 pass / 0 fail; typecheck and eslint clean.

🤖 Generated with Claude Code


Generated by Claude Code

…th (#474, #473)

Two self-contained security/correctness bugs in the memory include-cache
plumbing (packages/tools/src/memory-tools.ts).

#474 — memory.include_list is declared read-only ("No mutations", category
"read", ungated) but its invoke() called resolveInclude(), which for an
uncached git+ directive performs a network `git clone` into the local cache.
Merely *listing* directives could therefore write to disk, egress to a
directive-named host, and stall a turn by the git timeout per uncached entry.
resolveInclude now takes an `allowClone` option (default true, preserving the
include_add/include_refresh behaviour); include_list passes `allowClone:false`
so an uncached git include is reported `resolves:false` ("git include not
cached; run memory.include_refresh …") instead of being fetched.

#473 — with no userRoot configured, includeCacheRoot fell back to a single
predictable, world-readable path ($TMPDIR/jarvis-include-cache) that any local
user could pre-create ("squat") or read, exposing private include repos. The
fallback is now namespaced by uid ($TMPDIR/jarvis-include-cache-<uid>) and the
cache dir is created 0o700 so cloned repos stay out of other users' reach.

Regression tests cover all three behaviours (no-clone list, uid-namespaced
fallback path, and the private userRoot path).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K2iufag2eVqbcsk9UMmDWT
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment