Scope and safety. This article analyses a fixed, publicly disclosed vulnerability using the vendor’s own published source references. It describes the defective data flow and the remediation. It does not include working traversal strings, target path lists, or a runnable proof of concept.
Executive summary
CVE-2026-86542 is a path-traversal vulnerability in the import routes of knowns before 0.30.0. The import feature clones or registers an external source and stores its .knowns/docs and .knowns/templates content under .knowns/imports/{name}/. That {name} came straight from the request body — or was derived from the source URL — and was never validated as a single path segment before being joined onto the imports directory.
The result is an arbitrary file write: the destination escapes the imports directory, and for a git source the content is whatever the attacker put in the repository they asked knowns to clone. Attacker-chosen path, attacker-chosen content.
The weakness is CWE-22: Improper Limitation of a Pathname to a Restricted Directory. The vendor scores it CVSS 4.0 8.8 High (CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:H/SC:N/SI:N/SA:N) and CVSS 3.1 9.1 Critical (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H) — the highest CVSS 3.1 score in this advisory set apart from the exposure issue itself.
PR:N again reflects CVE-2026-86543: the management API was unauthenticated on all interfaces by default.
Fixed in knowns 0.30.0 by commit d3989829fb50.
Evidence labels used below:
- Verified means stated by the CVE record, the GitHub Security Advisory, or the project’s published source and release material.
- Inferred means a deployment consequence derived from those facts and clearly marked as such.
Key facts
| Item | Finding | Confidence |
|---|---|---|
| Identifier | CVE-2026-86542 | Verified |
| Advisory | GHSA-wh3c-v55g-qfg8 | Verified |
| Product | knowns (pkg:npm/knowns, Go implementation) |
Verified |
| Affected versions | All versions before 0.30.0 | Verified |
| Fixed version | 0.30.0 | Verified |
| Weakness | Path traversal, CWE-22 | Verified |
| Entry point | name parameter on the import routes |
Verified |
| Dangerous sink | os.MkdirAll and copyFile under an unvalidated destination |
Verified |
| Authentication required | None, by default | Verified |
| Impact | Overwrite of arbitrary files writable by the server process | Verified |
| CVSS 4.0 | 8.8 High | Verified |
| CVSS 3.1 | 9.1 Critical | Verified |
| Reporters | Tong Hoang Gia, Nguyen Huy Hoang | Verified |
| Fix commit | d3989829fb5095666d23d005b2f78a082832a396 |
Verified |
Why the write side is categorically worse
An arbitrary read is a confidentiality problem with a bounded, if severe, outcome: the attacker learns things. An arbitrary write on a developer workstation is an execution problem, because a developer’s home directory is full of files that something will later read and act on.
Inferred: the general shape of the escalation is that a write primitive plus any auto-executed configuration file equals code execution at the next trigger. Shell startup files, editor and tool configuration, git hooks, and per-project settings all get read by something that will do what they say. The attacker does not need memory corruption or a shell — they need patience and one file.
That is why this CVE matters more than its CVSS 4.0 score suggests. It is the pivot in the chain:
1 | CVE-2026-86543 CVE-2026-86542 CVE-2026-86540 |
Writing a crafted .knowns/config.json into a project directory is enough to arm CVE-2026-86540, which executes an unvalidated binary name from that file when the project is opened.
Root-cause analysis
The name is taken and used, never checked
From internal/server/routes/imports.go at v0.29.1:
1 | // add registers a new import source. |
req.Source is checked for emptiness. req.Name is checked for emptiness only as a trigger for the fallback. Neither branch establishes that name is a single path segment before it reaches filepath.Join.
The non-git branch already gives directory creation at an arbitrary location. The git branch gives the full primitive.
The clone path multiplies the problem
From the same file, in gitCloneImport:
1 | // Collect files from docs/ and templates/ subdirectories. |
Three properties of this loop are worth drawing out:
destBaseinherits the unvalidated name. Every destination in the walk is relative to a base that may already sit outside the imports directory.- The name is interpolated a second time in
relPath = "docs/" + name + "/" + subRel, so it influences the path twice — once through the base and once through the relative portion. os.MkdirAllcreates whatever directories the destination needs. The write is not limited to overwriting files that already exist; it can construct the target directory tree first.
Combined with a source repository the attacker controls, the loop copies attacker-authored file content to attacker-chosen destinations. The only structural constraints are that the content lives under .knowns/docs or .knowns/templates in the source repo, and that the target must be writable by the knowns process.
The dryRun flag is not a security control
dryRun suppresses the copy, and is presumably why the feature felt safe to expose — a caller can preview changes. But it is a caller-supplied boolean. It reduces the impact of an accidental import; it does nothing about a deliberate one.
The other handlers shared the flaw
The fix commit adds validation to get, remove, and gitCloneImport as well as add. Inferred: the DELETE /api/imports/{name} route joining an unvalidated URL parameter onto the imports directory before calling os.RemoveAll is the availability half of the VA:H rating — a traversal there removes a directory tree outside the import root.
Impact
Verified impact
- Unauthenticated attackers could write files outside the imports directory.
- Arbitrary files writable by the server process could be overwritten.
- Integrity and availability impacts are rated High; confidentiality is not directly affected (
VC:N).
Deployment-dependent consequences
The following are inferred:
- overwriting shell startup files, tool configuration, or git hooks to obtain code execution the next time the developer opens a terminal or runs a repository operation;
- planting a malicious
.knowns/config.jsonto chain into CVE-2026-86540; - corrupting or replacing source files, build scripts, and CI configuration in any repository the process can write to — a local supply-chain attack that reaches production through the developer’s own commits;
- poisoning the docs, specs, and memory entries that an AI assistant treats as authoritative, which then influences code the developer reviews and accepts;
- destructive impact through the delete path, including loss of project data;
- persistence that survives restarting knowns, because the payload lives in files outside the tool.
Inferred: the AI-specific consequence deserves separate weight. A poisoned architectural decision record is not a defaced file; it is an instruction that a coding assistant will follow, in a document a human is unlikely to re-read. That is a quiet, durable influence channel over future code.
Safe validation and detection
Inventory and configuration review
- Identify knowns installations and versions; anything before 0.30.0 is affected.
- Establish whether the API was reachable beyond loopback and for how long — see CVE-2026-86543.
- List the configured imports and compare them to what the team actually intended to import. An import whose name is not a plain identifier is a finding.
- Review
.knowns/imports/for unexpected entries, and the directory above it for unexpected siblings.
Telemetry and integrity review
POST /api/importsandDELETE /api/imports/{name}requests where the name is not a simple single segment;- imports whose source is a repository nobody on the team recognises;
- file modification times outside the project that cluster around an import request;
- unexpected changes to shell startup files, git hooks, editor configuration, and any
.knowns/config.json; - for repositories under version control,
git statusandgit fsckwill surface unexpected working-tree changes, though files outside a repository will not be covered; - host filesystem audit events showing the knowns process writing outside its data directory.
Inferred: because the payload is a file rather than a process, detection should focus on integrity monitoring rather than on runtime behaviour. The malicious write may execute days later, or never — but it is still there.
Remediation and hardening
Required remediation
Upgrade to knowns 0.30.0 or later. The fix adds an explicit, reusable validator and applies it at every entry point:
1 | func validateImportName(name string) error { |
This is a good validator, and it is worth reading closely because each clause closes a distinct bypass:
name == "." || name == ".."— the degenerate relative names;filepath.IsAbs(name)— absolute paths that would ignore the base entirely;filepath.VolumeName(name) != ""— Windows drive and UNC prefixes;strings.ContainsAny(name, "/\\")— both separators, so a Windows-style separator cannot slip past a Unix-oriented check;strings.ContainsRune(name, '\x00')— NUL truncation against the syscall layer;filepath.Base(name) != name— the catch-all invariant: if the name is not identical to its own basename, it is not a single segment.
That final clause is the one to steal. It expresses the actual requirement — “this is a name, not a path” — rather than enumerating forbidden characters, so it stays correct as platform quirks change.
The validator is called in add, get, remove, and at the top of gitCloneImport, so the check cannot be bypassed by reaching the clone through a different route:
1 | func (ir *ImportRoutes) gitCloneImport(source, name, ref, cachedHash string, dryRun bool) ([]importChange, []string, string, bool, error) { |
Note that the fallback nameFromSource(req.Source) result is validated too, because the check sits after the fallback assignment. Derived values are untrusted values.
Defense in depth
- Set a password and keep the listener on loopback (both are default in 0.30.0).
- Treat “import from a URL” as a privileged operation and require authentication and explicit user confirmation for it.
- Run developer tooling with a filesystem view that does not include the home directory’s dotfiles where practical.
- Enable file integrity monitoring for shell startup files, git hooks, and tool configuration on developer machines.
- If an affected version was exposed, audit for unexpected writes rather than assuming absence of an alert means absence of a write.
Timeline
| Date | Event | Source certainty |
|---|---|---|
| 2026-08-15 | Fix commit d3989829fb50 authored in the knowns repository |
Repository metadata |
| 2026-08-16 | Public disclosure date recorded on the CVE record | CVE record datePublic |
| 2026-09-07 | CVE-2026-86542 reserved and published | CVE record metadata |
| 2026-09-08 | This analysis published | This site |
Lessons for builders
A name is not a path
The single most useful invariant in this patch is filepath.Base(name) != name. If a value is documented as an identifier, enforce that it is one at the boundary. Everything downstream then gets to assume it.
Validate derived values, not just supplied ones
name could come from the request or from nameFromSource(req.Source). Only validating the request field would have left the derived path open. Put the check after the value is finalised, immediately before it is used.
Validate at every entry to the dangerous function
gitCloneImport is reachable from more than one route. The fix validates inside it as well as in the handlers. Guarding call sites is a maintenance burden that eventually fails; guarding the function is durable.
Write primitives are execution primitives on developer machines
Threat-model a file write as “the attacker will pick a file that something else executes”. On a workstation, that set is large and mostly undocumented. Score accordingly, and prioritise write traversals above read traversals when triaging.
dryRun is a UX feature
Any flag the caller controls is part of the attack surface, not part of the defence. If a preview mode exists, ensure the security properties hold identically in both modes.
Related advisories in this set
- CVE-2026-86543 — unauthenticated management API exposure
- CVE-2026-86538 — the read-side counterpart, via
templateFile - CVE-2026-86540 — arbitrary code execution via the LSP binary override
- CVE-2026-86439 — path traversal through MCP doc and memory tools
- CVE-2026-86541 — path traversal in the
code.replaceMCP action - CVE-2026-86544 — authorisation bypass via misclassified code actions
- CVE-2026-86775 — unauthenticated path traversal through the HTTP Document API
References
- CVE.org record: CVE-2026-86542
- NIST National Vulnerability Database: CVE-2026-86542
- GitHub Security Advisory: GHSA-wh3c-v55g-qfg8
- VulnCheck advisory
- Import
addhandler at v0.29.1 gitCloneImportcopy loop at v0.29.1- Fix commit
d3989829fb50 - knowns v0.30.0 release notes
Reported by Tong Hoang Gia and Nguyen Huy Hoang. Recheck the primary sources above during remediation; public vulnerability records are frequently revised after publication.