Skip to content

fix(mcp-guard): refuse table reads without table access events - #251

Merged
askdba merged 1 commit into
mainfrom
fix/guard-hidden-table-reads
Nov 1, 2026
Merged

askdba merged 1 commit into
mainfrom
fix/guard-hidden-table-reads

Conversation

@askdba

@askdba askdba commented Nov 1, 2026 •

Copy link
Copy Markdown
Owner

Found while reviewing the guard code behind #247. #247 is the stored-function case: a component can't see those reads at all. This PR fixes a related class it can catch.

The bug

mcp.allowed_schemas and mcp.deny_system_schemas are enforced on table-access events. MySQL raises those only for SELECT, data changes, ANALYZE and HANDLER READ (sql_audit.cc). Several statements on the read-only allowlist read tables without them.

Measured on 9.7.2, with an MCP account holding global SELECT (the case allowed_schemas exists for), allowed_schemas = 'd1' and deny_system_schemas on:

Statement Before
SET @x = (SELECT MAX(i) FROM d2.t); SELECT @x returns d2's data
SET @x = (SELECT COUNT(*) FROM mysql.user) returns mysql's data (password hashes would work the same way)
DO @x := (SELECT …), SET SESSION v = (SELECT …), PREPARE s FROM 'SET @p = (SELECT …)'; EXECUTE s returns the data
SHOW DATABASES WHERE (SELECT …) = 42 yes/no oracle
CALL mysql_mcp.list_tables((SELECT …)) oracle through the result
CHECKSUM TABLE d2.t, CHECK TABLE d2.t checksum or status

Not affected:

  • SELECT, TABLE and VALUES (events fire);
  • HANDLER (it has its own event; READ … WHERE rejects subqueries);
  • KILL (rejects subqueries);
  • SHOW CREATE (metadata, by design).

Fix

  • hides_table_reads() (policy header): CHECK / CHECKSUM, and SET, DO, CALL or SHOW … statements containing a subquery. Every subquery is parenthesized, so the check is ( followed by SELECT, TABLE, VALUES or WITH.
    • It scans in both quoting modes, like the read-only check.
    • Strings and comments never count.
  • The query callback refuses these, top-level or nested (an EXECUTEd statement included), while a schema rule is in effect: deny_system_schemas, on by default, or allowed_schemas. The error names that rule.
    • SET, SHOW and CALL without subqueries are unaffected. Neither the tool pack nor mysql-mcp-server uses these forms (checked).
  • Hardening: under lower_case_table_names 1 or 2, system schema names now compare case-insensitively, as the server compares them.
  • Parity table: 3 rows. The Go server copy will be updated after merge.

Verification

  • Policy unit test passes, with new cases including strings, comments and NO_BACKSLASH_ESCAPES.
  • scripts/test-mcp-guard.sh: 110/110 on MySQL 8.4.11, 9.7.2 and 26.10.0.
  • The unfixed guard fails all 8 new checks. It returns d2's 42 and a mysql.user row count.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Schema restrictions now also block table reads made through SET, DO, SHOW ... WHERE, CALL, prepared statements, and table-check commands when those reads are not visible to normal access checks.
    • System schema names are matched without regard to case when the server is configured for case-insensitive table names.
  • Documentation
    • Updated guidance to describe these restrictions and note SELECT as an alternative for affected statement forms.

Found reviewing the code behind #247. The server raises table access events only
for SELECT, data changes, ANALYZE and HANDLER READ, so the schema rules
(mcp.allowed_schemas, mcp.deny_system_schemas) did not see:
- a subquery in SET or DO: SET @x = (SELECT ... FROM mysql.user) returned the
  data, also through PREPARE/EXECUTE;
- a subquery in SHOW ... WHERE or a CALL argument (a yes/no oracle);
- CHECK TABLE / CHECKSUM TABLE.
Under the schema rules these are now refused (hides_table_reads, scanned in both
quoting modes). System schema names compare case-insensitively under
lower_case_table_names 1 or 2.

Tests: policy unit cases; test-mcp-guard.sh 110 checks on 8.4.11, 9.7.2,
26.10.0 (the unfixed guard fails all 8 new ones); 3 parity rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@askdba

askdba commented Nov 1, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-01T16:02:20.343042Z 831bab9 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 51e76573-b5f0-48fc-8446-0dd169e8445d
Commits

Reviewing files that changed from the base of the PR and between c61ccdc and 831bab9.

Files ignored due to path filters: 1
  • sql/mcp/guard-parity.tsv is excluded by !**/*.tsv
Files selected for processing: 6
  • CHANGELOG.md
  • docs/MCP_COMPONENTS.md
  • scripts/test-mcp-guard.sh
  • src/mcp/guard/component_mcp_guard.cc
  • src/mcp/guard/mcp_guard_policy.h
  • src/mcp/guard/test_mcp_guard_policy.cc

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

Walkthrough

The guard now detects selected table reads that do not raise table-access events and refuses them when schema rules apply. System-schema name matching also follows the configured case-sensitivity setting.

Changes: Hidden table-read enforcement

Layer / File(s) Summary
Detect hidden reads and match schema names
src/mcp/guard/mcp_guard_policy.h, src/mcp/guard/test_mcp_guard_policy.cc
Policy helpers detect subqueries in selected commands and flag CHECK and CHECKSUM. System-schema and mysql_mcp name comparisons follow the configured case sensitivity. Unit tests cover detection and name matching.
Enforce schema rules and validate coverage
src/mcp/guard/component_mcp_guard.cc, scripts/test-mcp-guard.sh, docs/MCP_COMPONENTS.md, CHANGELOG.md
The callback refuses hidden reads when schema rules are active. Integration checks cover denied reads and allowed commands without subqueries. Documentation and the changelog describe the behavior and updated test count.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Possibly related issues

Possibly related PRs

  • askdba/myvector#248: This PR extends the guard coverage and parity work established by #248.
  • askdba/myvector#241: This PR extends the schema policies and table-access enforcement introduced by #241.
  • askdba/myvector#244: This PR changes schema-enforcement behavior for the guard packaged and tested by #244.
Merge Risk: Minimal · up to 831ba

No merge-blocking issue was identified. The reported integration results were not independently reproduced.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentat… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: refusing table reads that bypass table-access events in mcp-guard.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 3 functions across 4 files. (2 skipped: 2 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches
Generate docstrings
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

A rabbit checks each query’s trail
For hidden reads that might prevail
When schema rules say, “Not this way,”
The guard can stop the read today
And mixed-case names join the play

@coderabbitai

coderabbitai Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Action not completed

Already reviewed the last commit. Use @coderabbitai full review to rerun a review of the entire changeset.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@askdba
askdba merged commit cd60ddb into main Nov 1, 2026
37 checks passed
@askdba
askdba deleted the fix/guard-hidden-table-reads branch November 1, 2026 17:19
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