AEV-2026-0057
Knowns' MCP docs and memory tools let a path or ID escape the project root (GHSA-9gfj-28hw-jchp)
Before commit 09c5a96, no containment function existed anywhere in Knowns' storage layer: the docs.* MCP tools resolved a caller supplied path with plain string trimming and an unchecked filepath.Join, and the memory.* tools concatenated a caller supplied ID directly into a filename, letting either reach a .md file anywhere the server process could access. This entry independently cloned knowns-dev/knowns and confirmed the vulnerable and fixed source directly, and separately confirmed the advisory's second finding, docs.update's newPath argument triggering a file deletion while classified only as a write, is not addressed by the same fix.
Affected
- Organisation
- knowns-dev
- Product
- Knowns
- Component
- The docs and memory storage layer reachable through Knowns' MCP tools: internal/storage/doc_store.go (Get, Create, Update, Rename, Delete), internal/storage/memory_store.go (Get, GetInLayer, Create, Update, Delete, moveLayer) and internal/models/memory.go (MemoryFileName), plus, for the second, unaddressed finding, internal/permissions/registry.go's own docs.update classification and internal/mcp/handlers/doc.go's newPath handling
- Versions
- This entry independently confirmed, by reading the source directly at the exact tag GHSA-9gfj-28hw-jchp states as the last affected release, v0.29.1, that every doc storage function built its local path with unchecked string trimming and filepath.Join, and every memory storage function concatenated an unvalidated caller supplied ID into a filename. Confirmed the fix, commit 09c5a96, is an ancestor of release tag v0.30.0 and not an ancestor of v0.29.1, and that v0.30.0 carries a commit date of 16 August 2026, the same day the advisory was published.
- Configurations
- Any Knowns MCP server instance exposing the docs.* or memory.* tools to a client able to supply a path or ID argument; For the second, distinct finding: any Knowns deployment relying on a CapWrite-scoped permission preset that deliberately withholds CapDelete to prevent an MCP client from deleting documents
Execution authority facts
Fields the evidence does not establish are shown as unknown rather than guessed.
- Intended task
- Read, create, update, rename or delete a document, or read, create, update or delete a memory entry, identified by a caller supplied relative path or ID, scoped to the current project's own .knowns/docs or memory directory, as the docs.* and memory.* MCP tools' own declared contract
- Agent
- The Knowns MCP server process executing a docs.* or memory.* tool call received from its connected MCP client
- Delegating actor
- unknown
- Action
- A local file path built, before the fix, by trimming a leading slash and a trailing .md extension from a caller supplied path and joining the remainder with an unchecked filepath.Join, or by concatenating a caller supplied ID directly into a filename with no format check, confirmed directly by this entry's own reading of the source at the affected tag, v0.29.1
- Target resource
- A .md file at an arbitrary filesystem location reachable by the Knowns server process, outside the project's own .knowns/docs or memory directory
- Environment
- unknown
- Credentials used
- unknown
- Privileges available
- Not established as broader than the account running the Knowns MCP server process; this entry found nothing in the advisory or the source indicating privilege elevation beyond that process's own filesystem access, while the mechanism itself reaches beyond whatever the project's own task scoped directory was meant to bound
- Authority presented
- Permission to call a docs.* or memory.* MCP tool scoped, by the tool's own declared contract, to the current project's own docs or memory directory
- Authority required
- Authority over the specific filesystem path the storage layer's own join or concatenation actually resolved against, which this entry's own reading of the source confirms was never checked against the project's own root before the fix
- Applicable policy
- unknown
- Approval mechanism
- unknown
- Required approver
- unknown
- Independent approval
- unknown
- Action binding
- unknown
- Sequence context
- unknown
Impact
- Consequence
- Arbitrary out of bounds file read, creation, overwrite and deletion of .md files reachable by the Knowns MCP server process, per GHSA-9gfj-28hw-jchp's own description, plus a distinct capability classification gap this entry independently confirmed: docs.update's newPath argument triggers a real file deletion while the tool remains registered under CapWrite alone through the latest commit this entry could read
- Reach
- single
- Reversibility
- unknown
- Detectability
- unknown
- Propagation
- A concrete propagation path exists, an overwritten or deleted file the server process can reach outside the intended project sandbox, but this entry did not observe the effect spreading to a further system
- Recovery
- Path traversal into docs, memory and template I/O fixed in v0.30.0 by commit 09c5a96, confirmed directly by this entry's own reading of the affected and fixed source and by git tag ancestry. The docs.update CapWrite versus CapDelete classification gap the same advisory describes is not touched by that commit, confirmed directly by this entry's own reading of internal/permissions/registry.go unchanged through the latest commit this entry's own clone could reach, c6ed4cb8548c266930910d3b24b7605fa2c21150, dated 5 September 2026 and tagged v0.33.0; this entry found no separate fix for it.
Evidence
Primary sources
- Unrestricted Path Traversal leading to out-of-bounds arbitrary .md file read, write, and deletion in MCP Docs + Memory Tools (GHSA-9gfj-28hw-jchp) (knowns-dev/knowns (GitHub Security Advisory))
- Commit 09c5a96: fix(security): contain filesystem paths (knowns-dev/knowns (GitHub, commit))
Supporting sources
- internal/storage/doc_store.go and internal/storage/memory_store.go at tag v0.29.1 (knowns-dev/knowns (GitHub, source))
- internal/permissions/registry.go at v0.29.1, v0.30.0 and the latest reachable commit (knowns-dev/knowns (GitHub, source and commit history))
- Reproduction status
- Independently confirmed by Moona Intelligence cloning knowns-dev/knowns directly and reading the real, affected and fixed source, rather than by issuing a live MCP tool call against a running server. This entry read doc_store.go and memory_store.go at the affected tag, v0.29.1, confirmed the unchecked joins and concatenation, then read the identical files after the fix and confirmed the fix commit replaces them with the new safepath package and a memory ID format check. Independently reproduced the memory ID join's own traversal arithmetic with Go's own filepath.Join against a representative traversal ID. Separately read internal/permissions/registry.go at three points in the repository's history and confirmed docs.update's own capability classification is unchanged.
- Evidence state
- Confirmed
Known unknowns
- Whether this has been exploited against a real Knowns deployment.
- Whether CVE-2026-86439, which third party trackers associate with this mechanism through web search, is confirmed against a fetched primary CVE record, since nvd.nist.gov, vuldb.com and radar.offseq.com were each blocked to direct fetch in this session on every attempt.
- What filesystem privileges the account typically running a Knowns MCP server holds in a given deployment.
- Whether a commit after 5 September 2026, outside the history this entry's own clone could reach, has since closed the docs.update capability classification gap.
Limitations
- This session's direct fetch of nvd.nist.gov, services.nvd.nist.gov, vuldb.com and radar.offseq.com was blocked by this session's network egress policy on every attempt; the CVE-2026-86439 identifier some third party trackers associate with this mechanism is stated as search corroborated rather than independently confirmed against a fetched primary CVE record.
- This entry verifies the mechanism and the fix by reading and comparing the affected and fixed source directly rather than by issuing a live MCP tool call against a running server, since the exact vulnerable joins, the exact fix, and the exact unchanged permission registry entry already establish the outcome.
Claim provenance
- verified
GitHub's Security Advisory Database, fetched directly in this session including its structured JSON record, documents GHSA-9gfj-28hw-jchp, published 2026-08-16T10:52:25Z, titled Unrestricted Path Traversal leading to out-of-bounds arbitrary .md file read, write, and deletion in MCP Docs + Memory Tools, no CVE ID populated in the structured record, CVSS 3.1 base score 8.8, High, vector CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, CWE-22, CWE-306 and CWE-863, against npm package knowns, versions through 0.29.1, patched in 0.30.0, crediting reporters uziii2208 and hoanggxyuuki. Confirmed the description states storage layer functions concatenate user controlled paths with filepath.Join with no containment validation, and that docs.update's newPath parameter performs a deletion classified as CapWrite rather than CapDelete.
The Path Argument Named a Document. The Join That Resolved It Named Nothing At All. - verified
This session cloned knowns-dev/knowns directly, checked out tag v0.29.1, the exact release GHSA-9gfj-28hw-jchp states as the last affected version, and confirmed directly that DocStore.Get, Create, Update, Rename and Delete each build a local file path with strings.TrimPrefix and strings.TrimSuffix, neither of which removes a parent traversal segment, joined with an unchecked filepath.Join. Confirmed MemoryStore.Get, GetInLayer, Create, Update, Delete and moveLayer each resolve a caller supplied ID into models.MemoryFileName(id), the literal concatenation memory-{id}.md, joined onto the memory directory with no format restriction on id before the fix. Independently reproduced, using Go's own filepath.Join against a representative traversal ID, that the resulting join resolves outside the intended memory directory.
The Path Argument Named a Document. The Join That Resolved It Named Nothing At All. - verified
This session read this commit's complete diff directly and confirmed it adds internal/safepath, a new package implementing Resolve, ResolveProject and ResolveProjectReal with parent traversal rejection, Windows volume and alternate data stream rejection, and post resolution symlink escape detection through filepath.EvalSymlinks, rewires doc_store.go, template_store.go and the template preview route to call it, and adds ValidateMemoryID to memory.go, called at every memory_store.go entry point. Confirmed, via git merge-base --is-ancestor, that this commit is an ancestor of release tag v0.30.0 and is not an ancestor of v0.29.1. Confirmed the commit does not modify internal/permissions/registry.go.
The Path Argument Named a Document. The Join That Resolved It Named Nothing At All. - verified
This session read internal/permissions/registry.go directly at three points in its history: tag v0.29.1, tag v0.30.0, and the latest commit this session's clone could reach, c6ed4cb8548c266930910d3b24b7605fa2c21150, dated 2026-09-05 and tagged v0.33.0. Confirmed docs.update is declared {Capability: CapWrite, Target: TargetDoc, Risk: RiskMedium} at all three points, with no separate entry or override for a newPath argument. Confirmed handleDocUpdate's newPath argument reaches DocStore.Rename through MutateDocWithHistory, and that Rename calls os.Remove on the document's own prior file path. Confirmed a separate commit on 2026-08-16 adds a docs.hard_delete registry entry under CapDelete without altering docs.update's own classification.
The Path Argument Named a Document. The Join That Resolved It Named Nothing At All.
