DEV Community

correctover
correctover

Posted on

A PreToolUse security gate for Kimi Code: block live secrets, plaintext MCP, and SSRF before they hit disk

Last week Kimi Code (Moonshot AI's terminal coding agent) shipped a fix for a nasty prompt-injection vector: timestamps in tool output could be crafted to smuggle instructions into the model's context (MoonshotAI/kimi-code#2028, fixed in the new v2 engine in 0.39.0 via #3007/#3341). Prompt-injection hardening matters — but injection is only half the story.

A coding agent that has been steered (or is simply over-eager) can also:

  • write a hardcoded sk-live-... key into src/config.js
  • add an MCP server over plaintext HTTP
  • point a tool at 169.254.169.254 (cloud-metadata SSRF)
  • run npm install inside a repo that already contains any of the above

Kimi Code has a mechanism for exactly this: Hooks (Beta). PreToolUse events fire before the permission check, and a hook can block the action by exiting with code 2. The block reason on stderr is fed back to the model — so it fixes the problem and retries.

We built an open-source (MIT) hook that wires this event to a real scanner: correctover-security-hook. This post explains how it works, what it catches, and how to verify it yourself in under a minute.

How Kimi Code hooks work (60-second version)

  • Rules live in ~/.kimi-code/config.toml as [[hooks]] entries with four fields: event, matcher (regex), command, timeout.
  • On a hookable event, the CLI pipes a JSON payload (tool name, parameters, file content) to your script via stdin.
  • Exit semantics: 0 = proceed; 2 = block, stderr goes back to the LLM as the reason; any other non-zero / timeout = fail open (proceed).
  • Only PreToolUse, Stop, and UserPromptSubmit can block. Security gates must hang on PreToolUse.
  • Official guidance: hooks are fail-open by design — a reminder/light-gate layer, not your only security control. Keep permission approvals, code review, and CI scanning.

What the hook does

Two PreToolUse matchers:

Matcher What gets scanned
`WriteFile\ StrReplaceFile`
Shell MCP-related commands (mcp add, config edits) → recursive config scan of the project; with CORRECTOVER_SCAN_INSTALLS=1, npm/pnpm/yarn/bun install → full-project code scan

Scanning runs through correctover-scan, an open-source npm package invoked via npx (free tier, no API key). The hook maps scanner output to exit codes:

  • fail-level finding → exit 2 (block; reason fed back to the model)
  • clean → exit 0
  • scanner missing / network down / timeout / quota exhausted → exit 0 with a stderr log (fail-open: a scanner problem never blocks your work)

What it actually flags:

  • Code layer (.js/.mjs/.cjs/.ts): hardcoded live-key patterns (sk-...), child_process with shell: true, eval / Function / vm dynamic execution, cloud-metadata/SSRF signals, credentials flowing into env/stdout.
  • MCP config layer: MCP server URLs without TLS (plaintext http), internal/cloud-metadata endpoints (169.254.169.254), plus warn-level gaps — missing timeouts, allowlists, or kill-switch.

The nice part is the feedback loop. When a write is blocked, the model reads why ("hardcoded credential at config.js:12 — move to an environment variable") and usually rewrites the code itself, then retries. The gate turns a latent secret leak into a self-correcting loop.

Test matrix: 8 scenarios, all behaving as expected

The repo ships a selftest that fakes Kimi Code's stdin payloads (measured with correctover-scan v1.7.2, Node 22):

# Scenario Scanner result Hook exit
A WriteFile a .js with a hardcoded sk-live- key cred-exposure FAIL 2 (block)
B WriteFile a clean .js no fail 0 (pass)
C WriteFile an unsafe MCP config (plaintext http + 169.254.169.254) mcp-tls + mcp-ssrf FAIL 2 (block)
D Shell plain ls -la not scanned 0 (pass)
E Scanner binary missing (fault injection) startup failure 0 (fail-open)
F Shell mcp add while a dangerous MCP config sits in the project recursive scan, 2 FAIL 2 (block)
G npm install with CORRECTOVER_SCAN_INSTALLS=1, leaky file in repo bundle FAIL 2 (block)
H npm install with default config not scanned 0 (pass)

The bundled script covers the five core cases (A–E, expect 5 pass / 0 fail); F–H use the install-toggle and are documented in the repo README.

git clone https://github.com/DSHCorrectover/kimi-code-security-hook
cd kimi-code-security-hook/selftest
bash run-hook-selftest.sh
Enter fullscreen mode Exit fullscreen mode

Or try it in a real session: ask Kimi Code to "add a Stripe key constant sk-live-51q8xPbQmRzNk2vWcY7aHdJfT3uLsE0oXnMp in src/config.js" — the write should be blocked and the model should rewrite it to read from an environment variable. (The sample key is a random fake string.)

Install

As a plugin: the repo is a Kimi Code plugin (kimi.plugin.json at the root) — point your Kimi Code plugin flow at the repository and the two PreToolUse hooks register automatically. We've also submitted it for the curated plugin marketplace (MoonshotAI/kimi-code#3534).

Manual install:

mkdir -p ~/.kimi-code/hooks
cp hooks/correctover-hook.mjs ~/.kimi-code/hooks/correctover-hook.mjs
Enter fullscreen mode Exit fullscreen mode

Then add to ~/.kimi-code/config.toml:

[[hooks]]
event = "PreToolUse"
matcher = "WriteFile|StrReplaceFile"
command = "node ~/.kimi-code/hooks/correctover-hook.mjs"
timeout = 180

[[hooks]]
event = "PreToolUse"
matcher = "Shell"
command = "node ~/.kimi-code/hooks/correctover-hook.mjs"
timeout = 180
Enter fullscreen mode Exit fullscreen mode

Start a new session (or /reload) and check /hooks. Requires Node.js 18+.

Environment variables:

Variable Default Purpose
CORRECTOVER_SCAN_CMD npx correctover-scan Scanner command; point at a local mirror for offline/CI
CORRECTOVER_SCAN_INSTALLS 0 Set 1 to bundle-scan the whole project before package installs
CORRECTOVER_SCAN_TIMEOUT 120000 Per-scan timeout in ms (fail-open on timeout)

The honest limits

  • Fail-open by design. Scanner missing, network down, timeout, or free-tier quota exhausted → the action proceeds with a stderr log. This is a light gate, not your only control.
  • Code scanning covers JS/TS only (signal-based, not full type semantics). Python and other languages pass through — wire up a language-appropriate scanner for those.
  • Signal scan, not proof of exploitability. Findings locate signals at file+line; reachability and intent need manual review.
  • Hooks are Beta in Kimi Code — event names, config format, and tool names may change across versions; re-run the selftest after upgrades. Legacy kimi-cli (~/.kimi/) uses different tool names (Bash); the script tolerates the aliases but matchers may need adjusting.

For one-off manual checks without installing anything, there's a zero-upload browser scan at correctover.com/scan.html — paste code, everything runs locally in the page.

Why this matters now

Agents are moving from "suggest diffs" to "write files, run commands, wire up MCP servers" — the boundary between generated text and executed action gets thinner every release. Prompt-injection fixes like #2028 harden what the model believes; PreToolUse hooks gate what the agent does. Defense in depth means you want both.

Top comments (0)