K3b:说明写清工具边界不按值换数值格的取舍;LATERAL 的 ON 条件里看得见它自己连的来源

整行引用找列时,在连接的来源本身里(LATERAL)只看得见前面的来源,在 ON 条件里连这个来源自己也看得见(_visible_sources),和 Postgres 的作用域一致。质询的已知局限补上:没设遮罩的列里存成数的号码工具边界不按值换、agent 看得到,要遮就在那个数据源上把那一列也设成遮罩;mask_query_result、模块说明按这一轮的改动重写,长行重新折好。
This commit is contained in:
ThearchyHelios
2026-10-06 23:17:15 +08:00
parent 888f746d31
commit 4b461e86e6
2 changed files with 15 additions and 9 deletions
+3 -2
View File
@@ -49,8 +49,9 @@
正文里写出某人的年龄遮不住,只靠 SQL 里的等值比较和按列遮;同一个短的数在 SQL 里被换掉了,别的列上撞上它的阈值、
LIMIT 的行数照原样(age = 30 AND visits >= 30)。够长的数在回答正文和回答引用的格子里按值遮,碰巧和某个遮罩值相同的
数(访问次数正好等于某一笔金额)也遮掉,这是产品取舍,不按上下文去猜;工具边界不按值换数值格(存下的是证据),交给
agent 的、存下的查询快照里这样的数照原样,只在回答和质询工件里遮。占位的写法(未知、暂无……,masking.PLACEHOLDERS)
不当遮罩值
agent 的、存下的查询快照里这样的数照原样,只在回答和质询工件里遮。反过来,没设遮罩的列里存成数的号码(别的库里的
联系方式表把手机号存成整数)工具边界也不按值换,agent 看得到:要遮就在那个数据源上把那一列也设成遮罩。存成文字的照换。
占位的写法(未知、暂无……,masking.PLACEHOLDERS)不当遮罩值
- 遮罩数值列的 SUM / AVG / MIN / MAX 算派生自遮罩列,整列遮(COUNT 不算,见 _value_columns),CASE 里有一支取的是
遮罩列的也算:MIN / MAX 就是某一行的原值,只有一行的分组 SUM / AVG 也是,按列认不出哪些汇总值安全,宁可多遮。
核查 agent 的查询在工具边界照这个办法遮,所以这样的指标双盲复算不了(原 SQL 里认得出的,派盲算之前就记为无法
+12 -7
View File
@@ -28,8 +28,9 @@
认它带不带同名的列(_schema_tables),不知道表结构的按带得出算(多遮)。分档写在派生来源里、外层按它分组的(一档
一行,_grouped_labels)同当分组键的 CASE 一样照常显示(_shown_labels)
- 哪些值(_Secrets):遮罩列里出现过的值,按值认有门槛(SECRET_MIN、SECRET_DIGITS),占位的写法(PLACEHOLDERS:未知、
暂无……)不收;换的时候先按词、按开头几个字筛出正文里真有的那几个值,不现编上万个分支的正则;正文、文字格、SQL 的字符串和注释里换了写法的长号码(199-1474-2785、
+86 …、全角数字)按纯数字写法在集合里查(loose_spans);已经摆在读者眼前的值免遮,之后并进来的照样免(protected)
暂无……)不收;换的时候先按词、按开头几个字筛出正文里真有的那几个值,不现编上万个分支的正则;正文、文字格、SQL 的
字符串和注释里换了写法的长号码(199-1474-2785、+86 …、全角数字)按纯数字写法在集合里查(loose_spans);已经摆在
读者眼前的值免遮,之后并进来的照样免(protected)
- 快照遮好的样子(_masked_view),回答时才认得出、事后要照位置重放的地方(view_edits、replay_edits)
- 一次核查里认出的遮罩值(SeenValues):调用方交进来的和 agent 查询时遮掉的分开记,合起来时 agent 查到的在前
@@ -1263,9 +1264,9 @@ def _visible_sources(select: Any, node: Any) -> list[tuple[str, Any]]:
from sqlglot import exp
sources = _from_sources(select)
child = node
child, below = node, None
while child.parent is not None and child.parent is not select:
child = child.parent
child, below = child.parent, child
if child.parent is not select:
return sources
if isinstance(child, exp.From):
@@ -1273,7 +1274,10 @@ def _visible_sources(select: Any, node: Any) -> list[tuple[str, Any]]:
if isinstance(child, exp.Join):
joins = list(select.args.get("joins") or [])
at = next((i for i, join in enumerate(joins) if join is child), None)
return sources[:1 + at] if at is not None else sources
if at is None:
return sources
# 在连接的来源本身里(LATERAL)只看得见前面的;在 ON 条件里连它自己也看得见
return sources[:1 + at] if below is child.this else sources[:2 + at]
return sources
@@ -2403,8 +2407,9 @@ def mask_query_result(payload: dict[str, Any], masked: Iterable[str], kind: str
- 整列遮:列名直接对上遮罩列的,加上按 SQL 认出值取自遮罩列的(_derived_hidden:别名、函数、子查询、WITH、
SELECT *(展开的列换了名字的也算)、表值函数、整行引用和函数参数里的 *、列名表、TABLE x;COUNT、只在条件里用到、
分档当分组键的不算);SQL 解析不了、又写着遮罩列(或者 *、TABLE x)的,整份结果的列都遮。这几列的格子换成
「已遮罩」,类型记成文本
分档当分组键的不算,分档写在派生来源里、外层按它分组的也不算);认不全的(SQL 解析不了又写着遮罩列、*、TABLE x,
(表达式).* 读到遮罩列,表名、SQL 写在字符串里交给函数,分析出意外、超出工作量上限)整份结果的列都遮。这几列的
格子换成「已遮罩」,类型记成文本
- 别的列按值换,只换文字格:整格就是遮罩值的换掉,文字格里夹着的那一截换掉(换了写法的号码也认,
_Secrets.loose_spans),换过的格子记下来(QueryMask.cells)。遮罩值是这份结果里的加上 known:先查号码、再查写着
号码的备注、没设遮罩的联系方式表(别的库里的也一样)时,后一份同样遮好。门槛同按值遮(SECRET_DIGITS、SECRET_MIN);