Skip to content

feat: refuse secrets on write/edit and bound read output#14

Merged
vreshch merged 1 commit into
masterfrom
fix/secret-refuse-read-budget
Jul 6, 2026
Merged

feat: refuse secrets on write/edit and bound read output#14
vreshch merged 1 commit into
masterfrom
fix/secret-refuse-read-budget

Conversation

@vreshch

@vreshch vreshch commented Jul 6, 2026

Copy link
Copy Markdown
Member

Closes two verified requirement gaps in the engine so every surface (CLI verbs, local MCP, future consumers) inherits one behavior.

Gap 1 - secret refusal was description-only

The write/edit tool descriptions claim secrets are refused, but nothing enforced it. Adds src/contract/restricted-data.ts, a deliberately NARROW high-confidence screen (mirrors the cloud MCP's detector byte-for-byte): provider key/token prefixes, JWTs, PEM private keys, labeled password/secret assignments (placeholder-allowlisted), OTP/MFA codes, government IDs, and Luhn-valid payment cards. Ordinary notes that merely mention "password" or "auth" pass.

router.write / router.edit now scan body + frontmatter (and new_str for str_replace) and throw RestrictedContentError before anything is persisted. Stable, greppable refusal message; the matched value is never echoed back.

Gap 2 - read had no size budget

memory__read returned the body verbatim, unbounded to the model. Adds src/contract/read-budget.ts: a 64 KB read-output budget. router.read clamps oversized bodies on a UTF-8 codepoint boundary and appends a marker naming the shown/total byte counts and stating the stored file is complete and unchanged. The clamp is on the read path only, so an edit's internal full-body read is untouched.

Tests

  • restricted-data.test.ts - every secret class refused (write + edit), clean bodies pass, exact canonical message, no value echo, frontmatter-hidden secrets.
  • read-budget.test.ts - under / at / over the boundary, marker content, codepoint-safe cut, view shape preserved.
  • router-guards.test.ts - end-to-end through the real git backend: write/edit refuse (incl. str_replace injecting a secret), refused ops persist nothing, oversized read clamps + marks while the on-disk copy stays whole.

89 tests pass; type-check, lint, format, build all green. Version 0.1.2 -> 0.1.3.

Add an engine-level restricted-data screen so write/edit refuse
high-confidence secrets (API keys, tokens, JWT, PEM private keys,
password/secret assignments, OTP codes, SSNs, payment cards) before
anything is persisted, with a stable greppable refusal message. Add a
64 KB read-output budget that clamps oversized bodies on a codepoint
boundary and appends a marker noting the stored file is complete. Both
run in the router so CLI verbs, the local MCP server, and future
consumers inherit one behavior.

Bumps version to 0.1.3.
@vreshch vreshch marked this pull request as ready for review July 6, 2026 09:32
@vreshch vreshch merged commit 7fc18b1 into master Jul 6, 2026
1 check passed
@vreshch vreshch deleted the fix/secret-refuse-read-budget branch July 6, 2026 09:32
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