Skip to content

fix(security): restrict tunnels.json file permissions#1168

Merged
igorls merged 1 commit intoMemPalace:developfrom
arnoldwender:fix/security-tunnels-permissions
Apr 25, 2026
Merged

fix(security): restrict tunnels.json file permissions#1168
igorls merged 1 commit intoMemPalace:developfrom
arnoldwender:fix/security-tunnels-permissions

Conversation

@arnoldwender
Copy link
Copy Markdown
Contributor

What and Why

~/.mempalace/tunnels.json (introduced in #790) was created via plain open(..., "w") with no follow-up chmod, and its parent directory via os.makedirs() without mode=0o700. On Linux with the default umask 022 both end up world-readable (0o644 / 0o755). Tunnel contents reveal cross-wing connections — which projects, people, and rooms the user has explicitly linked — so this is sensitive metadata that should not be readable by other local users on shared systems.

This is the same class of issue as the 6 locations already hardened in #814; tunnels.json was missed because it landed after the file-permissions audit. @Kesshite greenlit a scoped follow-up in the #809 thread on 2026-04-16 but the PR was never filed until now.

Root Cause

mempalace/palace_graph.py:310-327_save_tunnels() writes atomically but never restricts permissions on either the file or its parent directory.

Change Summary

  • mempalace/palace_graph.py — in _save_tunnels(), os.chmod(parent, 0o700) after os.makedirs and os.chmod(_TUNNEL_FILE, 0o600) after os.replace. Both calls are wrapped in try/except (OSError, NotImplementedError) for Windows / unsupported-filesystem compatibility, matching the established pattern from fix: restrict file permissions on sensitive palace data #814.
  • tests/test_palace_graph_tunnels.py — new test_save_tunnels_restricts_permissions, skipped on Windows because POSIX permission bits do not apply.

Test Plan

  • pytest tests/test_palace_graph_tunnels.py -v — 10/10 pass (1 new test green)
  • ruff check — clean
  • ruff format --check — formatted

Closes #1165

@igorls igorls added bug Something isn't working security Security related labels Apr 24, 2026
~/.mempalace/tunnels.json (introduced in MemPalace#790) was created via plain
open(..., "w") with no chmod, and its parent dir via os.makedirs()
without mode=0o700. On Linux with default umask 022 both end up
world-readable (0o644 / 0o755).

Tunnels reveal cross-wing connections — which projects, people, and
rooms the user has explicitly linked — so they are sensitive metadata
that should not be readable by other local users on shared systems.

Apply the same 0o700 / 0o600 pattern that MemPalace#814 established for the
other sensitive palace files. Chmod calls are wrapped in try/except
(OSError, NotImplementedError) for Windows / unsupported-filesystem
compatibility.

Closes MemPalace#1165
@arnoldwender arnoldwender force-pushed the fix/security-tunnels-permissions branch from c32e5c2 to 5fd09d3 Compare April 24, 2026 20:57
@igorls igorls merged commit 91a6026 into MemPalace:develop Apr 25, 2026
6 checks passed
jphein added a commit to jphein/mempalace that referenced this pull request Apr 25, 2026
…HNSW fixes

Bring in 29 commits from upstream/develop since the last merge (2026-04-23):

Major absorbed changes:
- MemPalace#976 (Felipe Truman): HNSW graph corruption fix, mine_global_lock for
  fan-out, MAX_PRECOMPACT_BLOCK_ATTEMPTS=2 for /compact deadlocks. Closes
  MemPalace#974/MemPalace#965/MemPalace#955; likely resolves MemPalace#1172 too.
- MemPalace#1179 (Igor): CLI mempalace search routes through _hybrid_rank,
  legacy-metric warning + _warn_if_legacy_metric, invariant tests on
  hnsw:space=cosine across all 5 collection-creation paths.
- MemPalace#1180/MemPalace#1183/MemPalace#1184: cross-wing topic tunnels, init mine UX, --auto-mine.
- MemPalace#1185 (perf/batched-upsert-gpu): batched ChromaDB upserts, GPU device
  detection via mempalace/embedding.py.
- MemPalace#1182: graceful Ctrl-C during mempalace mine.
- MemPalace#1168: tunnel permissions security fix.

Conflict resolutions (8 files):
- searcher.py: kept fork's CLI delegation through search_memories (cleaner
  single-source-of-truth path); upstream's inline-retrieval branch dropped.
  Merged upstream's print formatting (cosine= + bm25=) with fork's
  matched_via reporting from sqlite BM25 fallback.
- backends/chroma.py: kept fork's _BLOB_FIX_MARKER + palace_path arg to
  ChromaCollection (MemPalace#1171 write lock); merged upstream's **ef_kwargs
  (embedding_function support from MemPalace#1185). Removed duplicate
  _pin_hnsw_threads (fork already cherry-picked Felipe's earlier).
- mcp_server.py: kept fork's palace_path arg + upstream's clearer comment
  on hnsw:num_threads=1 rationale.
- miner.py: took upstream's serial mine() flow (mine_global_lock + Ctrl-C),
  RESTORED fork's strict detect_room — substring matching from upstream
  breaks fork-only test_detect_room_priority1_no_substring_match. Added
  `import re` for word-boundary regex matching. Fork-ahead concurrent
  mining (workers=, ThreadPoolExecutor from 5cd14bd) is dropped — daemon
  migration deprioritizes local concurrent mining; can re-port if needed.
- CHANGELOG.md: combined fork's segfault-trio narrative with upstream's
  v3.3.4 release notes.
- HOOKS_TUTORIAL.md: took MEMPALACE_PYTHON env var name (fork README was
  stale; hooks already use this name per fork-ahead MemPalace#19).
- test_convo_miner_unit.py: took both contextlib + pytest imports.
- test_searcher.py: imported upstream's 3 new TestSearchCLI tests but
  skipped them with TODOs — they assume upstream's inline-retrieval CLI
  with simpler mocks. Rewrite needed for fork's delegated search_memories
  path (sqlite BM25 fallback + scope counting).

Test result: 1334 passed, 3 skipped (the upstream tests above), 3 warnings.

Implications for open fork PRs:
- MemPalace#1171 (cross-process write lock at adapter) becomes structurally
  redundant given MemPalace#976's mine_global_lock + daemon-strict serialization.
  Slated for close with thank-you to Felipe.
- MemPalace#1173/MemPalace#1177 still relevant (quarantine threshold + blob_seq marker).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Scorpion1221 pushed a commit to Scorpion1221/mempalace that referenced this pull request May 2, 2026
Scorpion1221 pushed a commit to Scorpion1221/mempalace that referenced this pull request May 2, 2026
Sync with upstream/develop via cherry-pick of 9 critical bugfixes:
- HNSW index bloat prevention (MemPalace#1191)
- SIGSEGV guards on collection reopen (MemPalace#1262, MemPalace#1289)
- blob-seq marker fast-path (MemPalace#1177)
- palace_graph None-metadata guard (MemPalace#1201)
- palace_graph security tunnels (MemPalace#1168)
- hyphenated wing name normalization (MemPalace#1197)
- searcher _tokenize None guard (MemPalace#1198)
- mcp_server diary topic sanitize (MemPalace#936)

Tests: 1459 default + 113 benchmark + 6/7 stress all pass.
(random-kill stress test was failing on dev pre-merge; not regressed.)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working security Security related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(security): restrict tunnels.json file permissions (followup to #814)

2 participants