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 intosrc/config.js - add an MCP server over plaintext HTTP
- point a tool at
169.254.169.254(cloud-metadata SSRF) - run
npm installinside 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.tomlas[[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, andUserPromptSubmitcan block. Security gates must hang onPreToolUse. - 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_processwithshell: true,eval/Function/vmdynamic 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
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
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
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.
- Repo (MIT, issues/PRs welcome): https://github.com/DSHCorrectover/kimi-code-security-hook
- Scanner on npm: https://www.npmjs.com/package/correctover-scan
Top comments (0)