Skip to content

MCP write refusals still distinguish a withheld field from a nonexistent one, disclosing what the data schema withheld #1360

Description

@borisno2

Found during review of PR #1352 (issue #1163, part of #1127). Pre-existing core behaviour (#568), out of scope there — filed so it is not lost.

The defect

#1163 closed this leak on the read side: a field withheld from the MCP schema is now refused with the byte-identical message an unknown field gets, so the refusal discloses nothing the schema withheld.

The write side still distinguishes the two:

  • a withheld field → field-level access denied
  • a nonexistent field → not a field of this list

So an assistant can enumerate the fields it may not write, one probe at a time, by reading which refusal comes back — exactly the disclosure the read-side work exists to prevent. #1163 made this newly visible by omitting denied fields from the create/update data schemas: the schema now withholds a name, and the refusal then confirms it exists.

Acceptance criteria

  • A write naming a field withheld from the data schema is refused with the byte-identical message a nonexistent field gets
  • There is a single construction site for that message, as there now is on the read side (packages/core/src/mcp/projection.ts:401)
  • A test asserts real string equality between the two refusals, and is falsifiable — it fails if a distinct message is reintroduced
  • The refusal for a row-dependent denial is unchanged; only row-independent withholding is masked

Notes

The read-side fix is the model to follow: relation visibility and field visibility were unified into one advertisableFields vocabulary, and the refusal path looks names up in a map built from that same list, so a withheld name takes the identical branch to an unknown one.

Care is needed not to degrade a genuine validation error into a misleading "no such field" for a field the caller may write — the masking must apply only where the name was actually withheld.


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    ready-for-agentFully specified, ready for an AFK agent

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions