feat: 资产拦截接入执行链,新增任务级拦截/允许规则

在 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 导致误关抽屉);任务详情总览新增规则管理卡片(增删改+启用开关)。
This commit is contained in:
Autumn-27 committed 2026-09-19 09:06:22 -04:00
1 parent dfc9d2fa2e
commit d5359e1f04
14 files changed
+1234

No files matched your search

+15
View File
@@ -15,6 +15,7 @@ import type {
ArchiveBatchItem,
Asset,
AssetInterceptRule,
AssetInterceptRuleInput,
Audit,
BatchCategoryItem,
BatchControlItem,
@@ -243,6 +244,7 @@ export const api = {
seedFirstIntent?: boolean;
planHeartbeatSeconds?: number;
coverageEnabled?: boolean;
interceptRules?: AssetInterceptRuleInput[];
}) =>
post<Task>("/tasks", {
name: input.name ?? "",
@@ -256,6 +258,7 @@ export const api = {
seed_first_intent: input.seedFirstIntent ?? false,
plan_heartbeat_seconds: input.planHeartbeatSeconds ?? 0, // 0 = 后端归一到默认 600(10min)
coverage_enabled: input.coverageEnabled ?? true, // 默认开;false=关闭资产覆盖度功能
intercept_rules: input.interceptRules ?? [], // 任务级资产拦截规则
}),
taskCategories: () => get<{ categories: TaskCategory[] }>("/task-categories").then((r) => arr(r.categories)),
updateTask: (id: string, input: { name?: string; pinned?: boolean }) => patch<Task>(`/tasks/${id}`, input),
@@ -388,6 +391,18 @@ export const api = {
deleteConstraint: (id: string, constraintId: string) =>
del<{ ok: boolean }>(`/tasks/${id}/constraints/${constraintId}`),
// ---- 任务级资产拦截/允许规则(总览)----
taskInterceptRules: (id: string) =>
get<{ rules: AssetInterceptRule[] | null }>(`/tasks/${id}/intercept-rules`).then((r) => arr(r.rules)),
createTaskInterceptRule: (id: string, rule: AssetInterceptRuleInput) =>
post<AssetInterceptRule>(`/tasks/${id}/intercept-rules`, rule),
updateTaskInterceptRule: (id: string, ruleId: number, rule: AssetInterceptRuleInput) =>
put<AssetInterceptRule>(`/tasks/${id}/intercept-rules/${ruleId}`, rule),
deleteTaskInterceptRule: (id: string, ruleId: number) =>
del<{ deleted: boolean }>(`/tasks/${id}/intercept-rules/${ruleId}`),
toggleTaskInterceptRule: (id: string, ruleId: number, enabled: boolean) =>
post<{ ok: boolean; enabled: boolean }>(`/tasks/${id}/intercept-rules/${ruleId}/toggle`, { enabled }),
// 本任务测试范围列表(含继承自来源任务的范围)。
taskScope: (id: string) => get<{ scope: TaskScopeRow[] }>(`/tasks/${id}/scope`),
// 手动新增一条测试范围(kind=company/root_domain/subdomain/ip/cidr)。