GHSA-MC52-MWQ4-VFX3

Vulnerability from github – Published: 2026-10-06 18:58 – Updated: 2026-10-06 18:58
VLAI
Summary
knowns OS Command Injection via Insecure LSP Binary Path Config in .knowns/config.json
Details

Overview

A critical Arbitrary Code Execution (ACE) vulnerability exists in the Knowns Language Server Protocol (LSP) detection and startup pipeline. The system blindly trusts the settings.lsp.languages.<lang>.binary field defined in the project-level .knowns/config.json file.

Because this field is never validated against an allowlist of managed binaries, and absolute paths are implicitly accepted, opening a malicious repository (or a legitimate repository where the config has been tampered with) results in the immediate execution of an attacker-controlled binary. The payload is executed twice per session: once during the initial runVersionCheck (health check), and again when the LSP server process is spawned via Server.Start(). When chained with the previously identified Config Overwrite vulnerabilities, this flaw yields a fully unauthenticated Remote Code Execution chain.

Affected paths

File Path Role Vulnerability & Execution Impact
internal/models/config.go Validation Gap Missing Binary Validation (CWE-829): ProjectSettings.Validate() enforces duration formats for task lifecycles but completely ignores the LSPLanguageSettings.Binary field. Absolute paths, shell interpreters, and untrusted executables are silently accepted.
internal/lsp/detect.go Execution Sink #1 Unsanitized Health Check Execution: Detector.resolve() passes the unvalidated override to exec.LookPath(), then invokes runVersionCheck() which blindly calls cmd.Run() on the attacker-controlled binary with CheckArgs.
internal/lsp/server.go Execution Sink #2 Unsanitized LSP Server Spawn: Server.Start() passes the resolved binary path to knownsprocess.Command() and spawns it as a long-running background process via cmd.Start(), executing the payload a second time.

Root Cause

Missing Validation in Configuration Schema

In internal/models/config.go, the ProjectSettings.Validate() function is responsible for sanitizing the project configuration loaded from .knowns/config.json. However, it only validates task lifecycle durations:

func (s ProjectSettings) Validate() error {
    settings := s.EffectiveTaskLifecycle()
    if _, err := ParseTaskLifecycleDuration(settings.ArchiveAfter); err != nil { ... }
    // NO VALIDATION FOR s.LSP.Languages[lang].Binary
    return nil
}

The LSPLanguageSettings struct exposes a Binary string field. When a malicious project is loaded, this string is passed unmodified into the LSP resolution pipeline.

Blind Execution in LSP Detector

In internal/lsp/detect.go, the Detector.resolve() function accepts an override string (from the config) and forces it into the execution pipeline:

func (d *Detector) resolve(ctx context.Context, root string, lang Language, override string) (ServerCommand, bool) {
    binaries := lang.Binaries
    if override != "" {
        binary := Binary{Name: override} // Attacker's malicious path injected here
        binaries = []Binary{binary}
    }
    for _, binary := range binaries {
        path, err := d.LookPath(binary.Name) // Accepts absolute paths (e.g., /tmp/evil.sh)
        // ...
        err = d.RunCheck(checkCtx, path, binary.CheckArgs...) // EXECUTION #1
        // ...
    }
}

d.RunCheck maps to runVersionCheck, which executes the binary via knownsprocess.CommandContext(ctx, path, args...).Run().

Unrestricted Process Spawn

In internal/lsp/server.go, when the LSP manager starts the server, it executes the same compromised path:

func (s *Server) Start(ctx context.Context) error {
    // ...
    cmd := knownsprocess.Command(s.Command.Path, s.Command.Args...) // EXECUTION #2
    cmd.Dir = s.Root
    // ...
    if err := cmd.Start(); err != nil { ... }
}

Attack Vector

Phase Request / Action Effect
1. Plant Attacker commits .knowns/config.json containing {"settings":{"lsp":{"languages":{"go":{"binary":"/tmp/evil.sh"}}}}} to a repository. Malicious config embedded in the project.
2. Trigger Victim (or AI Agent) clones the repo and opens it with knowns mcp or knowns browser. Auto-detection scans the project, finds a .go file, and loads the malicious config.
3. Execute (Health Check) Detector.resolve() calls runVersionCheck("/tmp/evil.sh", "version"). RCE Execution #1 occurs silently in the background.
4. Execute (LSP Spawn) Server.Start() calls cmd.Start() with the same binary. RCE Execution #2 occurs, spawning the malicious process as a long-running daemon.

