Scope and safety. This article analyses a fixed, publicly disclosed vulnerability using the vendor’s own published source references. It describes the defective control flow and the remediation. It intentionally omits ready-to-run exploit requests, traversal strings, and weaponised proof-of-concept material.
Executive summary
CVE-2026-86543 is a critical missing-authentication vulnerability in knowns, a Go-based context server that gives AI coding assistants structured access to a project’s tasks, docs, specs, and decisions. In versions before 0.30.0, three independent defaults compounded into a single critical outcome:
- the HTTP server bound every interface rather than loopback;
- the authentication middleware degraded to a pass-through whenever no password was configured — the state of every fresh installation; and
POST /api/tunnel/startperformed no authorisation check of its own, so any client that reached the server could ask it to publish itself on the public internet.
The weakness is classified as CWE-306: Missing Authentication for Critical Function. The vendor scores it CVSS 4.0 9.3 Critical (CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N) and CVSS 3.1 9.8 Critical (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H).
This CVE is the exposure multiplier for the rest of the knowns advisory set. On its own it grants an unauthenticated caller the full management surface. Combined with CVE-2026-86538 (arbitrary file read) and CVE-2026-86542 (arbitrary file write), it turns two path-traversal bugs into remotely reachable, credential-free primitives.
The issue is fixed in knowns 0.30.0 by commit 878a02cb7cc1.
Evidence labels used below:
- Verified means stated by the CVE record, the GitHub Security Advisory, or the project’s own 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-86543 | Verified |
| Advisory | GHSA-fc85-99vc-9c75 | Verified |
| Product | knowns (pkg:npm/knowns, Go implementation) |
Verified |
| Vendor | knowns-dev | Verified |
| Affected versions | All versions before 0.30.0 | Verified |
| Fixed version | 0.30.0 | Verified |
| Weakness | Missing authentication for critical function, CWE-306 | Verified |
| Entry point | The management HTTP API, reachable on all interfaces | Verified |
| Critical function | POST /api/tunnel/start |
Verified |
| Authentication posture | Middleware is a no-op when no password is set; fresh installs have none | Verified |
| CVSS 4.0 | 9.3 Critical | Verified |
| CVSS 3.1 | 9.8 Critical | Verified |
| Reporters | Tong Hoang Gia, Nguyen Huy Hoang | Verified |
| Fix commit | 878a02cb7cc14f0a592fdfda7a520af3cac500fb |
Verified |
Why this class matters for AI developer tooling
A context server for AI assistants is an unusually attractive target because of what it is designed to hold. It is deliberately given the project’s specifications, architectural decisions, task history, notes, and — through its MCP surface — read and write access to the source tree itself. It is a single process that concentrates exactly the material an attacker would otherwise have to assemble from many places.
Local developer tools have historically been allowed a relaxed security posture on the theory that “it only listens on my machine”. That assumption has been eroding for years, and it fails outright in the environments where AI coding tooling actually runs today:
- laptops on coffee-shop, coworking, conference, and hotel networks;
- shared office and campus VLANs with no client isolation;
- cloud development environments, dev containers, and Codespaces-style remote workspaces;
- CI runners and ephemeral build agents that keep a tool alive between steps;
- WSL and container bridges where “localhost” is a routable address to other tenants.
Inferred: on any of those networks, a service that binds 0.0.0.0 with authentication disabled is not a local tool. It is an unauthenticated network service that happens to run next to a developer’s source code.
Attack surface and trust boundary
1 | Any client on a reachable network |
Two trust boundaries fail here, and the second is the interesting one.
The first is the ordinary one: unauthenticated network client → privileged management API. That is a serious but familiar bug.
The second is reachable client → control of the service’s own network exposure. POST /api/tunnel/start is not a data endpoint. It is a topology endpoint. It takes a service whose blast radius was bounded by the local network and re-publishes it at a globally resolvable address. An attacker who reaches the service once, briefly, on a shared network can convert transient local access into durable internet exposure — and then keep using it after leaving the network.
Root-cause analysis
Defect 1 — the authentication middleware opts itself out
From internal/server/auth.go at v0.29.1:
1 | func (am *AuthManager) Middleware(next http.Handler) http.Handler { |
The guard is real, well written, and completely inert until somebody sets a password. Fresh installations have no password. The security control is therefore opt-in, and the default state of every new install is “off”.
This is a fail-open design. It is worth being precise about why it is tempting: without it, a first-run user is locked out of their own tool before they can configure it. The mistake is not the goal, it is the scope — the escape hatch was applied to the whole API surface and to every network interface, rather than being narrowed to loopback and to first-run setup.
Defect 2 — the listener binds every interface
From internal/cli/browser.go at v0.29.1:
1 | listener, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", port)) |
And in internal/server/server.go:
1 | addr := ":" + strconv.Itoa(s.port) |
Both bind the wildcard address. The CLI then went a step further and printed the LAN address on startup:
1 | if ip := getLocalIP(); ip != "" { |
Inferred: a convenience feature that advertises the reachable LAN URL also documents the exposure to anyone reading over a shoulder, in a screen share, or in a pasted terminal log.
Defect 3 — the tunnel endpoint has no authorisation of its own
From internal/server/routes/tunnel.go at v0.29.1:
1 | func SetupTunnelRoutes(r chi.Router, tunnelMgr TunnelManager, broadcaster Broadcaster) { |
The handler delegates entirely to the shared middleware for protection. Because that middleware is inert on a fresh install, the endpoint that changes the service’s exposure model inherits exactly zero checks. The handler even returns the resulting public URL in its response, so the caller does not have to guess where the service now lives.
The three defects compose
Individually, each is arguable. A local-only tool binding 0.0.0.0 is sloppy. Optional authentication is a product decision. A tunnel endpoint without its own check is a layering choice. Together they form a complete, credential-free chain from “same network” to “internet-published management API with full project access”.
Inferred: this is the ordinary shape of a critical finding in developer tooling. It is rarely one dramatic bug. It is three reasonable-looking defaults whose intersection nobody modelled.
Impact
Verified impact
- The management API was reachable without credentials from any host that could route to the listener.
- No privileges and no user interaction were required (
PR:N/UI:N). - Confidentiality, integrity, and availability impacts on the vulnerable system are all rated High.
- An unauthenticated caller could provision a public tunnel and republish the API at a publicly accessible address.
Deployment-dependent consequences
The following are inferred:
- disclosure of project specifications, architectural decisions, internal notes, and memory entries held by the server;
- read and write access to the project source tree through the exposed code and docs routes;
- persistence of exposure beyond the attacker’s presence on the local network, once a tunnel is running;
- chaining with CVE-2026-86538 to read files outside the project — credentials, SSH keys, cloud configuration — with no authentication;
- chaining with CVE-2026-86542 to write attacker-controlled files outside the imports directory, which in turn can plant a malicious
.knowns/config.jsonand reach code execution through CVE-2026-86540; - poisoning of the docs, specs, and memory that an AI assistant reads, which is a durable influence channel over code the developer will later accept.
That last point deserves emphasis. Compromising a context server is not only a data-theft event. The whole purpose of the product is that an AI assistant treats its contents as authoritative. Silent modification of a spec or an architectural decision is an attack on every future session.
Safe validation and detection
You can establish exposure without sending a single malicious request.
Inventory and configuration review
Locate every installed knowns instance — npm global installs, project dev-dependencies, dev containers, and CI images.
Record the version. Anything earlier than 0.30.0 is affected.
On each host, check what the process is actually bound to:
1
2
3# Any bind address that is not loopback is the exposed condition.
ss -ltnp | grep -i knowns
lsof -nP -iTCP -sTCP:LISTEN | grep -i knownsCheck whether a password is configured.
GET /api/auth/statusis a legitimate, non-destructive endpoint the product exposes for exactly this purpose.Check whether a tunnel is or has been running, and treat any unexplained tunnel as an incident rather than a misconfiguration.
Telemetry to review
- inbound connections to the knowns port from any address other than loopback;
POST /api/tunnel/startin access logs, especially from an unexpected source or at a time when no developer was present;cloudflared(or equivalent) child processes spawned by the knowns process;- outbound connections consistent with tunnel establishment;
- unexplained changes to files under
.knowns/, particularlyconfig.json, docs, and memory entries; - requests to
/api/templates/previewand/api/importswith unusual parameters.
Inferred: a knowns process with a live tunnel and no password is best treated as a compromise until proven otherwise, because the exposure window is unbounded and the API grants project read/write.
Remediation and hardening
Required remediation
Upgrade to knowns 0.30.0 or later. The fix in 878a02cb7cc1 closes all three defects rather than patching one of them:
The listener is loopback by default.
1 | addr := net.JoinHostPort("127.0.0.1", strconv.Itoa(s.port)) |
1 | listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port)) |
A non-loopback listener is refused unless a password exists. This is the important structural change — the invariant is enforced at the point of serving, not at each call site:
1 | func (s *Server) serve(listener net.Listener) error { |
The tunnel endpoint gained its own check rather than trusting shared middleware:
1 | r.Post("/start", func(w http.ResponseWriter, r *http.Request) { |
Password removal is blocked while a tunnel is live, closing the obvious downgrade path of “authenticate, remove password, keep the tunnel”:
1 | if ar.canRemovePassword != nil && !ar.canRemovePassword() { |
The CLI also now refuses --tunnel without --password, drops the LAN-URL advertisement, and the router adds a trustedOriginMiddleware so cross-site requests are rejected before CORS can answer a preflight.
Defense in depth
- Set a password on every instance, even after upgrading, and especially on shared or remote development hosts.
- Do not expose developer context servers to any network you do not control; prefer SSH port forwarding over binding a routable interface.
- Treat “start a public tunnel” as an administrative action requiring explicit, interactive, local confirmation — not an API call.
- Apply host firewall rules to the knowns port rather than relying on the application’s bind address alone.
- In dev containers and cloud workspaces, verify what the platform publishes; some automatically forward listening ports.
- Rotate any credential that was reachable from the project directory if you cannot rule out exposure.
- Review
.knowns/contents for unexpected modification, since poisoned context outlives the network exposure that created it.
Upgrading is the fix. Firewalling is a compensating control — useful, but it does not help against a peer on the same VLAN, and it does nothing once a tunnel has been established from the inside.
Timeline
| Date | Event | Source certainty |
|---|---|---|
| 2026-08-15 | Fix commit 878a02cb7cc1 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-86543 reserved and published | CVE record metadata |
| 2026-09-08 | This analysis published | This site |
Consult the live CVE, GHSA, and VulnCheck records during remediation; vulnerability entries are routinely enriched and revised after publication.
Lessons for builders
“Local tool” is a deployment claim, not a property
If a process calls net.Listen on a wildcard address, it is a network service. The correct default is 127.0.0.1, and any widening should be an explicit, argued decision that carries an authentication requirement with it.
Fail-open authentication is not authentication
A middleware that returns early when unconfigured provides confidence without protection. If setup genuinely needs an unauthenticated window, scope it to loopback and to the specific setup endpoints — never to the entire API on every interface. The 0.30.0 fix is instructive precisely because it inverts the default: the server now refuses to serve in the unsafe combination rather than serving with the check disabled.
Endpoints that change exposure deserve their own gate
Middleware is a layer, not a guarantee. Any handler that alters the system’s own reachability — tunnels, port bindings, listener configuration, remote-access toggles — should re-check authorisation locally. That handler’s failure mode is categorically worse than a data endpoint’s, so it should not share a single point of failure with them.
Model the intersection of defaults, not each default alone
Wildcard bind, optional auth, and an unauthenticated topology endpoint each survived review. The combination is a 9.3. Threat modelling that examines settings independently will keep missing this class. Ask instead: what is the worst reachable state a user can arrive at without making a single wrong decision?
Context servers are high-value targets
A tool that holds a project’s specifications and feeds them to an AI assistant is both a data store and an influence channel. Design it with the posture of a secrets store, not the posture of a linter.
Related advisories in this set
Eight CVEs were assigned to knowns before 0.30.0 and fixed in the same release:
- CVE-2026-86538 — unauthenticated arbitrary file read via
templateFile - CVE-2026-86542 — unauthenticated arbitrary file write via import name
- 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-86540 — arbitrary code execution via the LSP binary override
- CVE-2026-86775 — unauthenticated path traversal through the HTTP Document API
References
- CVE.org record: CVE-2026-86543
- NIST National Vulnerability Database: CVE-2026-86543
- GitHub Security Advisory: GHSA-fc85-99vc-9c75
- VulnCheck advisory
AuthManager.Middlewareat v0.29.1- Browser listener binds
0.0.0.0at v0.29.1 POST /api/tunnel/startat v0.29.1- Fix commit
878a02cb7cc1 - knowns v0.30.0 release notes
Reported by Tong Hoang Gia and Nguyen Huy Hoang. Public vulnerability records change as maintainers and databases add references or revise metadata; recheck the primary sources above during remediation and incident response.