feat(tools): 新增 sql_query 只读 SQL 查询工具(agent 可见)
agent 可对数据库执行只读查询。三重防护:
- 静态校验:仅单条 SELECT/WITH,词边界拒 insert/update/delete/drop/alter/create/
truncate/grant 等写/DDL 关键字(不误伤 created_at 之类列名)。
- 只读事务(sql.TxOptions{ReadOnly:true}):Postgres 引擎级强制只读,硬兜底。
- 行数(100)+超时(10s)+单元格(200 rune)上限。
连接:SQL_QUERY_DSN 优先(生产应指向专用只读库/账号),未设回退服务已解析的平台 PG DSN
(经 NewGateway 传入 g.pgDSN,不再裸读 env)。独立小连接池(8)。
测试:单测 validateReadOnlySQL(放行 SELECT/WITH/列名含 created;拒写/DDL/多语句)。
live 自主 agent 实测:查 sundynix_task=157 行、sundynix_model=2 行。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -56,3 +56,32 @@ func TestDDGUnwrap(t *testing.T) {
|
||||
t.Fatalf("ddgUnwrap 解码错误: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateReadOnlySQL(t *testing.T) {
|
||||
okCases := []string{
|
||||
"SELECT 1",
|
||||
"select count(*) from sundynix_task",
|
||||
"WITH x AS (SELECT 1) SELECT * FROM x",
|
||||
" select * from t where created_at > now() ", // created/update 作为列名一部分不应误伤
|
||||
}
|
||||
for _, q := range okCases {
|
||||
if _, ok := validateReadOnlySQL(q); !ok {
|
||||
t.Errorf("应通过: %q", q)
|
||||
}
|
||||
}
|
||||
badCases := []string{
|
||||
"insert into t values (1)",
|
||||
"update t set a=1",
|
||||
"delete from t",
|
||||
"drop table t",
|
||||
"select 1; drop table t",
|
||||
"truncate t",
|
||||
"SELECT 1; SELECT 2",
|
||||
"grant all on t to x",
|
||||
}
|
||||
for _, q := range badCases {
|
||||
if _, ok := validateReadOnlySQL(q); ok {
|
||||
t.Errorf("应拒绝: %q", q)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user