Chained Attack Vector (Unauthenticated RCE): If combined with the Config Overwrite vulnerability (via code.replace path traversal), a remote attacker can overwrite .knowns/config.json in a target project, inject a payload, and trigger an LSP restart (via docs.update or server reload), achieving Remote Code Execution without any user interaction.

Analysis

This vulnerability is a textbook example of Inclusion of Functionality from Untrusted Control Sphere (CWE-829), commonly known in the IDE/Editor space as the "Malicious Workspace" vulnerability (similar to historical CVEs in VS Code where .vscode/settings.json could point to malicious interpreter paths).

The core failure is the lack of a strict allowlist for executable paths. By relying on exec.LookPath(), the code allows an attacker to bypass binary name resolution simply by providing an absolute path (/abs/path/evil.sh) or a path containing a slash (./evil.sh).

Furthermore, the execution happens automatically upon project load. In modern AI-driven development workflows, AI Agents automatically scan project files (like .go, .ts, .py) to provide context. The LSP detector triggers on file extensions, meaning the victim or agent does not even need to explicitly run a "build" or "start" command; the mere act of the Agent indexing the workspace triggers the payload.

Fix

Patch is available right now at New Release.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 0.29.1"
      },
      "package": {
        "ecosystem": "npm",
        "name": "knowns"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.30.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-86540"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-427",
      "CWE-78",
      "CWE-829",
      "CWE-94"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-10-06T18:58:38Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "## Overview\n\nA critical **Arbitrary Code Execution (ACE)** vulnerability exists in the Knowns Language Server Protocol (LSP) detection and startup pipeline. The system blindly trusts the `settings.lsp.languages.\u003clang\u003e.binary` field defined in the project-level `.knowns/config.json` file. \n\nBecause this field is **never validated** against an allowlist of managed binaries, and absolute paths are implicitly accepted, opening a malicious repository (or a legitimate repository where the config has been tampered with) results in the immediate execution of an attacker-controlled binary. The payload is executed **twice** per session: once during the initial `runVersionCheck` (health check), and again when the LSP server process is spawned via `Server.Start()`. When chained with the previously identified Config Overwrite vulnerabilities, this flaw yields a fully unauthenticated Remote Code Execution chain.\n\n## Affected paths\n\n| File Path | Role | Vulnerability \u0026 Execution Impact |\n| :--- | :--- | :--- |\n| **`internal/models/config.go`** | Validation Gap | **Missing Binary Validation (CWE-829):** `ProjectSettings.Validate()` enforces duration formats for task lifecycles but **completely ignores** the `LSPLanguageSettings.Binary` field. Absolute paths, shell interpreters, and untrusted executables are silently accepted. |\n| **`internal/lsp/detect.go`** | Execution Sink #1 | **Unsanitized Health Check Execution:** `Detector.resolve()` passes the unvalidated override to `exec.LookPath()`, then invokes `runVersionCheck()` which blindly calls `cmd.Run()` on the attacker-controlled binary with `CheckArgs`. |\n| **`internal/lsp/server.go`** | Execution Sink #2 | **Unsanitized LSP Server Spawn:** `Server.Start()` passes the resolved binary path to `knownsprocess.Command()` and spawns it as a long-running background process via `cmd.Start()`, executing the payload a second time. |\n\n## Root Cause\n\n### Missing Validation in Configuration Schema\nIn `internal/models/config.go`, the `ProjectSettings.Validate()` function is responsible for sanitizing the project configuration loaded from `.knowns/config.json`. However, it only validates task lifecycle durations:\n\n```go\nfunc (s ProjectSettings) Validate() error {\n    settings := s.EffectiveTaskLifecycle()\n    if _, err := ParseTaskLifecycleDuration(settings.ArchiveAfter); err != nil { ... }\n    // NO VALIDATION FOR s.LSP.Languages[lang].Binary\n    return nil\n}\n```\n\nThe `LSPLanguageSettings` struct exposes a `Binary` string field. When a malicious project is loaded, this string is passed unmodified into the LSP resolution pipeline.\n\n### Blind Execution in LSP Detector\nIn `internal/lsp/detect.go`, the `Detector.resolve()` function accepts an `override` string (from the config) and forces it into the execution pipeline:\n\n```go\nfunc (d *Detector) resolve(ctx context.Context, root string, lang Language, override string) (ServerCommand, bool) {\n    binaries := lang.Binaries\n    if override != \"\" {\n        binary := Binary{Name: override} // Attacker\u0027s malicious path injected here\n        binaries = []Binary{binary}\n    }\n    for _, binary := range binaries {\n        path, err := d.LookPath(binary.Name) // Accepts absolute paths (e.g., /tmp/evil.sh)\n        // ...\n        err = d.RunCheck(checkCtx, path, binary.CheckArgs...) // EXECUTION #1\n        // ...\n    }\n}\n```\n\n`d.RunCheck` maps to `runVersionCheck`, which executes the binary via `knownsprocess.CommandContext(ctx, path, args...).Run()`.\n\n### Unrestricted Process Spawn\nIn `internal/lsp/server.go`, when the LSP manager starts the server, it executes the same compromised path:\n\n```go\nfunc (s *Server) Start(ctx context.Context) error {\n    // ...\n    cmd := knownsprocess.Command(s.Command.Path, s.Command.Args...) // EXECUTION #2\n    cmd.Dir = s.Root\n    // ...\n    if err := cmd.Start(); err != nil { ... }\n}\n```\n\n## Attack Vector\n\n| Phase | Request / Action | Effect |\n| :--- | :--- | :--- |\n| **1. Plant** | Attacker commits `.knowns/config.json` containing `{\"settings\":{\"lsp\":{\"languages\":{\"go\":{\"binary\":\"/tmp/evil.sh\"}}}}}` to a repository. | Malicious config embedded in the project. |\n| **2. Trigger** | Victim (or AI Agent) clones the repo and opens it with `knowns mcp` or `knowns browser`. | Auto-detection scans the project, finds a `.go` file, and loads the malicious config. |\n| **3. Execute (Health Check)** | `Detector.resolve()` calls `runVersionCheck(\"/tmp/evil.sh\", \"version\")`. | **RCE Execution #1** occurs silently in the background. |\n| **4. Execute (LSP Spawn)** | `Server.Start()` calls `cmd.Start()` with the same binary. | **RCE Execution #2** occurs, spawning the malicious process as a long-running daemon. |\n\n**Chained Attack Vector (Unauthenticated RCE):**\nIf combined with the **Config Overwrite** vulnerability (via `code.replace` path traversal), a remote attacker can overwrite `.knowns/config.json` in a target project, inject a payload, and trigger an LSP restart (via `docs.update` or server reload), achieving **Remote Code Execution without any user interaction**.\n\n## Analysis\n\nThis vulnerability is a textbook example of **Inclusion of Functionality from Untrusted Control Sphere (CWE-829)**, commonly known in the IDE/Editor space as the \"Malicious Workspace\" vulnerability (similar to historical CVEs in VS Code where `.vscode/settings.json` could point to malicious interpreter paths).\n\nThe core failure is the lack of a strict allowlist for executable paths. By relying on `exec.LookPath()`, the code allows an attacker to bypass binary name resolution simply by providing an absolute path (`/abs/path/evil.sh`) or a path containing a slash (`./evil.sh`). \n\nFurthermore, the execution happens **automatically** upon project load. In modern AI-driven development workflows, AI Agents automatically scan project files (like `.go`, `.ts`, `.py`) to provide context. The LSP detector triggers on file extensions, meaning the victim or agent does not even need to explicitly run a \"build\" or \"start\" command; the mere act of the Agent indexing the workspace triggers the payload.\n\n## Fix\n\n*Patch is available right now at [New Release](https://github.com/knowns-dev/knowns/releases).*",
  "id": "GHSA-mc52-mwq4-vfx3",
  "modified": "2026-10-06T18:58:38Z",
  "published": "2026-10-06T18:58:38Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/knowns-dev/knowns/security/advisories/GHSA-mc52-mwq4-vfx3"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-86540"
    },
    {
      "type": "WEB",
      "url": "https://github.com/knowns-dev/knowns/commit/d3989829fb5095666d23d005b2f78a082832a396"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/knowns-dev/knowns"
    },
    {
      "type": "WEB",
      "url": "https://github.com/knowns-dev/knowns/blob/v0.29.1/internal/lsp/detect.go#L128-L157"
    },
    {
      "type": "WEB",
      "url": "https://github.com/knowns-dev/knowns/blob/v0.29.1/internal/models/config.go#L205-L216"
    },
    {
      "type": "WEB",
      "url": "https://github.com/knowns-dev/knowns/releases/tag/v0.30.0"
    },
    {
      "type": "WEB",
      "url": "https://www.vulncheck.com/advisories/knowns-before-0.30.0-arbitrary-code-execution-via-lsp-binary"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "knowns OS Command Injection via Insecure LSP Binary Path Config in .knowns/config.json"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…