Skip to content

fix(core): refuse a generated body whose evidence is missing or not its own - #94

Merged
csdev19 merged 2 commits into
mainfrom
feature/niw2-333-apply_generated_body-can-desync-a-body-from-its-evidence-and
Oct 25, 2026
Merged

csdev19 merged 2 commits into
mainfrom
feature/niw2-333-apply_generated_body-can-desync-a-body-from-its-evidence-and

Conversation

@csdev19

@csdev19 csdev19 commented Oct 25, 2026

Copy link
Copy Markdown
Owner

What changed and why

apply_generated_body documents that a body and its generation evidence are "one fact, written together", but nothing enforced it. The HTTP and MCP schemas declared both as z.unknown(), which Zod treats as optional, and the writer skips undefined patch values. Three failures followed:

  1. No evidence key: the new body was written beside the old generation (old seed, old bodyHash), so the next apply reported a spurious "body-modified".
  2. No body key, confirm unset: the request passed the vouching check and replaced only generation, so the evidence claimed to describe a body it never saw.
  3. Forged evidence: nothing checked bodyHash(body) === generation.bodyHash, so an agent could mark any hand-written body as laqi-vouched.

Now Project.applyGeneratedBody refuses, before touching the file, an undefined body, evidence that is not GenerationEvidence, and a body whose hash is not evidence.bodyHash. confirm waives none of these. The HTTP route and the MCP tool also require both keys, so a bad request fails with a 400 / MCP error at the edge. A literal null body is still a body and is stored as null.

The editor already always sends both keys, so it is untouched. NIW2-314 (annotations, elicitation, backups) and NIW2-309 are not included.

How it was verified

Tests were committed first and failed (7 in core/HTTP, 3 in MCP stdio), then the fix turned them green.

  • bun run test packages/core packages/server packages/mcp/src/stdio.test.ts
  • The bun run verify steps (lint, format check, build, check-types, check-types:scripts, test), run twice in a row: 118 files, 1618 tests passing both times.

bun run verify itself stops in this worker's checkout at oxfmt --check on .claude/skills/.gc-skill-ownership.json, a Gas City file excluded from git. It is not part of this change and CI does not see it, so I ran the same steps with that one file left out.

How to test it yourself

  1. bun install && bun run test packages/core/src/project.test.ts packages/server/src/control-plane-app.test.ts packages/mcp/src/stdio.test.ts
  2. By hand, with a project that has a generated response (npx laqi, then generate_data and update_endpoint over MCP, or the panel's Generate button):
    • Call apply_generated_body with a preview's body but no generation: it is refused and laqi/api.json is unchanged.
    • Call it with a preview's generation but a different body: the error names bodyHash and the file is unchanged.
    • Regenerate and apply in the panel: it still works.

Refs NIW2-333

Run: Gas City session ni-wisp-sxa8swf, bead la-550, Mac mini

🤖 Generated with Claude Code

csdev19 and others added 2 commits October 24, 2026 23:07
Failing tests for NIW2-333: a missing evidence or body key, a body whose
hash is not the evidence's, and malformed evidence must all be refused
without touching the stored response. A literal null body stays valid.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ed or not its own

apply_generated_body took body and evidence as z.unknown(), which Zod
treats as optional, and the writer skips undefined patch values. A request
without the evidence key wrote a new body beside the old generation; one
without the body key replaced only the generation; and nothing checked
that bodyHash(body) matched evidence.bodyHash, so a hostile or buggy agent
could mark any hand-written body as laqi-vouched.

Project.applyGeneratedBody now rejects, before touching the file, an
undefined body, evidence that is not GenerationEvidence, and a body whose
hash is not evidence.bodyHash. The HTTP and MCP schemas require both keys.
A literal null body is still a body.

Refs NIW2-333

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@csdev19
csdev19 merged commit fb51f78 into main Oct 25, 2026
1 check passed
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