Skip to content

chore(hardening): pin the plugin catalog and stop unattended native updates - #6

Merged
devGregA merged 2 commits into
mainfrom
hardening/supply-chain
Aug 13, 2026
Merged

chore(hardening): pin the plugin catalog and stop unattended native updates#6
devGregA merged 2 commits into
mainfrom
hardening/supply-chain

Conversation

@devGregA

Copy link
Copy Markdown
Contributor

Problem

Three ways code could arrive on the machine with nothing binding it to a publisher.

  1. The plugin catalog was fetched from any URL, including http://. resolveMarketplaceLocation accepted anything, and KIMI_CODE_PLUGIN_MARKETPLACE_URL can point it anywhere. The catalog chooses which plugins are offered, where their archives come from, and carries the tier: "official" label — and an installed plugin can declare an mcpServers command that gets spawned. Whoever serves that file effectively chooses code that runs locally.
  2. Remote plugin archives could be http://. resolveInstallSource turned any non-GitHub http(s) URL into a zip-url, unsigned.
  3. The native updater ran unattended. For a native install, the background auto-update spawned bash -c "set -o pipefail; curl -fsSL …/install.sh | bash" — detached, output discarded, nothing verifying the response.

What changed

  • Catalog host policy: https required, host must be code.kimi.com / cdn.kimi.com, or named in KIMI_CODE_PLUGIN_MARKETPLACE_ALLOWED_HOSTS (comma-separated) so a self-hosted internal catalog stays possible. Errors name the offending host and the env var, so the fix is obvious from the message.
  • Archive scheme: https required for remote plugin sources, in both engine copies.
  • Loopback carve-out for both: plaintext to localhost / 127.0.0.1 / ::1 stays allowed. There is no network path to tamper with, and local dev/test servers legitimately use it — without this, several existing suites and pnpm dev:plugin-marketplace would break for no security gain.
  • canAutoInstall('native') is now false on every platform, so the updater prints the command instead of running it. This mirrors what the Windows path already did.

Behaviour change worth reviewing

  • A custom catalog on a non-default host now needs its host named in the env var. That is the intended trade: the default stops being open.
  • Native installs no longer self-update in the background; they print the command. npm / pnpm / yarn / bun installs are unaffected, since those go through the registry.

Testing

  • New: plaintext catalog rejected; non-allowed https host rejected; allowlisted self-hosted host accepted; loopback http accepted. Plus the archive-scheme cases (remote http rejected, loopback http accepted) in the source resolver.
  • Verified as real tests: with the three source files reverted, exactly the 5 new/updated negative tests fail.
  • Updated rather than deleted, per the repo's "fix the test to the new intent" rule:
    • two resolver tests asserted http:// was accepted;
    • the darwin preflight test asserted the background curl|bash spawn — it now mirrors the existing win32 "prints manual command, does not spawn" test;
    • three catalog tests use placeholder hosts (example.test / example.com) and now name them through the allowlist env, so they keep testing loader mechanics rather than host policy. The TUI "marketplace unreachable" test in particular stays a test about an unreachable catalog, not a blocked one.
  • Full regression across both engines and the CLI: 731 files / 11,664 tests green.
  • oxlint clean on changed files (one pre-existing dirname warning in plugin-marketplace.ts predates this branch — confirmed on main); tsc --noEmit clean for all three projects.

Checklist

  • Problem explained above (fork-local hardening; no upstream issue).
  • Tests added that prove the change works.
  • Changeset added (minor).
  • No doc update needed.

Greg Anderson and others added 2 commits August 12, 2026 21:00
…pdates

The plugin catalog decides which plugins are offered and where their
archives come from, and an installed plugin can declare a spawnable
mcpServers command, so whoever serves that file effectively chooses code
that runs on the machine. Require https and an allowed host, defaulting to
code.kimi.com and cdn.kimi.com, with
KIMI_CODE_PLUGIN_MARKETPLACE_ALLOWED_HOSTS for a self-hosted catalog.

Require https for remote plugin archives too. Plaintext to loopback stays
allowed: a local dev or test server has no network path to tamper with,
and several suites rely on it.

Stop auto-running the native updater. It is curl | bash against the CDN
with nothing verifying the response, so running it unattended in the
background turns a bad day at the CDN into local code execution. The
command is surfaced for the user to run instead, which is what the Windows
path already did.

Tests that asserted the old behaviour now assert the new one, and the two
catalog tests that use a placeholder host name it through the allowlist
env the way an operator would.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@devGregA
devGregA merged commit e56b156 into main Aug 13, 2026
13 checks passed
@devGregA
devGregA deleted the hardening/supply-chain branch August 13, 2026 03:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant