On 2026-09-23 an external contributor (docxology) audited the repository and filed twenty issues, then three pull requests that were merged on 2026-09-25. Until 2026-10-01 nothing in the vault mentioned them. All twenty are still open on GitHub (checked 2026-10-01). The trust model they are measured against is in Security; the refactor that addresses the structural causes is Milestone 18 - Library Refactor.
| # | kind | in short | overlaps | fixed so far | addressed by |
|---|---|---|---|---|---|
| 1 | security | the server resolves a named secret and sends it as Bearer to a base_url the client chose (key exfiltration, SSRF); the claude-code binary path is client-chosen too | refactor, Milestone 18 phase 4.5: secrets go only to built-in or operator-listed endpoints (MOONKALE_LLM_ENDPOINTS); the client’s claude-code path is ignored | — | |
| 2 | security | a “read-only” DuckDB source can still read any local file (read_csv, read_parquet, read_text, glob) and autoload extensions | — | fixed on dev-claude (2026-10-04): file mode read-only + enable_external_access=false; folder mode allowed_directories = the folder; no extension autoload; configuration locked | — |
| 3 | security | the websocket Origin check strips the port and trusts the host name: another port on the same host, or DNS rebinding, reaches the terminal | — | — | api auth module (phase 4) |
| 4 | security | extension permissions are checked with what the client sends on /api/ext/run; ids can be squatted; workspace settings grant permissions; no wasm budgets | refactor, phase 4.5 (structural part): grants from the user scope only; an id belongs to the module file that loaded it first (no squatting). Open: fuel/epoch and memory limits | fuel/epoch limits | |
| 5 | security | the folder source follows symlinks out of the root; on Windows \ and drive letters escape it | — | — | one path-jail function in project-fs |
| 6 | bug, high | CRLF + non-ASCII text panics the search chunker | — | PR #22 (the chunker); dev-claude (2026-10-04): the index locks survive a panic | — |
| 7 | security | rate limits keyed on a spoofable X-Forwarded-For; no limiter on /api; cookie Secure from a client header; an empty MCP token accepted | — | — | api auth module (phase 4) |
| 8 | security | a cloned repository’s .moonkale/settings.json can define an agent whose command runs, and enable + grant a folder wasm extension | refactor, phase 4.5: SettingsFile::without_authority — a folder may not set a provider, agent, shell, SSH host, grant, auto-approval or embeddings; may deny tools | — | |
| 9 | security | the read-only SQL gate: WITH-wrapped writes, PRAGMA, trailing statements, no Cypher gate, allow_writes auto-approves | — | PR #21 (CTE writes, PRAGMA names); dev-claude (2026-10-04): one statement per query, the Cypher gate on LadybugDB, allow_writes never approves destructive calls, shell commands read as words | — |
| 10 | security | javascript:/data: links are clickable in the rich editor on the web; no Content-Security-Policy | — | — | link scheme allow-list; CSP header |
| 11 | bug, high | the flow editor replaces an unparseable .flow.json with an empty flow and destroys it on the next edit | — | fixed on dev-claude (2026-10-04): a broken file shows its parse error and is never written; Reload | — |
| 12 | bug, high | graph renderer panics on a stale drag index (view dead until reload); the terminal pump is not restartable after a remount | — | fixed on dev-claude (2026-10-04): the drag follows its node by id; terminal output pumped per session (xterm replays recent output to a remounted view) | — |
| 13 | bug | agent turns wedge: terminal.run without a timeout, unreachable approvals, a dead LSP never restarts | P-069 | — | — |
| 14 | robustness | unbounded buffers: PTY output to slow clients, provider streams, provider cache, fetch without a size cap | — | — | — |
| 15 | bug, data loss | project-fs races: create/rename TOCTOU, stale NodeIds after a directory rename, a shared temp name, trash overwrite | — | fixed on dev-claude (2026-10-04): create_new, no-replace rename (renameat2 on Linux/Android), ids under a renamed/deleted path forgotten, unique temp files, trash never overwritten and pruned after 30 days. Open: the low-severity notes at the end of the issue | — |
| 16 | bug | Python and Julia symbol extraction never runs: walk::wants_text admits only markdown and Rust | contradicts spec 022 “done” | fixed on refactor (Milestone 18 phase 1: one language list for the walk and the dispatch, a test that indexes .jl/.py) | — |
| 17 | bug | entity log: slice panic on non-ASCII commit ids, O(n²) merge, dropped events, text_at after compaction | P-086 | PR #23 (the slice); the rest open | the internal store (Internal State) |
| 18 | security | SSH remote: control socket in a shared /tmp, remote port hijack, token-echo fallback, a fixed upload temp name | — | — | — |
| 19 | bug | IME Enter sends, UTF-16 column mismatch, focus traps, image decompression bomb, full-document LSP sync, an unpinned CI download | the rest of P-037 (LSP didChange) | — | — |
| 20 | security, low | secret-existence oracle, presence impersonation, transcripts in the git tree, JSON injection in websocket frames, sessions not bound to a folder | — | — | — |
What the issues have in common
Most of the security findings are one mistake repeated: the server trusts a decision the client made (#1 endpoint, #4 permissions, #8 settings from a folder, #20 presence names). The reason is structural — ext-api::Workspace and the settings types are shared between client and server, and the server functions accept whatever shape the client sends. The refactor’s server contribution point (phase 4 of Milestone 18 - Library Refactor) makes the server resolve providers, permissions and grants from its own state.
The second group is unbounded or unvalidated input (#2, #5, #9, #10, #14, #15). Those are local fixes and do not need to wait for the refactor.
Fixes of 2026-10-04 (dev-claude)
#2, #6, #9, #11, #12 and #15, one commit each, with tests (unit tests for the gates, the relay and the file races; browser steps for #11 and #12). Each commit says Fixes #n, so the issues close when dev-claude is merged into master. Found on the way:
- remounting a panel (docking it elsewhere, or the phone layout) replayed the last command: a terminal panel started the last New Terminal again, the code editor ran the last Undo/Save; handlers now remember the last command they handled;
- a hidden copy of the native terminal measured 0 × 0 and shrank the shared screen;
- the web terminal’s websocket pumps were spawned in the component that connected it, so a remount closed the socket and the server ended the shell;
ext-host’s wordcount test looked for the wasm under<root>/targetwhile building it intoCARGO_TARGET_DIR.
Still open from the list: #1 and #4 (structural parts done in Milestone 18; re-check and close or narrow), #3, #5, #7, #8 (done in Milestone 18 phase 4.5 — re-check and close), #10, #13, #14, #16 (fixed; close), #17, #18, #19, #20, and the low-severity notes of #15.
Housekeeping
- #16 is fixed (Milestone 18 phase 1) but still open; #6 closes with the
dev-claudemerge; #17 is partly fixed. - Release notes should say that these are open until they are closed: Remote and Server Modes recommends exposing a server only on a trusted network, and that stands.