@yanzhang123/readonly-db-mcp 0.9.7 → 0.9.8

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
@@ -98,7 +98,11 @@ mask_columns = [] # 默认全部放开;省略此项效果相同。
98
98
 
99
99
  旧单连接模式通过 `MYSQL_MASK_COLUMNS` 配置,以逗号分隔;未设置或设为空字符串均不脱敏。例如 `MYSQL_MASK_COLUMNS=password,access_token` 只按这两个规则处理。使用命名连接文件时,以各连接自己的 `mask_columns` 为准。
100
100
 
101
- 显式配置会追踪别名、表达式和 CTE 的字段来源,并对 JSON 内匹配的键递归处理。不带点的模式只匹配字段名,例如 `password`;带点的模式按限定路径匹配,例如 `users.phone`。`COUNT` / `COUNT(DISTINCT ...)` 统计值可以正常读取,同一查询中匹配规则的原始值和拼接值仍会隐藏。
101
+ 显式配置会把每个结果列追溯到底层表的字段:别名、表达式、函数与类型转换、窗口函数、标量子查询与关联子查询、派生表、CTE(含列清单)、`UNION`、`LATERAL`、`JSON_TABLE`、`TABLE` 语句和 `*` 都会继续追踪,并对 JSON 内匹配的键递归处理。派生表或 CTE 中名称匹配规则的列也视为敏感来源;只有最外层的别名不算。不带点的模式只匹配字段名,例如 `password`;带点的模式按限定路径匹配,例如 `users.phone` 或 `app.users.phone`,表别名会解析回真实表名,未写库名的表按当前库匹配。`COUNT` / `COUNT(DISTINCT ...)` 统计值可以正常读取,同一查询中匹配规则的原始值和拼接值仍会隐藏;`WHERE`、`HAVING`、`EXISTS`、`IN` 子查询里的值不会出现在结果中,不会让其他列被遮蔽。
102
+
103
+ 脱敏按 fail-closed 设计:某个结果列的来源无法确定时(例如无法解析的列名、子查询中可能来自外层查询的列,或 MySQL 自动生成、与表达式文本对不上的列名),只要它可能来自匹配规则的字段,就按敏感处理;SQL 分析失败时同样按保守规则处理。因此少数复杂查询可能多遮几列。
104
+
105
+ 脱敏是结果出口的辅助控制,不是访问授权边界:它基于 SQL 静态分析,无法看到视图定义、存储函数内部或数据库中实际的表结构,也不能排除尚未覆盖的 SQL 写法。绝不能暴露的字段,应在数据库层用只包含安全字段的视图或列级权限隔离,再让本服务使用该账号。
102
106
 
103
107
  脱敏只替换返回值,不改变 MySQL 的 `WHERE` / `JOIN` 条件或删除结果行。`[REDACTED]` 表示值被隐藏,JSON/compact 的 `masked_columns` 可定位受影响列。已有的非空配置仍会生效;需要全部放开时,把它改成空列表(单连接模式清空环境变量),或删除该配置项。
104
108
 
package/SECURITY.md CHANGED
@@ -161,13 +161,28 @@ 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
+ - Masking is designed to fail closed: a result column whose source cannot be
177
+ determined (including unqualified names a subquery may resolve in the outer
178
+ query) is treated as sensitive if it could come from a matching field, and
179
+ analysis failures fall back to conservative rules. Some complex queries may
180
+ therefore show extra `[REDACTED]` columns.
181
+ - Masking is a static-analysis egress control, not an authorization boundary. It
182
+ cannot see view definitions, stored-function bodies, or actual table schemas,
183
+ and cannot rule out SQL shapes it does not yet model.
184
+ Fields that must never be exposed belong behind database views or column-level
185
+ permissions for the account this server uses.
171
186
 
172
187
  These controls enforce the MCP read-only contract even when the supplied MySQL
173
188
  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.8",
4
4
  "description": "Enterprise-auditable, strictly read-only MySQL MCP server with multi-environment profiles.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",