@yanzhang123/readonly-db-mcp 0.9.7 → 0.9.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -160,8 +160,14 @@ on unmatched started events and preserve them during incident review.
160
160
 
161
161
  Result masking is a final egress control, not an authorization boundary. Source
162
162
  columns matching `mask_columns` are redacted through aliases, expressions,
163
- subqueries and CTEs; unresolved outputs fall back to their result names. Bare
164
- patterns match only field names, and qualified patterns retain their scope.
163
+ conversions, window functions, subqueries, derived tables, CTEs, set operations,
164
+ JSON_TABLE, TABLE statements and stars. It is designed to fail closed: an output
165
+ whose source cannot be determined is masked when it could come from a matching
166
+ field, so complex queries may show extra redactions. Static analysis cannot see
167
+ view definitions, stored-function bodies or real schemas, and cannot rule out
168
+ SQL shapes it does not yet model. Bare
169
+ patterns match only field names; qualified patterns retain their scope, resolve
170
+ table aliases, and use the selected database for unqualified tables.
165
171
  COUNT statistics remain available without exposing their input values. Masking
166
172
  is disabled by default: omitted or empty `mask_columns` returns all field values
167
173
  without redaction. Configure the fields to mask per connection; there is no
package/README.md CHANGED
@@ -72,14 +72,16 @@ npx -y @yanzhang123/readonly-db-mcp@0.9.1
72
72
 
73
73
  服务启动后,建议依次调用 `validate_connections` 和 `check_connection`,确认配置有效、网络可达、账号权限符合预期。`check_connection` 只返回不可逆账号指纹和权限分类,不会暴露用户名或原始授权语句。
74
74
 
75
- ### 手动安装 Python 包
75
+ ### 从源码安装 Python 包
76
+
77
+ 本项目只通过上述 npm 包发布,不发布到 PyPI。PyPI 上名为 `mysql-mcp-server` / `mysql_mcp_server` 的包属于上游原项目,不包含本分支的只读、审计和脱敏实现,请勿用 `pip install mysql-mcp-server` 安装。源码部署时从本仓库安装:
76
78
 
77
79
  ```bash
78
- pip install mysql-mcp-server
80
+ git clone https://github.com/12zhangyan/mysql_mcp_server.git
81
+ cd mysql_mcp_server
82
+ pip install ".[http]" # 只用 STDIO 时可省略 [http]
79
83
  ```
80
84
 
81
- Python 包仍适用于源码部署,但本分支的主要分发方式是上述 npm 包。
82
-
83
85
  ## 配置
84
86
 
85
87
  ### 结果脱敏配置
@@ -98,7 +100,11 @@ mask_columns = [] # 默认全部放开;省略此项效果相同。
98
100
 
99
101
  旧单连接模式通过 `MYSQL_MASK_COLUMNS` 配置,以逗号分隔;未设置或设为空字符串均不脱敏。例如 `MYSQL_MASK_COLUMNS=password,access_token` 只按这两个规则处理。使用命名连接文件时,以各连接自己的 `mask_columns` 为准。
100
102
 
101
- 显式配置会追踪别名、表达式和 CTE 的字段来源,并对 JSON 内匹配的键递归处理。不带点的模式只匹配字段名,例如 `password`;带点的模式按限定路径匹配,例如 `users.phone`。`COUNT` / `COUNT(DISTINCT ...)` 统计值可以正常读取,同一查询中匹配规则的原始值和拼接值仍会隐藏。
103
+ 显式配置会把每个结果列追溯到底层表的字段:别名、表达式、函数与类型转换、窗口函数、标量子查询与关联子查询、派生表、CTE(含列清单)、`UNION`、`LATERAL`、`JSON_TABLE`、`TABLE` 语句和 `*` 都会继续追踪,并对 JSON 内匹配的键递归处理。按路径从 JSON 文档读取的值也会检查路径中的键:`JSON_EXTRACT`、`->`、`->>`、`JSON_VALUE` 和 `JSON_TABLE` 列的路径只要经过匹配不带点规则的键(例如 `$.contact.phone` 匹配 `phone`),读出的值就会隐藏;路径含通配符、递归 `**` 或不是常量时按敏感处理。派生表或 CTE 中名称匹配规则的列也视为敏感来源;只有最外层的别名不算。不带点的模式只匹配字段名,例如 `password`;带点的模式按限定路径匹配,例如 `users.phone` 或 `app.users.phone`,表别名会解析回真实表名,未写库名的表按当前库匹配。`COUNT` / `COUNT(DISTINCT ...)` 统计值可以正常读取,同一查询中匹配规则的原始值和拼接值仍会隐藏;`WHERE`、`HAVING`、`EXISTS`、`IN` 子查询里的值不会出现在结果中,不会让其他列被遮蔽。
104
+
105
+ 脱敏按 fail-closed 设计:某个结果列的来源无法确定时(例如无法解析的列名、子查询中可能来自外层查询的列,或 MySQL 自动生成、与表达式文本对不上的列名),只要它可能来自匹配规则的字段,就按敏感处理;SQL 分析失败时同样按保守规则处理。因此少数复杂查询可能多遮几列。
106
+
107
+ 脱敏是结果出口的辅助控制,不是访问授权边界:它基于 SQL 静态分析,无法看到视图定义、存储函数内部或数据库中实际的表结构,也不能排除尚未覆盖的 SQL 写法。绝不能暴露的字段,应在数据库层用只包含安全字段的视图或列级权限隔离,再让本服务使用该账号。
102
108
 
