mirror of
https://github.com/RuoJi6/ARTEX.git
synced 2026-10-28 01:03:50 +08:00
在 agent 下发意图(add_intent)与插入资产(insert_assets)两处接入资产闸门;
并新增任务级规则(拦截 block + 允许 allow 白名单),创建任务时录入、任务详情
总览里可编辑。
- 匹配/执行(db/asset_intercept_match.go):MatchAssetInterceptRules 纯匹配
(exact/fuzzy 的 域名·IP·URL + CIDR,URL host 会拆出归类);EvaluateAssetGate
「先拦截后允许」闸门——命中拦截即禁止;未命中但存在启用的允许规则且都不命中→
不允许测试;无启用允许规则则不启用白名单。附单元测试。
- 执行点:add_intent 命中即不下发意图;insert_assets 命中即跳过 Upsert 及
AddAutoScope 副作用。均合并 全局∪任务级 规则。
- 任务级规则:表 task_intercept_rules(task_id FK CASCADE + action block/allow,
幂等 ALTER 补旧库);db/task_intercept.go 提供 List/Split/CRUD/事务插入;
创建任务随 intercept_rules 提交;CRUD 路由 /api/tasks/{id}/intercept-rules。
- 前端:新建任务抽屉多行录入(拦截/允许 + 类型,用 NativeSelect 避免 Sheet 内
下拉 portal 导致误关抽屉);任务详情总览新增规则管理卡片(增删改+启用开关)。
123 lines
4.2 KiB
Go
123 lines
4.2 KiB
Go
package db
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
)
|
|
|
|
// TaskInterceptRuleInput is one task-level rule supplied at task creation.
|
|
// Action: 'block'=拦截 'allow'=允许(白名单);空视为 'block'。
|
|
type TaskInterceptRuleInput struct {
|
|
Enabled bool `json:"enabled"`
|
|
Action string `json:"action"`
|
|
Kind string `json:"kind"`
|
|
Pattern string `json:"pattern"`
|
|
Note string `json:"note"`
|
|
}
|
|
|
|
const taskInterceptRuleCols = `id, enabled, action, kind, pattern, note, created_at, updated_at`
|
|
|
|
func scanTaskInterceptRule(row interface{ Scan(...any) error }) (AssetInterceptRule, error) {
|
|
var r AssetInterceptRule
|
|
err := row.Scan(&r.ID, &r.Enabled, &r.Action, &r.Kind, &r.Pattern, &r.Note, &r.CreatedAt, &r.UpdatedAt)
|
|
return r, err
|
|
}
|
|
|
|
// ListTaskInterceptRules returns a task's rules (both block and allow) as
|
|
// AssetInterceptRule (Builtin always false; Action carries block/allow). taskID
|
|
// <= 0 returns nothing.
|
|
func (s *AssetStore) ListTaskInterceptRules(taskID int64) ([]AssetInterceptRule, error) {
|
|
if taskID <= 0 {
|
|
return nil, nil
|
|
}
|
|
rows, err := s.db.Query(`SELECT `+taskInterceptRuleCols+` FROM task_intercept_rules WHERE task_id=$1 ORDER BY action, id`, taskID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var out []AssetInterceptRule
|
|
for rows.Next() {
|
|
r, err := scanTaskInterceptRule(rows)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, r)
|
|
}
|
|
return out, rows.Err()
|
|
}
|
|
|
|
// TaskInterceptRulesSplit loads a task's rules and splits them into block and
|
|
// allow sets, for the enforcement gate.
|
|
func (s *AssetStore) TaskInterceptRulesSplit(taskID int64) (block, allow []AssetInterceptRule, err error) {
|
|
rules, err := s.ListTaskInterceptRules(taskID)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
for _, r := range rules {
|
|
if r.Action == "allow" {
|
|
allow = append(allow, r)
|
|
} else {
|
|
block = append(block, r)
|
|
}
|
|
}
|
|
return block, allow, nil
|
|
}
|
|
|
|
func normalizeRuleAction(action string) string {
|
|
if action == "allow" {
|
|
return "allow"
|
|
}
|
|
return "block"
|
|
}
|
|
|
|
// CreateTaskInterceptRule inserts a rule under a task.
|
|
func (s *AssetStore) CreateTaskInterceptRule(taskID int64, action, kind, pattern, note string, enabled bool) (AssetInterceptRule, error) {
|
|
row := s.db.QueryRow(`
|
|
INSERT INTO task_intercept_rules(task_id, enabled, action, kind, pattern, note)
|
|
VALUES ($1,$2,$3,$4,$5,$6)
|
|
RETURNING `+taskInterceptRuleCols,
|
|
taskID, enabled, normalizeRuleAction(action), kind, pattern, note)
|
|
return scanTaskInterceptRule(row)
|
|
}
|
|
|
|
// UpdateTaskInterceptRule replaces the editable fields of a task's rule (scoped
|
|
// by task_id so a rule can only be edited through its owning task).
|
|
func (s *AssetStore) UpdateTaskInterceptRule(taskID, ruleID int64, action, kind, pattern, note string, enabled bool) (AssetInterceptRule, error) {
|
|
row := s.db.QueryRow(`
|
|
UPDATE task_intercept_rules
|
|
SET enabled=$3, action=$4, kind=$5, pattern=$6, note=$7
|
|
WHERE id=$1 AND task_id=$2
|
|
RETURNING `+taskInterceptRuleCols,
|
|
ruleID, taskID, enabled, normalizeRuleAction(action), kind, pattern, note)
|
|
return scanTaskInterceptRule(row)
|
|
}
|
|
|
|
// DeleteTaskInterceptRule removes a task's rule. Returns false if not found.
|
|
func (s *AssetStore) DeleteTaskInterceptRule(taskID, ruleID int64) (bool, error) {
|
|
res, err := s.db.Exec(`DELETE FROM task_intercept_rules WHERE id=$1 AND task_id=$2`, ruleID, taskID)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
n, _ := res.RowsAffected()
|
|
return n > 0, nil
|
|
}
|
|
|
|
// ToggleTaskInterceptRule flips the enabled state of a task's rule.
|
|
func (s *AssetStore) ToggleTaskInterceptRule(taskID, ruleID int64, enabled bool) error {
|
|
_, err := s.db.Exec(`UPDATE task_intercept_rules SET enabled=$3 WHERE id=$1 AND task_id=$2`, ruleID, taskID, enabled)
|
|
return err
|
|
}
|
|
|
|
// insertTaskInterceptRules inserts task-level rules within the task-creation
|
|
// transaction (mirrors insertTaskCompanies).
|
|
func insertTaskInterceptRules(tx *sql.Tx, taskID int64, rules []TaskInterceptRuleInput) error {
|
|
for _, r := range rules {
|
|
if _, err := tx.Exec(`
|
|
INSERT INTO task_intercept_rules(task_id, enabled, action, kind, pattern, note)
|
|
VALUES ($1,$2,$3,$4,$5,$6)`, taskID, r.Enabled, normalizeRuleAction(r.Action), r.Kind, r.Pattern, r.Note); err != nil {
|
|
return fmt.Errorf("insert task intercept rule %q: %w", r.Pattern, err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|