103
109
  脱敏只替换返回值,不改变 MySQL 的 `WHERE` / `JOIN` 条件或删除结果行。`[REDACTED]` 表示值被隐藏,JSON/compact 的 `masked_columns` 可定位受影响列。已有的非空配置仍会生效;需要全部放开时,把它改成空列表(单连接模式清空环境变量),或删除该配置项。
104
110
 
package/SECURITY.md CHANGED
@@ -161,13 +161,32 @@ The MCP server includes built-in protection against SQL injection for administra
161
161
  - Result masking is opt-in: `mask_columns` defaults to an empty list, and legacy
162
162
  `MYSQL_MASK_COLUMNS` defaults to empty. There is no built-in sensitive-field list;
163
163
  omitted or empty rules return all field values without redaction, including JSON
164
- keys. When rules are configured, matching source columns (or output names when
165
- unresolved) and JSON keys are replaced with `[REDACTED]`. Unqualified patterns
166
- match only field names; qualified patterns retain table/database scope. Aliases,
167
- expressions and CTEs retain tracing. COUNT statistics do not inherit masking
168
- from their inputs, while matching raw values and combined expressions remain
169
- masked. Configure rules against your schema; name-based masking cannot replace
170
- database views or column-level permissions.
164
+ keys. When rules are configured, each result column is traced through aliases,
165
+ expressions and conversions, window functions, scalar and correlated
166
+ subqueries, derived tables, CTEs (including column lists), set operations,
167
+ LATERAL, JSON_TABLE, TABLE statements and stars to base-table columns, and
168
+ matching sources and JSON keys are replaced with `[REDACTED]`. Columns of derived
169
+ tables and CTEs whose names match are sensitive sources too; only final output
170
+ aliases are exempt. Unqualified patterns match only field names; qualified
171
+ patterns retain table/database scope, resolve table aliases, and apply the
172
+ selected database to tables named without one. COUNT statistics do not inherit
173
+ masking from their inputs, while matching raw values and combined expressions
174
+ remain masked. Values from WHERE, HAVING, EXISTS and IN subqueries never reach
175
+ the result and do not mask other columns.
176
+ - Values read from a JSON document by path (`JSON_EXTRACT`, `->`, `->>`,
177
+ `JSON_VALUE`, JSON_TABLE column paths) are masked when any key on the path
178
+ matches an unqualified pattern, consistent with key masking inside returned JSON
179
+ text. Wildcard, recursive (`**`) and computed paths are treated as sensitive.
180
+ - Masking is designed to fail closed: a result column whose source cannot be
181
+ determined (including unqualified names a subquery may resolve in the outer
182
+ query) is treated as sensitive if it could come from a matching field, and
183
+ analysis failures fall back to conservative rules. Some complex queries may
184
+ therefore show extra `[REDACTED]` columns.
185
+ - Masking is a static-analysis egress control, not an authorization boundary. It
186
+ cannot see view definitions, stored-function bodies, or actual table schemas,
187
+ and cannot rule out SQL shapes it does not yet model.
188
+ Fields that must never be exposed belong behind database views or column-level
189
+ permissions for the account this server uses.
171
190
 
172
191
  These controls enforce the MCP read-only contract even when the supplied MySQL
173
192
  account has write privileges. A `SELECT`-only account is still recommended as
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yanzhang123/readonly-db-mcp",
3
- "version": "0.9.7",
3
+ "version": "0.9.9",
4
4
  "description": "Enterprise-auditable, strictly read-only MySQL MCP server with multi-environment profiles.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",