diff --git a/.flocks/plugins/skills/browser-use/references/cdp-setup.md b/.flocks/plugins/skills/browser-use/references/cdp-setup.md new file mode 100644 index 000000000..d3b0f3ed8 --- /dev/null +++ b/.flocks/plugins/skills/browser-use/references/cdp-setup.md @@ -0,0 +1,16 @@ +# Flocks browser setup + +浏览器已运行,但 daemon 或 active browser connection 不可用 + +必须直接提示用户: + +```text +browser: not connected — 请确保 Chrome / Chromium / Edge 已打开,然后访问对应浏览器的 inspect 页面(例如 chrome://inspect/#remote-debugging 或 edge://inspect/#remote-debugging)并勾选 Allow remote debugging +``` + +然后等待用户进一步指示,不要直接操作。 + +当用户确认已开启remote-debugging后: +1. 执行 `flocks browser --setup` 触发交互式 attach,不要用短超时包装该命令 +2. 再运行 `flocks browser --doctor` 做只读确认。 +3. 如果还失败,先执行 `flocks browser --reload` 清理旧 daemon,再重新执行 `flocks browser --setup`,避免因为残留 daemon 造成干扰。 \ No newline at end of file diff --git a/.flocks/plugins/skills/onesec-use/references/browser-workflow.md b/.flocks/plugins/skills/onesec-use/references/browser-workflow.md index c442b1de6..d09c13c8a 100644 --- a/.flocks/plugins/skills/onesec-use/references/browser-workflow.md +++ b/.flocks/plugins/skills/onesec-use/references/browser-workflow.md @@ -1,6 +1,8 @@ - # OneSEC 终端安全平台浏览器自动化 +> 本文档统一按 `browser-use` 的 `cdp-direct` 流程执行。进入浏览器模式后,先跑 `flocks browser --doctor`;doctor 通过后只使用 `flocks browser`。 +> 对后台任务 / 定时任务,或系统不支持可视化,使用 `browser-use` 的 `cdp-headless` 模式。 + ## 零、登录认证 State 文件路径:`~/.flocks/browser/onesec/auth-state.json`(固定,全局唯一)。 @@ -8,16 +10,37 @@ State 文件路径:`~/.flocks/browser/onesec/auth-state.json`(固定,全 ### 首次登录 / Session 过期重新登录 ```bash -# 用 --headed 打开浏览器,人工完成登录 -agent-browser close -agent-browser --headed open "https:///login" +flocks browser --doctor +``` + +如果 `flocks browser --doctor` 提示浏览器已运行,但 daemon 或 active browser connection 不可用,必须直接提示用户: + +```text +browser: not connected — 请确保 Chrome / Chromium / Edge 已打开,然后访问对应浏览器的 inspect 页面(例如 chrome://inspect/#remote-debugging 或 edge://inspect/#remote-debugging)并勾选 Allow remote debugging +``` + +然后等待用户进一步指示,不要直接操作。 + +当用户确认已开启 remote debugging 后: + +1. 执行 `flocks browser --setup` 触发交互式 attach,不要用短超时包装该命令。 +2. 再运行 `flocks browser --doctor` 做只读确认。 +3. 如果还失败,先执行 `flocks browser --reload` 清理旧 daemon,再重新执行 `flocks browser --setup`,避免因为残留 daemon 造成干扰。 +4. 只有随后 `--doctor` 通过后,才继续后面的登录或页面操作。 + +```bash +flocks browser -c ' +tid = new_tab("https:///login", activate=True) +wait_for_load() +print(tid) +print(page_info()) +' ``` 等待用户登录结束,收到通知后继续: ```bash -# 登录成功后立即保存 state -agent-browser state save ~/.flocks/browser/onesec/auth-state.json +flocks browser state save ~/.flocks/browser/onesec/auth-state.json ``` ### CLI 或页面认证失败时的恢复流程 @@ -32,28 +55,24 @@ agent-browser state save ~/.flocks/browser/onesec/auth-state.json 恢复步骤(最多尝试 1 次): ```bash -# 1) close 并重新加载 state -agent-browser close -agent-browser state load ~/.flocks/browser/onesec/auth-state.json - -# 2) 打开受保护页面验证 session -agent-browser open "https:///pcedr/dashboard" -agent-browser wait --load networkidle +flocks browser state load ~/.flocks/browser/onesec/auth-state.json --url "https:///pcedr/dashboard" +``` -# 3) 根据结果决策 -URL=$(agent-browser get url) +```bash +URL=$(flocks browser -c ' +info = page_info() +print(info.get("url", "")) +' | tail -n 1) if [[ "$URL" == *"/login"* ]]; then echo "Session 仍无效,需重新登录" else - agent-browser state save ~/.flocks/browser/onesec/auth-state.json + flocks browser state save ~/.flocks/browser/onesec/auth-state.json echo "Session 已恢复,可重试 CLI 或页面操作" fi ``` 如果仍然落回登录页,再要求用户重新登录,不要无限循环重试。 ---- - ## 一、产品导航与功能模块 > ⚠️ 如果 OneSEC 域名不清楚,请先询问用户,不要擅自填写域名。 @@ -62,9 +81,13 @@ fi **进入页面首选直接拼接 URL**(比菜单点击更稳定): ```bash -agent-browser open "https:///" -agent-browser wait --load networkidle -agent-browser get text body +flocks browser -c ' +tid = new_tab("https:///", activate=True) +wait_for_load() +print(tid) +print(page_info()) +print(js("document.body.innerText.slice(0, 2000)")) +' ``` | 模块 | 子功能 | URL 路径 | 主要用途 | @@ -74,21 +97,21 @@ agent-browser get text body | | DNS防护概览 | `/onedns/console/dashboard` | DNS 防护状态总览 | | | 银狐防治 | `/antisilverfox` | 银狐专项治理入口 | | | 报告中心 | `/onedns/console/reports` | 各类安全报表查看入口 | -| **终端检测与响应 (EDR)** | 威胁事件 | `/pcedr/threatincidents` | EDR 聚合威胁事件列表(事件维度),用于查看事件摘要、判定结果和处置状态 | -| | 检出行为 | `/pcedr/anomalyactivities` | 异常行为检测结果列表,用于查看被检出的可疑行为 | +| **终端检测与响应 (EDR)** | 威胁事件 | `/pcedr/threatincidents` | EDR 聚合威胁事件列表(事件维度) | +| | 检出行为 | `/pcedr/anomalyactivities` | 异常行为检测结果列表 | | | 恶意文件 | `/pcedr/threatfiles` | 恶意文件检测与管理入口 | -| | 日志调查 | `/pcedr/investigation` | EDR 原始告警/行为记录高级查询(日志维度),用于精细筛选和溯源分析 | -| | 响应中心 | `/pcedr/tasks` | 响应任务管理入口(处置维度),包含人工响应和自动响应 | -| | 威胁狩猎 | `/pcedr/threat_hunting` | 主动威胁狩猎场景入口,可联动日志调查 | +| | 日志调查 | `/pcedr/investigation` | EDR 原始告警 / 行为记录高级查询(日志维度) | +| | 响应中心 | `/pcedr/tasks` | 响应任务管理入口(处置维度) | +| | 威胁狩猎 | `/pcedr/threat_hunting` | 主动威胁狩猎场景入口 | | **DNS安全防护** | 域名解析报表 | `/onedns/console/domains` | DNS 解析统计报表 | | | 域名解析日志 | `/onedns/console/domainLog` | DNS 原始解析记录查询(日志维度) | | | 安全事件报表 | `/onedns/console/securityincident` | DNS 安全事件统计报表 | | | 内容分类报表 | `/onedns/console/contentCategory` | 网站内容分类统计报表 | -| | 威胁定位处置 | `/onedns/console/threatMitigation` | DNS 威胁告警定位与处置入口(DNS 告警维度) | +| | 威胁定位处置 | `/onedns/console/threatMitigation` | DNS 威胁告警定位与处置入口 | | | VA溯源日志 | `/onedns/console/vaInvestigation` | VA 溯源调查日志 | -| **漏洞补丁管理** | 漏洞管理 | `/vulnerability_manage` | 漏洞清单查询入口(资产维度),含严重级别、CVE 和影响终端 | +| **漏洞补丁管理** | 漏洞管理 | `/vulnerability_manage` | 漏洞清单查询入口(资产维度) | | | 补丁管理 | `/patch_manage` | 补丁分发与安装状态管理 | -| **软件安全** | 已安装软件/AI应用 | `/pcedr/softwarelist` | 终端软件资产清单(资产维度),用于查询已安装软件,不属于行为日志 | +| **软件安全** | 已安装软件/AI应用 | `/pcedr/softwarelist` | 终端软件资产清单(资产维度) | | | 软件管控 | `/software_control` | 软件黑白名单与远控软件管控入口 | | | 软件管控日志 | `/pcedr/software_log` | 软件管控执行记录查询(日志维度) | | **外设管控** | 外设管控日志 | `/device_control_log` | 外设使用记录查询(日志维度) | @@ -109,37 +132,22 @@ agent-browser get text body | | 平台配置 | `/platformconfig` | 系统参数配置入口 | | | 敏感数据加密 | `/pcedr/encrypt_data` | 敏感数据加密配置 | ---- - ## 二、数据查询与调查 -> 进入浏览器模式后,对于查询类诉求,优先阅读 [references/cli-reference.md](cli-reference.md) 并使用本地 CLI,只有在需要详情下钻或复杂交互时才继续页面点击。 +> 进入浏览器模式后,对于查询类诉求,优先阅读 [references/cli-reference.md](references/cli-reference.md) 并使用本地 CLI,只有在需要详情下钻或复杂交互时才继续页面点击。 ### 入口选择说明 -OneSEC 中 **事件**、**告警/日志**、**DNS 告警**、**资产数据** 分布在不同页面,必须先判断查询目标再选入口: +OneSEC 中 **事件**、**告警 / 日志**、**DNS 告警**、**资产数据** 分布在不同页面,必须先判断查询目标再选入口: | 查询目标 | 使用入口 | 数据维度 | |---------|---------|---------| -| 查看最新威胁事件、事件总览、处置状态 | 威胁事件 `/pcedr/threatincidents` | **事件**维度,平台已聚合,关注“发生了什么事件、影响哪些终端” | -| 精细查询高危告警、原始行为记录、溯源轨迹 | 日志调查 `/pcedr/investigation` | **告警/日志**维度,关注“具体哪条记录命中、命中了什么条件” | -| 查看 DNS 威胁告警和终端处置 | 威胁定位处置 `/onedns/console/threatMitigation` | **DNS 告警**维度,关注“哪些终端产生 DNS 威胁告警、当前如何处置” | -| 查询 DNS 原始解析记录 | 域名解析日志 `/onedns/console/domainLog` | **DNS 日志**维度,关注“终端解析了什么域名、何时发生” | -| 查看漏洞清单与高危漏洞 | 漏洞管理 `/vulnerability_manage` | **漏洞资产**维度,关注“有哪些漏洞、影响哪些终端” | -| 查询终端已安装软件 | 已安装软件 `/pcedr/softwarelist` | **软件资产**维度,关注“终端装了什么软件”,不是行为日志 | - -**核心区别**: -- **威胁事件**:OneSEC 将相关告警和行为聚合后的事件,一个事件通常包含多个告警/证据。 -- **告警/日志**:日志调查中的单条威胁、风险或行为记录,适合按字段、时间、终端、进程做精细筛选。 -- **DNS 告警**:OneDNS 模块里的独立告警视图,不应与 EDR 威胁事件混用。 -- **资产数据**:漏洞、软件、终端清单等静态或半静态数据,不应在日志调查里查。 - -**意图判定建议**: -- 用户说“最新事件”“看下最近的威胁事件”时,优先进入 `threatincidents`。 -- 用户说“最新高危告警”“查某终端的告警日志”时,优先进入 `investigation`。 -- 用户明确提到 “DNS 告警” 或 “DNS 威胁”,进入 `threatMitigation` 或 `domainLog`。 - ---- +| 查看最新威胁事件、事件总览、处置状态 | 威胁事件 `/pcedr/threatincidents` | **事件**维度 | +| 精细查询高危告警、原始行为记录、溯源轨迹 | 日志调查 `/pcedr/investigation` | **告警 / 日志**维度 | +| 查看 DNS 威胁告警和终端处置 | 威胁定位处置 `/onedns/console/threatMitigation` | **DNS 告警**维度 | +| 查询 DNS 原始解析记录 | 域名解析日志 `/onedns/console/domainLog` | **DNS 日志**维度 | +| 查看漏洞清单与高危漏洞 | 漏洞管理 `/vulnerability_manage` | **漏洞资产**维度 | +| 查询终端已安装软件 | 已安装软件 `/pcedr/softwarelist` | **软件资产**维度 | ### 2.1 EDR 威胁事件查询 @@ -147,106 +155,93 @@ OneSEC 中 **事件**、**告警/日志**、**DNS 告警**、**资产数据** > 事件详情查看的两种方式(威胁图 vs 事件概览)见 [references/onesec-incident.md](references/onesec-incident.md) -适合:查看最新威胁事件、按处置状态筛选、确认影响终端、阅读事件摘要。 - #### 默认方式:CLI 直接查询(推荐) ```bash ONESEC_BASE_URL=https:// \ ONESEC_AUTH_STATE=~/.flocks/browser/onesec/auth-state.json \ -python .flocks/plugins/skills/onesec-use/scripts/onesec_cli.py \ +uv run python .flocks/plugins/skills/onesec-use/scripts/onesec_cli.py \ threat search [--days ] [--page ] [--page-size ] [--keyword "<关键词>"] ONESEC_BASE_URL=https:// \ ONESEC_AUTH_STATE=~/.flocks/browser/onesec/auth-state.json \ -python .flocks/plugins/skills/onesec-use/scripts/onesec_cli.py \ +uv run python .flocks/plugins/skills/onesec-use/scripts/onesec_cli.py \ threat top [--days ] [--limit ] ``` -适用: - -- 看最近威胁行动列表 -- 看分页结果 -- 看 TOP 威胁名称 -- 需要稳定结构化结果而不是页面截图或摘要 - 只有在需要查看威胁图、事件概览、事件详情页时,才继续页面操作。 -页面左侧为**终端维度**列表(按终端聚合的威胁),右侧为**事件列表**(按事件聚合)。 - ```bash -agent-browser open "https:///pcedr/threatincidents" -agent-browser wait --load networkidle -agent-browser get text body +flocks browser -c ' +tid = new_tab("https:///pcedr/threatincidents", activate=True) +wait_for_load() +print(tid) +print(js("document.body.innerText.slice(0, 2000)")) +' ``` -**事件列表关键字段**:事件名称、判定结果(APT/黑产/远控/钓鱼/攻击者工具/蠕虫/自定义规则)、处置状态(未处置/处置中/已处置)、MEDR 告警解读、影响终端。 - -> 这里展示的是**聚合后的事件摘要**和关联告警解读,不是原始告警明细列表。 - **查看事件详情(默认方式,威胁图)**: ```bash -# 每行事件有一个跳转链接,直接 open 详情 URL(在 snapshot 中找到 ref 对应的 /url) -agent-browser open "https:///pcedr/threatincidents/incident?umid=...&guid=..." -agent-browser wait --load networkidle -agent-browser get text body +flocks browser -c ' +tid = new_tab("https:///pcedr/threatincidents/incident?umid=...&guid=...", activate=True) +wait_for_load() +print(tid) +print(js("document.body.innerText.slice(0, 2500)")) +' ``` **查看事件概览(快速方式)**: ```bash -# 点击事件表格行展开概览(index 从 0 开始,[0] 为第1行) -# 如 tbody tr 无效,改用 data-row-key:document.querySelector('[data-row-key="table0"]')?.click() -agent-browser eval "document.querySelectorAll('tbody tr')[0]?.click()" -agent-browser wait 500 -agent-browser get text body +flocks browser -c ' +attach_tab("") +js("document.querySelectorAll(\"tbody tr\")[0]?.click()") +wait(0.8) +print(js("document.body.innerText.slice(0, 2000)")) +' ``` -**顶部筛选**(判定结果、PUA 检测、处置状态等)是自定义组件,需用 eval 操作: +顶部筛选(判定结果、PUA 检测、处置状态等)是自定义组件,优先直接用 `js(...)`: ```bash -agent-browser eval "Array.from(document.querySelectorAll('*')).find(el => el.textContent?.trim() === 'APT')?.click()" +flocks browser -c ' +attach_tab("") +js(""" +Array.from(document.querySelectorAll("*")) + .find(el => el.textContent?.trim() === "APT") + ?.click() +""") +wait(0.5) +print(page_info()) +' ``` ---- - -### 2.2 告警日志调查(EDR 原始告警/行为日志,高级查询) +### 2.2 告警日志调查(EDR 原始告警 / 行为日志,高级查询) → 页面:`/pcedr/investigation` -适合:精细筛选高危告警、按终端/IP/Hash/进程路径查询、追踪某终端完整行为轨迹、做溯源分析。 - -**与威胁事件页的区别**:日志调查查的是每一条告警/日志原始行为记录,而非聚合后的事件。 - -> 完整字段列表(50+字段)、枚举值和查询语法见 [references/instruction.md](references/instruction.md) +> 完整字段列表(50+ 字段)、枚举值和查询语法见 [references/instruction.md](references/instruction.md) #### 默认方式:CLI 直接查询(推荐) ```bash ONESEC_BASE_URL=https:// \ ONESEC_AUTH_STATE=~/.flocks/browser/onesec/auth-state.json \ -python .flocks/plugins/skills/onesec-use/scripts/onesec_cli.py \ +uv run python .flocks/plugins/skills/onesec-use/scripts/onesec_cli.py \ log search "" [--days ] [--hours ] [--limit ] ONESEC_BASE_URL=https:// \ ONESEC_AUTH_STATE=~/.flocks/browser/onesec/auth-state.json \ -python .flocks/plugins/skills/onesec-use/scripts/onesec_cli.py \ +uv run python .flocks/plugins/skills/onesec-use/scripts/onesec_cli.py \ log types [--days ] ONESEC_BASE_URL=https:// \ ONESEC_AUTH_STATE=~/.flocks/browser/onesec/auth-state.json \ -python .flocks/plugins/skills/onesec-use/scripts/onesec_cli.py \ +uv run python .flocks/plugins/skills/onesec-use/scripts/onesec_cli.py \ log trend [--days ] ``` -适用: - -- 按 SQL 搜索日志 -- 最近 N 小时 / N 天日志筛查 -- 查看日志类型统计 -- 查看日志趋势 - 只有在需要点击单条记录详情、使用页面 AI 查询、查看联动面板或复杂筛选时,才继续页面操作。 **进入高级查询模式**: @@ -254,66 +249,69 @@ python .flocks/plugins/skills/onesec-use/scripts/onesec_cli.py \ > ⚠️ 高级查询使用字段名直接写条件语句,**必须先查阅 [references/instruction.md](references/instruction.md)** 确认字段名、枚举值和语法,否则查询无效。 ```bash -agent-browser open "https:///pcedr/investigation" -agent-browser wait --load networkidle - -# 点击"高级查询"按钮(按文字定位) -agent-browser eval "Array.from(document.querySelectorAll('button')).find(el => el.textContent?.trim() === '高级查询')?.click()" -agent-browser wait 1000 - -# 在输入框中填写查询语句(snapshot 获取输入框 ref) -agent-browser snapshot -i -agent-browser fill @eN "查询语句" # ref 以实际 snapshot 为准 - -# 点击"查询"执行 -agent-browser eval "Array.from(document.querySelectorAll('button')).find(el => el.textContent?.trim() === '查询')?.click()" -agent-browser wait --load networkidle -agent-browser get text body +flocks browser -c ' +tid = new_tab("https:///pcedr/investigation", activate=True) +wait_for_load() +print(tid) +js(""" +Array.from(document.querySelectorAll("button")) + .find(el => el.textContent?.trim() === "高级查询") + ?.click() +""") +wait(1.0) +js(""" +(() => { + const el = document.querySelector("textarea, input[placeholder*=查询], input[placeholder*=SQL]"); + if (!el) return false; + el.focus(); + el.value = "查询语句"; + el.dispatchEvent(new Event("input", {bubbles: true})); + el.dispatchEvent(new Event("change", {bubbles: true})); + return true; +})() +""") +js(""" +Array.from(document.querySelectorAll("button")) + .find(el => el.textContent?.trim() === "查询") + ?.click() +""") +wait(1.5) +print(js("document.body.innerText.slice(0, 2500)")) +' ``` -**常用查询示例**: - -| 查询场景 | 查询语法 | -|---------|---------| -| 特定终端所有行为 | `host_name = 'LAPTOP-20ECC75'` | -| 特定终端IP | `host_ip = '192.168.0.6'` | -| 只看威胁告警 | `threat.level = 'attack'` | -| 只看高危威胁告警 | `threat.level = 'attack' AND threat.severity = '3'` | -| 特定威胁名称 | `threat.name LIKE '%勒索%'` | -| 特定进程路径 | `proc_file.path LIKE '%AppData%Temp%'` | -| 可疑命令行(PowerShell编码) | `proc.cmdline LIKE '%powershell%' AND proc.cmdline LIKE '%-enc%'` | - -查询结果按 **全部 / 威胁 / 风险 / 攻击面收敛** Tab 分类展示,点击具体条目可查看完整字段详情。 - **使用 AI 查询**(输入自然语言 → 生成 SQL → 一键填入 → 查询): ```bash -# 第1步:打开 AI 查询面板 -agent-browser eval "Array.from(document.querySelectorAll('*')).filter(el => el.textContent?.includes('AI查询') && el.tagName === 'BUTTON')[0]?.click()" -agent-browser wait 1000 -agent-browser snapshot -i - -# 第2步:在输入框中填写自然语言描述(面板内的 textarea/input) -agent-browser fill @eN "查询最近一周内所有高危威胁日志" # ref 以实际 snapshot 为准 - -# 第3步:点击"生成SQL"按钮 -agent-browser eval "Array.from(document.querySelectorAll('button')).find(el => el.textContent?.trim() === '生成SQL')?.click()" - -# 第4步:等待 AI 推理并生成 SQL(有推理过程文字出现后再继续) -agent-browser wait --text "一键填入" - -# 第5步:点击"一键填入"将生成的 SQL 填入查询框 -agent-browser eval "Array.from(document.querySelectorAll('*')).find(el => el.textContent?.trim() === '一键填入')?.click()" -agent-browser wait 500 - -# 第6步:点击"查询"执行 -agent-browser eval "Array.from(document.querySelectorAll('button')).find(el => el.textContent?.trim() === '查询')?.click()" -agent-browser wait --load networkidle -agent-browser get text body +flocks browser -c ' +attach_tab("") +js(""" +Array.from(document.querySelectorAll("button")) + .find(el => el.textContent?.includes("AI查询")) + ?.click() +""") +wait(1.0) +js(""" +(() => { + const el = document.querySelector("textarea, input"); + if (!el) return false; + el.focus(); + el.value = "查询最近一周内所有高危威胁日志"; + el.dispatchEvent(new Event("input", {bubbles: true})); + el.dispatchEvent(new Event("change", {bubbles: true})); + return true; +})() +""") +js(""" +Array.from(document.querySelectorAll("button")) + .find(el => el.textContent?.trim() === "生成SQL") + ?.click() +""") +wait(2.0) +print(js("document.body.innerText.slice(0, 2500)")) +' ``` ---- - ### 2.3 DNS 查询与告警处置 #### 域名解析日志 @@ -322,210 +320,173 @@ agent-browser get text body 按终端内网 IP、终端名称、MAC 地址、DNS 查询域名、威胁类型、组内资产标记、时间范围等条件查询 DNS 解析记录。 -适合:还原某终端访问过哪些域名、核查某域名何时被解析、查询 DNS 原始日志。 - #### 威胁定位处置 → 页面:`/onedns/console/threatMitigation` 左侧为**威胁终端列表**,右侧为**威胁告警**(含严重级别、威胁名称、威胁类型、响应状态)。支持对终端下发处置任务。 -适合:查看最新 DNS 威胁告警、按终端定位 DNS 风险、联动做 DNS 处置。 - ---- - ### 2.4 资产与配置查询 #### 漏洞管理 → 页面:`/vulnerability_manage` -分 **Windows** 和 **信创** 两个 Tab,展示漏洞编号(CVE)、漏洞名称、影响应用、严重级别(高危/中危)、CVSS 评分、修复方式、涉及终端数。 - -适合:查看高危漏洞、按 CVE 检索、确认受影响终端范围。 - #### 已安装软件(软件资产查询) → 页面:`/pcedr/softwarelist` -> ⚠️ **查询终端上安装了哪些软件,应使用此页面,而非日志调查。** 此页面展示的是资产数据(软件清单),日志调查展示的是行为记录。 +> ⚠️ 查询终端上安装了哪些软件,应使用此页面,而非日志调查。 #### 终端管理 → 页面:`/pcedr/agent_group` -左侧为**职场/分组树**,右侧为终端列表(IP/MAC 地址、用户信息、所属职场/分组、当前生效策略、在线状态)。支持按分组筛选。 - #### 终端策略 → 页面:`/pcedr/policies` -顶部有多个 Tab:**基础**、安全通用、EDR、OneDNS、威胁防护、漏洞补丁管理、软件管控、外设管控、攻击面收敛。切换 Tab 查看不同策略配置。 - ---- - ### 2.5 响应与威胁狩猎 #### 响应中心 → 页面:`/pcedr/tasks` -分 **人工响应** 和 **自动响应** 两个 Tab,展示响应任务列表(任务ID、下发对象、任务类型、任务目标、任务状态、执行信息)。 - -**任务类型**:同步资产信息、漏洞扫描、恶意文件查杀等。 - ```bash -agent-browser open "https:///pcedr/tasks" -agent-browser wait --load networkidle -# 切换到"自动响应" Tab -agent-browser eval "Array.from(document.querySelectorAll('*')).find(el => el.textContent?.trim() === '自动响应')?.click()" -agent-browser wait 1000 -agent-browser get text body +flocks browser -c ' +tid = new_tab("https:///pcedr/tasks", activate=True) +wait_for_load() +js(""" +Array.from(document.querySelectorAll("*")) + .find(el => el.textContent?.trim() === "自动响应") + ?.click() +""") +wait(1.0) +print(js("document.body.innerText.slice(0, 2000)")) +' ``` #### 威胁狩猎 → 页面:`/pcedr/threat_hunting` -提供内置狩猎场景(执行与下载行为类、信息探测与收集、系统配置篡改、权限控制与突破等),可跳转至日志调查继续查看原始记录。 - ```bash -agent-browser open "https:///pcedr/threat_hunting" -agent-browser wait --load networkidle -agent-browser get text body -# 点击具体场景展开(折叠面板,需 eval) -agent-browser eval "const el = document.evaluate('//*[contains(text(),\"执行与下载\")]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; if(el) el.click();" +flocks browser -c ' +tid = new_tab("https:///pcedr/threat_hunting", activate=True) +wait_for_load() +print(tid) +print(js("document.body.innerText.slice(0, 2000)")) +js(""" +const el = document.evaluate( + '//*[contains(text(),"执行与下载")]', + document, + null, + XPathResult.FIRST_ORDERED_NODE_TYPE, + null +).singleNodeValue; +if (el) el.click(); +""") +wait(0.8) +print(js("document.body.innerText.slice(0, 2500)")) +' ``` - ---- - ## 三、浏览器操作技巧 -**核心原则**:OneSEC 是 SPA(React/Ant Design),自定义组件(div/span onClick)**无法被 `agent-browser click` 直接点击**,**都可以用 `agent-browser eval` 点击**。 - -**`agent-browser click` 失败时**,立即改用 eval(不要重试 click): - -```bash -# 方式1:用 -C 发现可交互元素(cursor:pointer/onclick),再用 CSS 选择器点击(不要用 ref) -agent-browser snapshot -i -C -# 输出示例: - clickable "导出" [ref=e200] [cursor:pointer, onclick] class="export-btn" -# 从输出中读取 class 名,构造 CSS 选择器点击(class 比 ref 更稳定): -agent-browser eval "document.querySelectorAll('.<从输出中获取的class>')[0]?.click()" - -# 方式2:XPath 文本定位(文字唯一时优先) -agent-browser eval "document.evaluate('//*[contains(text(),\"目标文字\")]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue?.click()" - -# 方式3:遍历查找(多个同名元素或精确匹配) -agent-browser eval "Array.from(document.querySelectorAll('*')).find(el => el.textContent?.trim() === '目标文字')?.click()" +**核心原则**:OneSEC 是 SPA(React / Ant Design),自定义组件通常不适合依赖通用点击语义;优先 `page_info()` 观察页面,再用 `js(...)` 直接读取 DOM 或触发点击。 -# 方式4:先打印 DOM 调试,再构造选择器 -agent-browser eval "Array.from(document.querySelectorAll('*')).filter(el => el.textContent?.trim().includes('目标文字') && el.textContent?.trim().length < 40).forEach(el => console.log(el.tagName, el.className, el.outerHTML.substring(0, 200)))" -``` +### 推荐操作模式 -**`eval --stdin` heredoc(复杂 JS 避免 shell 引号破坏)**: - -```bash -# 当 eval 表达式含嵌套引号、箭头函数、模板字符串时,用 heredoc 避免 shell 解析破坏 -agent-browser eval --stdin <<'EVALEOF' -Array.from(document.querySelectorAll('*')).find( - el => el.textContent?.trim() === '目标文字' && - window.getComputedStyle(el).cursor === 'pointer' -)?.click() -EVALEOF -``` +1. 先用 `page_info()` 看 URL、标题、滚动位置 +2. 再用 `js("document.body.innerText.slice(...)")` 或更具体的 DOM 读取确认页面结构 +3. 能稳定定位 DOM 时,直接 `js(...)` 点击或填值 +4. 页面变化后重新读取状态,不复用上一步的判断结果 -**语义定位器 `find`(比 eval 更简洁,优先尝试)**: +### 复杂 JS 推荐写法 ```bash -agent-browser find text "高级查询" click # 按文字找并点击 -agent-browser find role button click --name "查询" # 按 role + name -agent-browser find placeholder "输入查询内容" fill "查询语句" # 按 placeholder 填表 +flocks browser -c ' +attach_tab("") +js(""" +Array.from(document.querySelectorAll("*")) + .find(el => el.textContent?.trim() === "目标文字") + ?.click() +""") +wait(0.8) +print(page_info()) +' ``` -**`wait --fn` 等待 JS 条件(比固定 wait 毫秒更可靠)**: +### 等待结果出现 -```bash -agent-browser wait --fn "document.querySelectorAll('tbody tr').length > 0" # 等表格有数据 -agent-browser wait --fn "!document.querySelector('.ant-spin')" # 等 loading 消失 -agent-browser wait --fn "document.querySelector('.ant-result')" # 等结果出现 -``` +`browser-use` 下不再使用旧的 `wait --text` / `wait --fn` 模型。推荐做法是: -**导航首选直接拼 URL**(SPA 路由,URL 跳转比菜单点击更稳定): - -```bash -agent-browser open "https:///path" -agent-browser wait --load networkidle -``` +- 动作后 `wait(0.5 ~ 2.0)` +- 再次 `page_info()` 或 `js(...)` +- 必要时循环 2 到 3 次观察结果是否出现 -**必须使用 JavaScript 滚动**: +### 滚动与表格点击 ```bash -agent-browser eval "window.scrollTo(0, document.body.scrollHeight)" # 滚到底部 -agent-browser eval "window.scrollBy(0, 1000)" # 滚动指定像素 +flocks browser -c ' +attach_tab("") +js("window.scrollTo(0, document.body.scrollHeight)") +wait(0.8) +print(js("document.body.innerText.slice(0, 2000)")) +' ``` -**表格行点击**:OneSEC 使用 Ant Design 表格,优先用 `tbody tr`,不行再用 `data-row-key`: - ```bash -# 方式1:标准 tbody tr(优先) -agent-browser eval "document.querySelectorAll('tbody tr')[0]?.click()" - -# 方式2:data-row-key 属性(行索引从 table0 开始) -agent-browser eval "document.querySelector('[data-row-key=\"table0\"]')?.click()" - -# 保底:先调试确认实际行结构,再选择方式 -agent-browser eval "const rows = document.querySelectorAll('tbody tr'); console.log('行数:', rows.length, '结构:', rows[0]?.outerHTML?.substring(0,150))" +flocks browser -c ' +attach_tab("") +js("document.querySelectorAll(\"tbody tr\")[0]?.click()") +wait(0.8) +print(js("document.body.innerText.slice(0, 2000)")) +' ``` -**关闭 Ant Design 弹窗**: +### 调试技巧 ```bash -agent-browser eval "document.querySelector('.ant-modal-close')?.click()" +flocks browser -c ' +attach_tab("") +print(js(""" +Array.from(document.querySelectorAll("a, button")) + .slice(0, 100) + .map(el => ({ + tag: el.tagName, + text: el.textContent?.trim()?.slice(0, 30), + })) +""")) +' ``` -### 定位不到元素时:调试技巧 - ```bash -# 查看页面所有 a/button 的文本 -agent-browser eval "Array.from(document.querySelectorAll('a, button')).forEach(el => console.log(el.tagName, el.textContent?.trim().substring(0,30)))" - -# 确认表格行数及结构(保底调试) -agent-browser eval "const rows = document.querySelectorAll('tbody tr'); console.log('行数:', rows.length, rows[0]?.outerHTML?.substring(0,150))" +flocks browser -c ' +attach_tab("") +print(js(""" +(() => { + const rows = document.querySelectorAll("tbody tr"); + return { + rowCount: rows.length, + firstRowHtml: rows[0]?.outerHTML?.slice(0, 200) || "", + }; +})() +""")) +' ``` ---- - ## 四、重要提醒 -1. **refs 生命周期**:每次点击/滚动/页面变化后,`@eN` refs 会失效,必须重新 `snapshot -i` - -2. **优先直接拼 URL**:OneSEC 菜单使用 SPA 路由,`agent-browser open ` 比点击菜单更稳定可靠。 - -3. **等待加载**:导航或操作后,务必执行 `wait --load networkidle` 等待页面完全加载 - -4. **使用 headed 模式**:除非用户明确要求,默认使用 `--headed` 参数: - ```bash - agent-browser --headed open - ``` - -5. **不要主动关闭浏览器**:除非用户明确要求,否则不要执行 `agent-browser close` 关闭当前浏览器会话。 - -6. **滚动必须用 JavaScript**:`agent-browser scroll` 无法触发动态内容加载 - -7. **列表只展示摘要**:威胁事件、漏洞、补丁等列表只展示摘要,需点击进入详情获取完整信息 - -8. **查询优先级**:浏览器模式下,如果需求只是拉列表、跑 SQL、看趋势或统计,优先使用 [references/cli-reference.md](cli-reference.md) 中的 CLI,不要直接开始页面点击。 - ---- +1. **优先直接拼 URL**:OneSEC 菜单使用 SPA 路由,URL 跳转比菜单点击更稳定可靠。 +2. **不要主动关闭浏览器**:除非用户明确要求,否则不要关闭用户当前浏览器或用户已有 tab。 +3. **滚动必须用 JavaScript**:滚动后再重新读取页面状态。 +4. **列表只展示摘要**:威胁事件、漏洞、补丁等列表只展示摘要,需点击进入详情获取完整信息。 +5. **查询优先级**:浏览器模式下,如果需求只是拉列表、跑 SQL、看趋势或统计,优先使用 [references/cli-reference.md](references/cli-reference.md) 中的 CLI,不要直接开始页面点击。 ## 附加资源 -- **OneSEC 完整菜单结构与 URL 映射**:[references/onesec-menu.md](onesec-menu.md) - - 遇到 404、找不到功能入口时查阅,涵盖所有模块的完整路径 -- **威胁事件详情查看方式**:[references/onesec-incident.md](onesec-incident.md) - - 威胁图(跳转详情页)和事件概览(表格行点击)两种查看方式的操作说明 -- **日志调查字段说明与高级查询**:[references/instruction.md](instruction.md) - - 日志调查高级查询的全量字段(JSON路径)、枚举值、常用查询示例 -- **OneSEC CLI 参考**:[references/cli-reference.md](cli-reference.md) - - 浏览器模式下优先使用的 5 个查询接口,含认证方式、调用示例和返回字段说明 +- **OneSEC 完整菜单结构与 URL 映射**:[references/onesec-menu.md](references/onesec-menu.md) +- **威胁事件详情查看方式**:[references/onesec-incident.md](references/onesec-incident.md) +- **日志调查字段说明与高级查询**:[references/instruction.md](references/instruction.md) +- **OneSEC CLI 参考**:[references/cli-reference.md](references/cli-reference.md) diff --git a/.flocks/plugins/skills/onesec-use/references/onesec-incident.md b/.flocks/plugins/skills/onesec-use/references/onesec-incident.md index 89af44dde..51a990e09 100644 --- a/.flocks/plugins/skills/onesec-use/references/onesec-incident.md +++ b/.flocks/plugins/skills/onesec-use/references/onesec-incident.md @@ -10,12 +10,18 @@ ## 方式一:威胁图 / 事件详情页 ```bash -agent-browser --headed open "https:///pcedr/threatincidents" -agent-browser wait --load networkidle -agent-browser snapshot -i -agent-browser --headed open "https:///pcedr/threatincidents/incident?umid=&guid=" -agent-browser wait --load networkidle -agent-browser get text body +flocks browser -c ' +list_tid = new_tab("https:///pcedr/threatincidents", activate=True) +wait_for_load() +print(list_tid) +detail_tid = new_tab( + "https:///pcedr/threatincidents/incident?umid=&guid=", + activate=True, +) +wait_for_load() +print(detail_tid) +print(js("document.body.innerText.slice(0, 2500)")) +' ``` 详情页 URL 形式: @@ -27,15 +33,21 @@ agent-browser get text body 优先用 `tbody tr`,不行再用 `data-row-key`: ```bash -agent-browser eval "document.querySelectorAll('tbody tr')[0]?.click()" -agent-browser wait 500 -agent-browser get text body +flocks browser -c ' +attach_tab("") +js("document.querySelectorAll(\"tbody tr\")[0]?.click()") +wait(0.8) +print(js("document.body.innerText.slice(0, 2000)")) +' ``` ```bash -agent-browser eval "document.querySelector('[data-row-key=\"table0\"]')?.click()" -agent-browser wait 500 -agent-browser get text body +flocks browser -c ' +attach_tab("") +js("document.querySelector(\"[data-row-key=\\\"table0\\\"]\")?.click()") +wait(0.8) +print(js("document.body.innerText.slice(0, 2000)")) +' ``` > 事件概览只适合看摘要;需要完整攻击链时,优先用详情页。 diff --git a/.flocks/plugins/skills/onesig-use/references/browser-workflow.md b/.flocks/plugins/skills/onesig-use/references/browser-workflow.md index 23f2555f2..30ee77dfd 100644 --- a/.flocks/plugins/skills/onesig-use/references/browser-workflow.md +++ b/.flocks/plugins/skills/onesig-use/references/browser-workflow.md @@ -10,6 +10,8 @@ 如果走 API 能完成,请回到 [api-reference.md](api-reference.md) —— 浏览器操作不稳定、不可批量、字段不一定完整。 > ⚠️ OneSIG 的 Web 控制台与 OneSEC、青藤是不同产品;不要把 OneSEC / 青藤的页面路径或 API 套用到 OneSIG。 +> 本文档统一按 `browser-use` 的 `cdp-direct` 流程执行:先 `flocks browser --doctor`,doctor 通过后只使用 `flocks browser`。 +> 对后台任务 / 定时任务,或系统不支持可视化,使用 `browser-use` 的 `cdp-headless` 模式。 ## 零、登录认证 @@ -18,8 +20,31 @@ State 文件路径:`~/.flocks/browser/onesig/auth-state.json`(固定,全 ### 首次登录 / Session 过期重新登录 ```bash -agent-browser close -agent-browser --headed open "https:///login" +flocks browser --doctor +``` + +如果 `flocks browser --doctor` 提示浏览器已运行,但 daemon 或 active browser connection 不可用,必须直接提示用户: + +```text +browser: not connected — 请确保 Chrome / Chromium / Edge 已打开,然后访问对应浏览器的 inspect 页面(例如 chrome://inspect/#remote-debugging 或 edge://inspect/#remote-debugging)并勾选 Allow remote debugging +``` + +然后等待用户进一步指示,不要直接操作。 + +当用户确认已开启 remote debugging 后: + +1. 执行 `flocks browser --setup` 触发交互式 attach,不要用短超时包装该命令。 +2. 再运行 `flocks browser --doctor` 做只读确认。 +3. 如果还失败,先执行 `flocks browser --reload` 清理旧 daemon,再重新执行 `flocks browser --setup`,避免因为残留 daemon 造成干扰。 +4. 只有随后 `--doctor` 通过后,才继续后面的登录或页面操作。 + +```bash +flocks browser -c ' +tid = new_tab("https:///login", activate=True) +wait_for_load() +print(tid) +print(page_info()) +' ``` OneSIG 多数部署启用了图形验证码 + TOTP + 强制改密: @@ -31,7 +56,7 @@ OneSIG 多数部署启用了图形验证码 + TOTP + 强制改密: 等用户登录结束、收到通知后保存 state: ```bash -agent-browser state save ~/.flocks/browser/onesig/auth-state.json +flocks browser state save ~/.flocks/browser/onesig/auth-state.json ``` ### Session 失效恢复 @@ -45,22 +70,19 @@ agent-browser state save ~/.flocks/browser/onesig/auth-state.json 恢复步骤(最多尝试 1 次): ```bash -# 1) close 并重新加载 state -agent-browser close -agent-browser state load ~/.flocks/browser/onesig/auth-state.json - -# 2) 打开受保护页面验证 session -agent-browser open "https:///monitoring/dashboard" -agent-browser wait --load networkidle - -# 3) 根据结果决策 -URL=$(agent-browser get url) -if [[ "$URL" == *"/login"* ]]; then - echo "Session 仍无效,需重新登录" -else - agent-browser state save ~/.flocks/browser/onesig/auth-state.json - echo "Session 已恢复,可重试页面操作" -fi +# 1) 重新加载 state,并直接带目标 URL 验证 +flocks browser state load ~/.flocks/browser/onesig/auth-state.json --url "https:///monitoring/dashboard" + +# 2) 读取当前页面状态 +flocks browser -c ' +print(page_info()) +' +``` + +如果输出 URL 仍然落回登录页,再要求用户重新登录;否则重新保存一次 state 并继续: + +```bash +flocks browser state save ~/.flocks/browser/onesig/auth-state.json ``` 如果仍然落回登录页,再要求用户重新登录,**不要无限循环重试**。 @@ -71,9 +93,12 @@ fi > 进入页面首选直接拼接 URL(比菜单点击更稳定)。 ```bash -agent-browser open "https:///" -agent-browser wait --load networkidle -agent-browser get text body +flocks browser -c ' +tid = new_tab("https:///", activate=True) +wait_for_load() +print(page_info()) +print(js("document.body.innerText.slice(0, 2000)")) +' ``` | 模块 | 子功能 | URL 路径 | 主要用途 | @@ -150,14 +175,18 @@ agent-browser get text body ## 四、文件下载与导出 -OneSIG 控制台的导出按钮(资产 / 黑白名单 / 报表 / 审计 / coredump / pcap)多数会触发浏览器下载。在 `agent-browser` 模式下: +OneSIG 控制台的导出按钮(资产 / 黑白名单 / 报表 / 审计 / coredump / pcap)多数会触发浏览器下载。优先顺序如下: + +1. **优先用 API 的导出 / 下载 action**(参见 [api-reference.md](api-reference.md) "文件类返回 / 上传"小节)。 +2. 如果必须走页面下载,先用 `js(...)` 或稳定 selector 触发下载,再用浏览器系统的下载目录或页面提示确认下载已开始;不要在 skill 里承诺不存在的“等待下载完成”专有命令。 + +页面下载最小模板: ```bash -# 触发下载 -agent-browser click "<导出 / 下载 按钮的 selector>" -# 等下载完成 —— OneSIG 服务端通常 < 30s -agent-browser wait --download -agent-browser download list +flocks browser -c ' +attach_tab("") +js("document.querySelector(\"<导出按钮选择器>\")?.click()") +wait(1.0) +print(page_info()) +' ``` - -建议优先用 API 的导出 / 下载 action(参见 [api-reference.md](api-reference.md) "文件类返回 / 上传"小节)。 diff --git a/.flocks/plugins/skills/qingteng-use/references/browser-workflow.md b/.flocks/plugins/skills/qingteng-use/references/browser-workflow.md index c569cbfc1..a118cb6a3 100644 --- a/.flocks/plugins/skills/qingteng-use/references/browser-workflow.md +++ b/.flocks/plugins/skills/qingteng-use/references/browser-workflow.md @@ -1,19 +1,105 @@ - # 青藤安全平台浏览器自动化 -### 浏览器最小操作模板 +> 本文档统一按 `browser-use` 的 `cdp-direct` 流程执行。进入浏览器模式后,优先使用 `flocks browser --doctor` 检查环境;doctor 通过后只使用 `flocks browser`,不要再切回旧的浏览器命令。 +> 对后台任务 / 定时任务,或系统不支持可视化,使用 `browser-use` 的 `cdp-headless` 模式。 + +## 零、登录认证 + +State 文件路径:`~/.flocks/browser/qingteng/auth-state.json`(固定,全局唯一)。 + +### 首次登录 / Session 过期重新登录 + +先确保本机可复用 Chromium 系浏览器: + +```bash +flocks browser --doctor +``` + +如果 `flocks browser --doctor` 提示浏览器已运行,但 daemon 或 active browser connection 不可用,必须直接提示用户: + +```text +browser: not connected — 请确保 Chrome / Chromium / Edge 已打开,然后访问对应浏览器的 inspect 页面(例如 chrome://inspect/#remote-debugging 或 edge://inspect/#remote-debugging)并勾选 Allow remote debugging +``` + +然后等待用户进一步指示,不要直接操作。 + +当用户确认已开启 remote debugging 后: + +1. 执行 `flocks browser --setup` 触发交互式 attach,不要用短超时包装该命令。 +2. 再运行 `flocks browser --doctor` 做只读确认。 +3. 如果还失败,先执行 `flocks browser --reload` 清理旧 daemon,再重新执行 `flocks browser --setup`,避免因为残留 daemon 造成干扰。 +4. 只有随后 `--doctor` 通过后,才继续后面的登录或页面操作。 + +打开登录页并等待用户手动完成登录: + +```bash +flocks browser -c ' +tid = new_tab("https:///login", activate=True) +wait_for_load() +print(tid) +print(page_info()) +' +``` + +用户登录完成后,立即保存状态: + +```bash +flocks browser state save ~/.flocks/browser/qingteng/auth-state.json +``` + +### CLI 或页面认证失败时的恢复流程 + +当出现以下任一情况,优先判定为认证问题: + +- 返回 HTTP `401` / `403` +- 返回内容包含 `Unauthorized`、`login`、未登录、认证失败 +- `auth-state.json` 存在,但 CLI 请求或页面访问仍失败 + +恢复步骤(最多尝试 1 次): + ```bash -agent-browser open "https:///" -agent-browser wait --load networkidle -agent-browser snapshot -i +# 1) 重新加载 state,并直接带目标 URL 做验证 +flocks browser state load ~/.flocks/browser/qingteng/auth-state.json --url "https:///" + +# 2) 读取当前页面状态 +flocks browser -c ' +print(page_info()) +' ``` -更多使用方法参考: agent-browser skill +如果输出 URL 仍然落回登录页,再要求用户重新登录;不要无限循环重试。 + +## 浏览器最小操作模板 + +```bash +flocks browser -c ' +tid = new_tab("https:///", activate=True) +wait_for_load() +print(page_info()) +print(js("document.body.innerText.slice(0, 2000)")) +' +``` + +后续继续使用同一个 tab 时,优先先 attach 再操作: + +```bash +flocks browser -c ' +attach_tab("") +print(page_info()) +' +``` + +## 页面操作原则 + +- 能直接拼 URL 时,优先直接拼 URL,不走左侧菜单点击。 +- 先用 `page_info()` 和 `js(...)` 读取页面状态,再执行点击、输入或滚动。 +- 需要点击自定义组件时,优先用 `js(...)` 定位 DOM 后直接点击;只有 DOM 很难稳定定位时,才退到 `click_at_xy(...)`。 +- 页面变化后,之前读取到的文本、DOM 状态和坐标都可能失效;必须重新 `page_info()` 或重新跑 `js(...)`。 ## 重要提醒 - **Session 管理**:详见[零、登录认证](#零登录认证)。任务开始前先确认 `auth-state.json` 存在;CLI 认证失败时先走恢复流程,不要立刻要求用户重新登录。 - **禁止连续失败循环**:同一命令最多重试 2 次;认证恢复流程只走一次,仍失败则提示用户手动重新登录。 - **以下错误属于需要用户干预的基础设施问题,立即停止所有重试,直接告知用户处理**: - - `ERR_CERT_AUTHORITY_INVALID`:站点证书不被本机信任,使用 `--ignore-https-errors` 或请求用户处理。 + - `ERR_CERT_AUTHORITY_INVALID`:站点证书不被本机信任,请求用户处理。 - `ERR_NAME_NOT_RESOLVED`:域名无法解析,告知用户确认域名或检查 DNS / hosts 配置。 \ No newline at end of file diff --git a/.flocks/plugins/skills/sangfor-edr-use/references/browser-workflow.md b/.flocks/plugins/skills/sangfor-edr-use/references/browser-workflow.md new file mode 100644 index 000000000..3e54375e0 --- /dev/null +++ b/.flocks/plugins/skills/sangfor-edr-use/references/browser-workflow.md @@ -0,0 +1,285 @@ +# 深信服 EDR 浏览器工作流 + +> 本文件是深信服 EDR 的独立浏览器流程,基于 `browser-use` / `flocks browser` 的 `cdp-direct` 能力编写。 +> 当任务进入浏览器模式时,优先执行本文件;不要再依赖旧的 socket 示例作为主流程。 +> 对后台任务 / 定时任务,或系统不支持可视化,使用 `browser-use` 的 `cdp-headless` 模式。 + +## 适用范围 + +当任务涉及以下场景时,进入浏览器模式: + +- EDR 首页仪表盘查看 +- 终端状态、终端概况统计 +- 失陷设备排查 +- 页面详情、交互式筛选 + +## 环境信息 + +| 项目 | 值 | +|------|-----| +| **EDR 地址** | **需用户提供**(无默认值) | +| **目标页面 URL** | `{EDR_URL}/ui/#/index` | +| **浏览器入口** | `flocks browser` | + +## 零、前置条件 + +### 1. 先确认 EDR URL + +必须询问用户 EDR 地址,例如: + +- `https://edr.example.com/` + +### 2. 确保 `browser-use` 可用 + +先执行: + +```bash +flocks browser --doctor +``` + +如果 `flocks browser --doctor` 提示浏览器已运行,但 daemon 或 active browser connection 不可用,必须直接提示用户: + +```text +browser: not connected — 请确保 Chrome / Chromium / Edge 已打开,然后访问对应浏览器的 inspect 页面(例如 chrome://inspect/#remote-debugging 或 edge://inspect/#remote-debugging)并勾选 Allow remote debugging +``` + +然后等待用户进一步指示,不要直接操作。 + +当用户确认已开启 remote debugging 后: + +1. 执行 `flocks browser --setup` 触发交互式 attach,不要用短超时包装该命令。 +2. 再运行 `flocks browser --doctor` 做只读确认。 +3. 如果还失败,先执行 `flocks browser --reload` 清理旧 daemon,再重新执行 `flocks browser --setup`,避免因为残留 daemon 造成干扰。 +4. 只有随后 `--doctor` 通过后,才继续后面的登录或页面操作。 + +### 3. 登录 EDR + +如果页面需要登录、MFA 或人工确认,必须让用户在可见浏览器中完成,不要假设已有会话。 + +## 一、标准工作流 + +### Step 1:打开或创建目标页 + +优先创建自己的 tab: + +```bash +flocks browser -c ' +tid = new_tab("{EDR_URL}/ui/#/index", activate=True) +wait_for_load() +print(tid) +print(page_info()) +' +``` + +如果当前任务已经创建过该 URL 的 tab,也可以复用: + +```bash +flocks browser -c ' +tid = open_or_attach_tab("{EDR_URL}/ui/#/index", activate=True) +wait_for_load() +print(tid) +print(page_info()) +' +``` + +### Step 2:确认页面是否已登录且 attach 正确 + +```bash +flocks browser -c ' +print(page_info()) +print(js("document.body.innerText.slice(0, 2000)")) +' +``` + +如果出现登录框、未授权提示,或文本明显不属于 EDR 首页,让用户先完成登录,再重新读取。 + +### Step 3:如果主文档为空,检查 iframe + +EDR 页面内容可能在 iframe 中。主文档文本为空、明显不完整,或只看到外层壳页面时,改用 iframe 读取: + +```bash +flocks browser -c ' +frame = iframe_target("/ui/") +print({"iframe_target": frame}) +if frame: + print(js("document.body.innerText.slice(0, 2000)", target_id=frame)) +' +``` + +如果 iframe 目标不稳定,可把 `"/ui/"` 换成用户提供的 EDR 域名中的关键片段。 + +### Step 4:等待图表和统计卡片渲染 + +首页图表可能需要额外等待。默认做法: + +```bash +flocks browser -c ' +wait(3.0) +print(page_info()) +print(js("document.body.innerText.slice(0, 2500)")) +' +``` + +若仍在加载,再增加到 `5` 秒,但不要无限等待。 + +## 二、核心数据提取 + +### 首页概览文本抓取 + +```bash +flocks browser -c ' +print(js("document.body.innerText")) +' +``` + +如果需要结构化片段,优先在页面内拼装 JSON: + +```bash +flocks browser -c ' +text = js("document.body.innerText") +print({ + "page_text_preview": text[:3000], + "has_compromised": "已失陷" in text, + "has_cpu": "CPU" in text or "CPU:" in text, + "has_memory": "内存" in text, + "has_disk": "硬盘" in text, +}) +' +``` + +### 重点字段关键词 + +从页面文本中重点关注: + +| 数据 | 关键词 | +|------|--------| +| CPU使用率 | `CPU:` / `CPU:` | +| 内存使用率 | `内存:` / `内存:` | +| 硬盘使用率 | `硬盘:` / `硬盘:` | +| 终端总数 | `受管控终端` | +| 在线 / 离线 / 其它 | `在线:` / `离线:` / `其它:` | +| 服务器 / PC | `服务器:` / `PC:` | +| 已失陷 / 高可疑 / 低可疑 | `已失陷` / `高可疑` / `低可疑` | + +## 三、失陷设备查询 SOP + +### 目标 + +首页仪表盘显示“已失陷 N 台”时,进一步获取具体清单。 + +### 推荐流程 + +1. 先在首页确认“已失陷 N 台” +2. 进入威胁资产分析页面 +3. 切换到“已失陷终端”标签页,而不是停留在默认“全部” + +### 浏览器操作示例 + +```bash +flocks browser -c ' +attach_tab("") +js(""" +Array.from(document.querySelectorAll("*")) + .find(el => el.textContent?.trim() == "已失陷终端") + ?.click() +""") +wait(1.5) +print(js("document.body.innerText.slice(0, 2500)")) +' +``` + +如果通过文字无法稳定点击,先打印候选元素再重新构造点击逻辑: + +```bash +flocks browser -c ' +attach_tab("") +print(js(""" +Array.from(document.querySelectorAll("*")) + .filter(el => el.textContent?.includes("已失陷")) + .slice(0, 20) + .map(el => ({ + tag: el.tagName, + text: el.textContent?.trim(), + cls: el.className, + })) +""")) +' +``` + +> 必须避免直接读取默认“全部”筛选结果作为失陷设备清单。 + +## 四、排障顺序 + +### 1. `js(...)` 返回空文本 + +优先检查: + +1. 当前 attach 的是不是正确 tab +2. 页面是否仍在登录页 +3. 内容是否在 iframe + +推荐命令: + +```bash +flocks browser -c ' +print(current_tab()) +print(list_tabs(include_chrome=False)) +' +``` + +### 2. `new_tab()` 后后续命令无响应 + +说明 session 可能切到了错误上下文。处理方式: + +```bash +flocks browser -c ' +switch_tab("") +print(page_info()) +' +``` + +### 3. 页面数据为空 + +优先顺序: + +1. `wait(3.0)` 后重读 +2. 检查 iframe +3. 让用户确认当前页面确实是 `{EDR_URL}/ui/#/index` + +### 4. 页面显示登录框 + +直接告知用户重新登录 EDR,不要尝试绕过登录。 + +## 五、执行规范 + +- 默认优先 `new_tab(..., activate=True)` 创建自己的 tab。 +- 需要继续操作同一 tab 时,优先 `attach_tab("")`,不要反复抢焦点。 +- 每次点击、切 tab、等待、滚动之后,都重新读取 `page_info()` 或 `js(...)`。 +- 需要截图时,使用 `capture_screenshot(...)`,不要把截图作为默认主流程。 + +## 六、可选调试动作 + +截图: + +```bash +flocks browser -c ' +attach_tab("") +print(capture_screenshot("/tmp/sangfor-edr.png", max_dim=1800)) +' +``` + +打印按钮 / 链接候选: + +```bash +flocks browser -c ' +attach_tab("") +print(js(""" +Array.from(document.querySelectorAll("a, button")) + .slice(0, 100) + .map(el => ({ + tag: el.tagName, + text: el.textContent?.trim()?.slice(0, 40), + })) +""")) +' +``` diff --git a/.flocks/plugins/skills/sangfor-xdr-use/references/browser-workflow.md b/.flocks/plugins/skills/sangfor-xdr-use/references/browser-workflow.md new file mode 100644 index 000000000..dc6b2f20e --- /dev/null +++ b/.flocks/plugins/skills/sangfor-xdr-use/references/browser-workflow.md @@ -0,0 +1,256 @@ +# 深信服 XDR 浏览器工作流 + +> 本文件是深信服 XDR 的独立浏览器流程,基于 `browser-use` / `flocks browser` 的 `cdp-direct` 能力编写。 +> 当任务进入浏览器模式时,优先执行本文件;不要再依赖旧的 socket 示例作为主流程。 +> 对后台任务 / 定时任务,或系统不支持可视化,使用 `browser-use` 的 `cdp-headless` 模式。 + +## 适用范围 + +当任务涉及以下场景时,进入浏览器模式: + +- 系统运维页面查看 +- 节点状态、CPU / 内存 / 磁盘 / IO 趋势查看 +- 页面详情、交互式筛选、图表数据读取 +- API 未覆盖或 API 当前不可用的场景 + +## 环境信息 + +| 项目 | 值 | +|------|-----| +| **XDR 地址** | **需用户提供**(无默认值) | +| **目标页面 URL** | `{XDR_URL}/#/apex-business/settings/run/state` | +| **浏览器入口** | `flocks browser` | + +## 零、前置条件 + +### 1. 先确认 XDR URL + +必须询问用户 XDR 地址,例如: + +- `https://xdr.example.com/` + +### 2. 确保 `browser-use` 可用 + +先执行: + +```bash +flocks browser --doctor +``` + +如果 `flocks browser --doctor` 提示浏览器已运行,但 daemon 或 active browser connection 不可用,必须直接提示用户: + +```text +browser: not connected — 请确保 Chrome / Chromium / Edge 已打开,然后访问对应浏览器的 inspect 页面(例如 chrome://inspect/#remote-debugging 或 edge://inspect/#remote-debugging)并勾选 Allow remote debugging +``` + +然后等待用户进一步指示,不要直接操作。 + +当用户确认已开启 remote debugging 后: + +1. 执行 `flocks browser --setup` 触发交互式 attach,不要用短超时包装该命令。 +2. 再运行 `flocks browser --doctor` 做只读确认。 +3. 如果还失败,先执行 `flocks browser --reload` 清理旧 daemon,再重新执行 `flocks browser --setup`,避免因为残留 daemon 造成干扰。 +4. 只有随后 `--doctor` 通过后,才继续后面的登录或页面操作。 + +### 3. 登录 XDR + +如果页面需要登录、MFA 或人工确认,必须让用户在可见浏览器中完成。 + +## 一、标准工作流 + +### Step 1:打开或创建目标页 + +优先创建自己的 tab: + +```bash +flocks browser -c ' +tid = new_tab("{XDR_URL}/#/apex-business/settings/run/state", activate=True) +wait_for_load() +print(tid) +print(page_info()) +' +``` + +如果当前任务已经创建过该 URL 的 tab,也可以复用: + +```bash +flocks browser -c ' +tid = open_or_attach_tab("{XDR_URL}/#/apex-business/settings/run/state", activate=True) +wait_for_load() +print(tid) +print(page_info()) +' +``` + +### Step 2:确认页面已登录且 attach 正确 + +```bash +flocks browser -c ' +print(page_info()) +print(js("document.body.innerText.slice(0, 2000)")) +' +``` + +如果显示登录框、权限错误或明显不是系统运维页,让用户先完成登录,再重新读取。 + +### Step 3:等待图表和系统状态卡片渲染 + +XDR 系统运维页的趋势图和卡片通常需要额外等待。默认做法: + +```bash +flocks browser -c ' +wait(5.0) +print(page_info()) +print(js("document.body.innerText.slice(0, 3000)")) +' +``` + +如果只需要快速探测页面是否已加载,可先等 `3` 秒;若文本仍不完整,再增量等待。 + +## 二、核心数据提取 + +### 系统运维页面文本抓取 + +```bash +flocks browser -c ' +print(js("document.body.innerText")) +' +``` + +如果需要结构化探测,可先做轻量判断: + +```bash +flocks browser -c ' +text = js("document.body.innerText") +print({ + "has_status_overview": "状态总览" in text, + "has_cpu": "CPU使用趋势" in text, + "has_memory": "内存使用趋势" in text, + "has_disk": "磁盘使用趋势" in text, + "has_ingest": "数据采集吞吐率" in text, +}) +' +``` + +### 重点字段关键词 + +从页面文本中重点关注: + +| 数据 | 关键词 | +|------|--------| +| 节点健康 / 异常 / 不可用 | `状态总览` | +| CPU / 内存 / 磁盘使用率 | `CPU使用趋势` / `内存使用趋势` / `磁盘使用趋势` | +| 系统盘 / 数据盘 / CPU温度 | `系统盘监控状况` / `数据盘监控状况` / `CPU最高温度` | +| 磁盘 IO | `读取` / `写入` + `MiB/s` | +| 网口流量 | `接收` / `发送` + `MiB/s` | +| IO 延迟 | `IO读取延迟` / `IO写入延迟` + `ms` | +| 数据接入指标 | `数据采集吞吐率` / `数据解析速率` / `授权日志上限` / `日志接入总量` | + +## 三、页面交互与观察 + +### 重新 attach 当前 tab + +```bash +flocks browser -c ' +attach_tab("") +print(page_info()) +' +``` + +### 读取更多页面文本 + +```bash +flocks browser -c ' +attach_tab("") +print(js("document.body.innerText.slice(0, 5000)")) +' +``` + +### 需要滚动时 + +XDR 页面上尽量少用滚动;若确实需要,先滚动再重新读取: + +```bash +flocks browser -c ' +attach_tab("") +js("window.scrollBy(0, 1000)") +wait(1.0) +print(js("document.body.innerText.slice(0, 4000)")) +' +``` + +## 四、排障顺序 + +### 1. `js(...)` 返回空文本 + +优先检查: + +1. 当前 attach 的是不是正确 tab +2. 页面是否仍在登录页 +3. 图表是否尚未渲染完成 + +推荐命令: + +```bash +flocks browser -c ' +print(current_tab()) +print(list_tabs(include_chrome=False)) +' +``` + +### 2. `new_tab()` 后后续命令无响应 + +说明 session 可能切到了错误上下文。处理方式: + +```bash +flocks browser -c ' +switch_tab("") +print(page_info()) +' +``` + +### 3. 页面数据为空或加载中 + +优先顺序: + +1. `wait(5.0)` 后重读 +2. 确认当前 URL 是否就是系统运维页 +3. 让用户确认当前页面确实打开了 `{XDR_URL}/#/apex-business/settings/run/state` + +### 4. 页面显示登录框 + +直接告知用户重新登录 XDR,不要尝试绕过登录。 + +## 五、执行规范 + +- 默认优先 `new_tab(..., activate=True)` 创建自己的 tab。 +- 需要继续操作同一 tab 时,优先 `attach_tab("")`。 +- 每次等待、切 tab、滚动之后,都重新读取 `page_info()` 或 `js(...)`。 +- 如果需要视觉证据,使用 `capture_screenshot(...)`;不要把截图作为默认主流程。 + +## 六、可选调试动作 + +截图: + +```bash +flocks browser -c ' +attach_tab("") +print(capture_screenshot("/tmp/sangfor-xdr.png", max_dim=1800)) +' +``` + +打印按钮 / 链接候选: + +```bash +flocks browser -c ' +attach_tab("") +print(js(""" +Array.from(document.querySelectorAll("a, button")) + .slice(0, 100) + .map(el => ({ + tag: el.tagName, + text: el.textContent?.trim()?.slice(0, 40), + })) +""")) +' +``` diff --git a/.flocks/plugins/skills/skyeye-sensor-data-fetch/SKILL.md b/.flocks/plugins/skills/skyeye-sensor-use/SKILL.md similarity index 80% rename from .flocks/plugins/skills/skyeye-sensor-data-fetch/SKILL.md rename to .flocks/plugins/skills/skyeye-sensor-use/SKILL.md index 808739ac6..de4612965 100644 --- a/.flocks/plugins/skills/skyeye-sensor-data-fetch/SKILL.md +++ b/.flocks/plugins/skills/skyeye-sensor-use/SKILL.md @@ -1,11 +1,11 @@ --- -name: skyeye-sensor-data-fetch +name: skyeye-sensor-use description: 使用天眼 SkyEye Sensor 传感器侧精简 CLI 查询告警列表和告警统计。适用于用户提到"SkyEye Sensor""天眼流量传感器告警"场景。 --- # SkyEye Sensor 数据查询 -> 如果是分析平台日志检索,不要用这个 skill,改用 `skyeye-data-fetch`。 +> 如果是分析平台日志检索,不要用这个 skill,改用 `skyeye-use`。 ## 适用范围 @@ -23,21 +23,47 @@ description: 使用天眼 SkyEye Sensor 传感器侧精简 CLI 查询告警列 ## 零、登录认证 +> 对后台任务 / 定时任务,或系统不支持可视化,使用 `browser-use` 的 `cdp-headless` 模式。 + State 文件路径:`~/.flocks/browser/skyeye-sensor/auth-state.json`(固定,全局唯一)。 ### 首次登录 / Session 过期重新登录 ```bash -# 用 --headed 打开浏览器,人工完成登录(含短信验证码/MFA等) -agent-browser close -agent-browser --headed open "https:///login" +flocks browser --doctor +``` + +如果 `flocks browser --doctor` 提示浏览器已运行,但 daemon 或 active browser connection 不可用,必须直接提示用户: + +```text +browser: not connected — 请确保 Chrome / Chromium / Edge 已打开,然后访问对应浏览器的 inspect 页面(例如 chrome://inspect/#remote-debugging 或 edge://inspect/#remote-debugging)并勾选 Allow remote debugging +``` + +然后等待用户进一步指示,不要直接操作。 + +当用户确认已开启 remote debugging 后: + +1. 执行 `flocks browser --setup` 触发交互式 attach,不要用短超时包装该命令。 +2. 再运行 `flocks browser --doctor` 做只读确认。 +3. 如果还失败,先执行 `flocks browser --reload` 清理旧 daemon,再重新执行 `flocks browser --setup`,避免因为残留 daemon 造成干扰。 +4. 只有随后 `--doctor` 通过后,才继续后面的登录或页面操作。 + +打开登录页并等待用户手动完成登录(含短信验证码 / MFA 等): + +```bash +flocks browser -c ' +tid = new_tab("https:///login", activate=True) +wait_for_load() +print(tid) +print(page_info()) +' ``` 自动执行上述命令,等待用户登录结束,收到通知后继续: ```bash # 登录成功后立即保存 state -agent-browser state save ~/.flocks/browser/skyeye-sensor/auth-state.json +flocks browser state save ~/.flocks/browser/skyeye-sensor/auth-state.json ``` ### CLI 认证失败时的恢复流程 @@ -52,20 +78,25 @@ agent-browser state save ~/.flocks/browser/skyeye-sensor/auth-state.json ```bash # 1) 重新加载 state(强制刷新浏览器会话) -agent-browser close -agent-browser state load ~/.flocks/browser/skyeye-sensor/auth-state.json +flocks browser state load ~/.flocks/browser/skyeye-sensor/auth-state.json --url "https://" -# 2) 访问受保护页面验证 -agent-browser open "https://" -agent-browser wait --load networkidle +# 2) 读取当前页面状态 +flocks browser -c ' +print(page_info()) +' +``` +```bash # 3) 根据结果决策 -URL=$(agent-browser get url) +URL=$(flocks browser -c ' +info = page_info() +print(info.get("url", "")) +' | tail -n 1) if [[ "$URL" == *"/login"* ]]; then echo "Session 仍无效,需重新登录" # → 走上方「首次登录 / 重新登录」流程 else - agent-browser state save ~/.flocks/browser/skyeye-sensor/auth-state.json + flocks browser state save ~/.flocks/browser/skyeye-sensor/auth-state.json echo "Session 已恢复,可重试 CLI" # → 重试一次 CLI;若仍失败,再走重新登录,不要无限循环 fi diff --git a/.flocks/plugins/skills/skyeye-sensor-data-fetch/references/API_REFERENCE.md b/.flocks/plugins/skills/skyeye-sensor-use/references/API_REFERENCE.md similarity index 100% rename from .flocks/plugins/skills/skyeye-sensor-data-fetch/references/API_REFERENCE.md rename to .flocks/plugins/skills/skyeye-sensor-use/references/API_REFERENCE.md diff --git a/.flocks/plugins/skills/skyeye-sensor-data-fetch/scripts/api_client.py b/.flocks/plugins/skills/skyeye-sensor-use/scripts/api_client.py similarity index 100% rename from .flocks/plugins/skills/skyeye-sensor-data-fetch/scripts/api_client.py rename to .flocks/plugins/skills/skyeye-sensor-use/scripts/api_client.py diff --git a/.flocks/plugins/skills/skyeye-sensor-data-fetch/scripts/config.py b/.flocks/plugins/skills/skyeye-sensor-use/scripts/config.py similarity index 100% rename from .flocks/plugins/skills/skyeye-sensor-data-fetch/scripts/config.py rename to .flocks/plugins/skills/skyeye-sensor-use/scripts/config.py diff --git a/.flocks/plugins/skills/skyeye-sensor-data-fetch/scripts/requirements.txt b/.flocks/plugins/skills/skyeye-sensor-use/scripts/requirements.txt similarity index 100% rename from .flocks/plugins/skills/skyeye-sensor-data-fetch/scripts/requirements.txt rename to .flocks/plugins/skills/skyeye-sensor-use/scripts/requirements.txt diff --git a/.flocks/plugins/skills/skyeye-sensor-data-fetch/scripts/skyeye_sensor_cli.py b/.flocks/plugins/skills/skyeye-sensor-use/scripts/skyeye_sensor_cli.py similarity index 100% rename from .flocks/plugins/skills/skyeye-sensor-data-fetch/scripts/skyeye_sensor_cli.py rename to .flocks/plugins/skills/skyeye-sensor-use/scripts/skyeye_sensor_cli.py diff --git a/.flocks/plugins/skills/skyeye-use/references/browser-workflow.md b/.flocks/plugins/skills/skyeye-use/references/browser-workflow.md index 326c1760f..d83e731c8 100644 --- a/.flocks/plugins/skills/skyeye-use/references/browser-workflow.md +++ b/.flocks/plugins/skills/skyeye-use/references/browser-workflow.md @@ -1,7 +1,8 @@ - # SkyEye/天眼/网神分析平台浏览器自动化 > 如果 SkyEye 域名不清楚,先问用户,不要擅自填写。 +> 本文档统一按 `browser-use` 的 `cdp-direct` 流程执行。进入浏览器模式后,先跑 `flocks browser --doctor`;doctor 通过后只使用 `flocks browser`。 +> 对后台任务 / 定时任务,或系统不支持可视化,使用 `browser-use` 的 `cdp-headless` 模式。 ## 零、登录认证 @@ -10,16 +11,39 @@ State 文件路径:`~/.flocks/browser/skyeye/auth-state.json`(固定,全 ### 首次登录 / Session 过期重新登录 ```bash -# 用 --headed 打开浏览器,人工完成登录(含短信验证码/MFA等) -agent-browser close -agent-browser --headed open "https:///login" +flocks browser --doctor +``` + +如果 `flocks browser --doctor` 提示浏览器已运行,但 daemon 或 active browser connection 不可用,必须直接提示用户: + +```text +browser: not connected — 请确保 Chrome / Chromium / Edge 已打开,然后访问对应浏览器的 inspect 页面(例如 chrome://inspect/#remote-debugging 或 edge://inspect/#remote-debugging)并勾选 Allow remote debugging +``` + +然后等待用户进一步指示,不要直接操作。 + +当用户确认已开启 remote debugging 后: + +1. 执行 `flocks browser --setup` 触发交互式 attach,不要用短超时包装该命令。 +2. 再运行 `flocks browser --doctor` 做只读确认。 +3. 如果还失败,先执行 `flocks browser --reload` 清理旧 daemon,再重新执行 `flocks browser --setup`,避免因为残留 daemon 造成干扰。 +4. 只有随后 `--doctor` 通过后,才继续后面的登录或页面操作。 + +打开登录页并等待用户手动完成登录: + +```bash +flocks browser -c ' +tid = new_tab("https:///login", activate=True) +wait_for_load() +print(tid) +print(page_info()) +' ``` 等待用户登录结束,收到通知后继续: ```bash -# 登录成功后立即保存 state -agent-browser state save ~/.flocks/browser/skyeye/auth-state.json +flocks browser state save ~/.flocks/browser/skyeye/auth-state.json ``` ### CLI 认证失败时的恢复流程 @@ -33,24 +57,19 @@ agent-browser state save ~/.flocks/browser/skyeye/auth-state.json **恢复步骤(最多尝试 1 次)**: ```bash -# 1) 重新加载 state(强制刷新浏览器会话) -agent-browser close -agent-browser state load ~/.flocks/browser/skyeye/auth-state.json - -# 2) 访问受保护页面验证 -agent-browser open "https://" -agent-browser wait --load networkidle - -# 3) 根据结果决策 -URL=$(agent-browser get url) -if [[ "$URL" == *"/login"* ]]; then - echo "Session 仍无效,需重新登录" - # → 走上方「首次登录 / 重新登录」流程 -else - agent-browser state save ~/.flocks/browser/skyeye/auth-state.json - echo "Session 已恢复,可重试 CLI" - # → 重试一次 CLI;若仍失败,再走重新登录,不要无限循环 -fi +# 1) 重新加载 state,并直接打开目标站点验证 +flocks browser state load ~/.flocks/browser/skyeye/auth-state.json --url "https:///" + +# 2) 读取当前页面状态 +flocks browser -c ' +print(page_info()) +' +``` + +如果输出 URL 仍然落回登录页,再要求用户重新登录;否则重新保存一次 state 后重试 CLI: + +```bash +flocks browser state save ~/.flocks/browser/skyeye/auth-state.json ``` --- @@ -106,11 +125,12 @@ uv run python scripts/skyeye_cli.py \ 详细使用方法请阅读 [cli-reference](cli-reference.md) -## CLI 与 agent-browser 配合方式 +## CLI 与 browser-use 的配合方式 默认先用 CLI,不要因为页面更直观就直接打开浏览器。 - CLI 负责稳定查询和快速筛选 -- `agent-browser` 负责页面详情、导出下载、复杂交互和人工确认 +- `flocks browser` 负责页面详情、导出下载、复杂交互和人工确认 + 出现以下任一情况时,切到浏览器: - 需要页面详情 - 需要导出或下载 @@ -118,16 +138,28 @@ uv run python scripts/skyeye_cli.py \ - CLI 未覆盖目标能力 - CLI 当前不可用 - 页面需要人工登录、验证码、多因子认证或页面确认 + 一旦进入浏览器模式,就不要在同一任务里来回切回 CLI。 ### 浏览器最小操作模板 + ```bash -agent-browser open "https:///" -agent-browser wait --load networkidle -agent-browser snapshot -i +flocks browser -c ' +tid = new_tab("https:///", activate=True) +wait_for_load() +print(page_info()) +print(js("document.body.innerText.slice(0, 2000)")) +' ``` -更多使用方法参考: agent-browser skill +需要继续使用同一个 tab 时: + +```bash +flocks browser -c ' +attach_tab("") +print(page_info()) +' +``` ## 边界说明 @@ -137,8 +169,8 @@ agent-browser snapshot -i ## 重要提醒 -- **Session 管理**:详见[零、登录认证](#零登录认证)。任务开始前先确认 `auth-state.json` 存在;CLI 认证失败时先走恢复流程,不要立刻要求用户重新登录。 +- **Session 管理**:详见[零、登录认证](#零、登录认证)。任务开始前先确认 `auth-state.json` 存在;CLI 认证失败时先走恢复流程,不要立刻要求用户重新登录。 - **禁止连续失败循环**:同一命令最多重试 2 次;认证恢复流程只走一次,仍失败则提示用户手动重新登录。 - **以下错误属于需要用户干预的基础设施问题,立即停止所有重试,直接告知用户处理**: - - `ERR_CERT_AUTHORITY_INVALID`:站点证书不被本机信任,使用 `--ignore-https-errors` 或请求用户处理。 + - `ERR_CERT_AUTHORITY_INVALID`:站点证书不被本机信任,请求用户处理。 - `ERR_NAME_NOT_RESOLVED`:域名无法解析,告知用户确认域名或检查 DNS / hosts 配置。 \ No newline at end of file diff --git a/.flocks/plugins/skills/tdp-use/SKILL.md b/.flocks/plugins/skills/tdp-use/SKILL.md index 719970fee..fb5cfa16b 100644 --- a/.flocks/plugins/skills/tdp-use/SKILL.md +++ b/.flocks/plugins/skills/tdp-use/SKILL.md @@ -52,7 +52,7 @@ API 参数和适用场景见 [references/api-reference.md](references/api-refere - ⚠️ 如果 TDP 域名不清楚,请先询问用户,不要擅自填写域名。 - ⚠️ 用 --headed 打开浏览器,人工完成登录 -只要进入浏览器模式,就请阅读并按照browser-workflow操作(不要直接使用agent-browser skill)。 +只要进入浏览器模式,就请阅读并按照 browser-workflow 操作,不要跳过本 skill 直接套用其他通用浏览器说明。 请严格按照以下文档执行: - [references/browser-workflow.md](references/browser-workflow.md) diff --git a/.flocks/plugins/skills/tdp-use/references/browser-tips.md b/.flocks/plugins/skills/tdp-use/references/browser-tips.md index fed12f43d..82395d274 100644 --- a/.flocks/plugins/skills/tdp-use/references/browser-tips.md +++ b/.flocks/plugins/skills/tdp-use/references/browser-tips.md @@ -7,109 +7,171 @@ TDP 是 SPA 应用,直接跳转 URL 比点击菜单更稳定: ```bash -agent-browser open "https:///dashboard" -agent-browser wait --load networkidle -agent-browser snapshot -i +flocks browser -c ' +tid = new_tab("https:///dashboard", activate=True) +wait_for_load() +print(tid) +print(page_info()) +print(js("document.body.innerText.slice(0, 1200)")) +' ``` -### 菜单点击(需要先获取 refs) - -> ⚠️ refs 是动态生成的,每次页面加载后编号都会变化,必须先 `snapshot -i` 获取当前实际 ref。 +### 继续使用已有 tab ```bash -# 先获取当前页面 refs -agent-browser snapshot -i - -# 点击目标菜单(ref 以实际 snapshot 结果为准,示例仅为示意) -agent-browser click @e1 -agent-browser wait --load networkidle - -# 每次操作后需重新获取 refs -agent-browser snapshot -i +flocks browser -c ' +attach_tab("") +print(page_info()) +' ``` --- ## 2. 页面滚动 -> ⚠️ **必须使用 JavaScript 滚动**,`agent-browser scroll` 命令无法触发 TDP 动态加载的内容。 +> ⚠️ 必须使用 JavaScript 滚动;滚动后再重新读取页面状态。 ```bash -# 获取页面总高度(判断是否还有更多内容) -agent-browser eval "document.body.scrollHeight" +flocks browser -c ' +attach_tab("") +print(js("document.body.scrollHeight")) +js("window.scrollTo(0, document.body.scrollHeight)") +wait(1.0) +print(page_info()) +print(js("document.body.innerText.slice(0, 1600)")) +' +``` + +分步滚动: -# 滚动到底部(推荐,可触发动态加载) -agent-browser eval "window.scrollTo(0, document.body.scrollHeight)" +```bash +flocks browser -c ' +attach_tab("") +js("window.scrollBy(0, 1000)") +wait(0.8) +print(js("document.body.innerText.slice(0, 1600)")) +' +``` -# 分步滚动(每次滚动后截图确认内容) -agent-browser eval "window.scrollBy(0, 1000)" -agent-browser snapshot -i +滚动到指定元素: -# 滚动到指定元素 -agent-browser eval "const el = document.evaluate('//*[contains(text(),\"目标文字\")]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; if(el) el.scrollIntoView();" +```bash +flocks browser -c ' +attach_tab("") +js(""" +const el = document.evaluate( + '//*[contains(text(),"目标文字")]', + document, + null, + XPathResult.FIRST_ORDERED_NODE_TYPE, + null +).singleNodeValue; +if (el) el.scrollIntoView({block: "center"}); +""") +wait(0.5) +print(page_info()) +' ``` **判断是否需要继续滚动的依据**: -- 快照中显示的内容较少,页面有大量空白 -- 看到"查看更多"、"加载更多"等链接 +- 页面有大量空白 +- 看到“查看更多”“加载更多”等链接 - 页面布局明显不完整(如被截断的表格) -- 没有看到预期的数据列表 +- 还没有看到预期的数据列表 --- ## 3. 动态元素点击 -TDP 使用 React/Vue 构建,大量元素是 `
/` + onClick,`snapshot -i` 无法捕获。 +TDP 使用 React/Vue 构建,大量元素是 `
/` + onClick。推荐顺序是:先 `page_info()`,再 `js(...)` 观察和点击;不要再依赖旧的 ref 编号。 ### 方法一:XPath 文本定位(适合文本唯一的元素) ```bash -# 通过文本精确定位并点击 -agent-browser eval "const el = document.evaluate('//*[contains(text(),\"Redis\")]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; if(el) el.click();" - -# 在特定容器内查找 -agent-browser eval "const el = document.evaluate('//div[@class=\"menu\"]//span[contains(text(),\"日志分析\")]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; if(el) el.click();" +flocks browser -c ' +attach_tab("") +js(""" +const el = document.evaluate( + '//*[contains(text(),"Redis")]', + document, + null, + XPathResult.FIRST_ORDERED_NODE_TYPE, + null +).singleNodeValue; +if (el) el.click(); +""") +wait(0.8) +print(page_info()) +' ``` ### 方法二:表格行点击(告警列表、事件列表) ```bash -# 点击第一条数据行(如果用 `tr` 选择器,index 1 通常跳过表头) -agent-browser eval "document.querySelectorAll('tr')[1].click()" +flocks browser -c ' +attach_tab("") +js("document.querySelectorAll(\"tbody tr\")[0]?.click()") +wait(1.0) +print(js("document.body.innerText.slice(0, 2000)")) +' +``` -# 点击 tbody 中的第一条数据行(更精确,避免选中表头) -agent-browser eval "document.querySelectorAll('tbody tr')[0]?.click()" +点击第 N 条数据行: -# 点击第 N 条数据行(从 0 开始) -agent-browser eval "document.querySelectorAll('tbody tr')[N]?.click()" +```bash +flocks browser -c ' +attach_tab("") +row_idx = 2 +print(js(f"document.querySelectorAll(\"tbody tr\")[{row_idx}]?.click(); true")) +wait(1.0) +print(page_info()) +' ``` -### 方法三:遍历所有元素(适合折叠面板、Tab切换、动态按钮) - -当 XPath 无法定位,或目标元素有可点击的子元素时使用: +### 方法三:遍历所有元素(适合折叠面板、Tab 切换、动态按钮) ```bash -# 等待页面渲染完成 -agent-browser wait 2000 - -# 遍历所有元素,按文本内容定位并点击子按钮 -agent-browser eval "for(const el of document.querySelectorAll('*')) { const txt = el.textContent?.trim(); if(txt?.includes('目标文本')) { const btn = el.querySelector('button, svg, [role=button]'); if(btn) { btn.click(); break; } } }" +flocks browser -c ' +attach_tab("") +js(""" +for (const el of document.querySelectorAll("*")) { + const txt = el.textContent?.trim(); + if (txt?.includes("目标文本")) { + const btn = el.querySelector("button, svg, [role=button]") || el; + btn.click(); + break; + } +} +""") +wait(1.0) +print(js("document.body.innerText.slice(0, 2000)")) +' +``` -# 等待展开/跳转完成 -agent-browser wait 1000 +### 方法四:查找链接(“查看详情”等文字链接) -# 获取结果 -agent-browser get text body +```bash +flocks browser -c ' +attach_tab("") +js(""" +Array.from(document.querySelectorAll("a")) + .find(a => a.textContent?.includes("查看详情")) + ?.click() +""") +wait(1.0) +print(page_info()) +' ``` -### 方法四:查找链接("查看详情"等文字链接) +关闭弹窗: ```bash -# 查找并点击第一个包含特定文字的链接 -agent-browser eval "Array.from(document.querySelectorAll('a')).find(a => a.textContent.includes('查看详情'))?.click()" - -# 关闭弹窗(Ant Design Modal) -agent-browser eval "document.querySelector('.ant-modal-close')?.click()" +flocks browser -c ' +attach_tab("") +js("document.querySelector(\".ant-modal-close\")?.click()") +wait(0.5) +print(page_info()) +' ``` ### 定位策略选择表 @@ -117,9 +179,9 @@ agent-browser eval "document.querySelector('.ant-modal-close')?.click()" | 场景 | 推荐方式 | |------|----------| | 文本唯一的元素 | XPath `contains(text(),'关键词')` | -| 多个相似元素,需精确匹配 | `textContent.match(/正则/)` | +| 多个相似元素,需精确匹配 | `textContent` + 过滤逻辑 | | 父元素含多个子按钮 | 先定位父,再 `querySelector('button, svg, [role=button]')` | -| class 名动态变化 | 用标签名 `button, svg, [role=button]` 而非 class | +| class 名动态变化 | 用标签名、文本、DOM 结构,不依赖 class | | 表格数据行 | `querySelectorAll('tbody tr')[index].click()` | | 文字链接 | `Array.from(querySelectorAll('a')).find(...)` | @@ -128,14 +190,57 @@ agent-browser eval "document.querySelector('.ant-modal-close')?.click()" ## 4. 调试技巧(定位不到元素时) ```bash -# 打印页面内所有包含目标文字的元素信息 -agent-browser eval "for(const el of document.querySelectorAll('*')) { const txt = el.textContent?.trim(); if(txt && txt.includes('目标文本') && txt.length < 50) { console.log(el.tagName, el.className, el.outerHTML.substring(0, 300)); } }" +flocks browser -c ' +attach_tab("") +print(js(""" +Array.from(document.querySelectorAll("*")) + .filter(el => { + const txt = el.textContent?.trim(); + return txt && txt.includes("目标文本") && txt.length < 50; + }) + .slice(0, 20) + .map(el => ({ + tag: el.tagName, + className: el.className, + text: el.textContent?.trim(), + html: el.outerHTML.slice(0, 200), + })) +""")) +' +``` + +查看页面所有链接和按钮: + +```bash +flocks browser -c ' +attach_tab("") +print(js(""" +Array.from(document.querySelectorAll("a, button")) + .slice(0, 100) + .map(el => ({ + tag: el.tagName, + text: el.textContent?.trim()?.slice(0, 30), + href: el.href || "", + })) +""")) +' +``` -# 查看页面所有可点击元素(a/button) -agent-browser eval "Array.from(document.querySelectorAll('a, button')).forEach(el => console.log(el.tagName, el.textContent?.trim().substring(0,30), el.href || ''))" +检查表格行数: -# 检查元素是否存在 -agent-browser eval "console.log('found:', document.querySelectorAll('tr').length, 'rows')" +```bash +flocks browser -c ' +attach_tab("") +print(js(""" +(() => { + const rows = document.querySelectorAll("tbody tr"); + return { + rowCount: rows.length, + firstRowHtml: rows[0]?.outerHTML?.slice(0, 200) || "", + }; +})() +""")) +' ``` --- @@ -143,21 +248,28 @@ agent-browser eval "console.log('found:', document.querySelectorAll('tr').length ## 5. 截图保存 ```bash -# 截取当前视口 -agent-browser screenshot +flocks browser -c ' +attach_tab("") +capture_screenshot("/tmp/tdp-shot.png", max_dim=1800) +print("/tmp/tdp-shot.png") +' +``` -# 截取完整页面(包含滚动内容) -agent-browser screenshot --full +完整页面截图: -# 带标注的截图(显示元素编号,用于确认 ref) -agent-browser screenshot --annotate +```bash +flocks browser -c ' +attach_tab("") +capture_screenshot("/tmp/tdp-shot-full.png", full=True, max_dim=2200) +print("/tmp/tdp-shot-full.png") +' ``` --- ## 6. 注意事项 -- **等待时间**:执行 `eval` 前确保 DOM 渲染完成,页面交互后用 `wait --load networkidle` 或 `wait ` -- **文本匹配精度**:匹配词要足够精确,避免误触相似元素;`textContent` 包含子元素文本,注意嵌套层级 -- **refs 失效**:每次滚动、点击或页面变化后 `@eN` refs 都会失效,必须重新 `snapshot -i` -- **获取页面内容**:`agent-browser get text body` 获取纯文本;`snapshot -i` 获取可交互元素列表 +- **等待时间**:页面交互后用 `wait(0.5 ~ 2.0)`,然后再读取 `page_info()` 或 `js(...)`。 +- **文本匹配精度**:匹配词要足够精确,避免误触相似元素;`textContent` 包含子元素文本,注意嵌套层级。 +- **获取页面内容**:优先用 `js("document.body.innerText.slice(...)")` 获取纯文本;需要结构化信息时,直接在页面内组装 JSON 返回。 +- **每次页面变化后都重新观察**:点击、滚动、切 tab、弹窗打开/关闭后,之前读取到的 DOM 状态都可能失效。 diff --git a/.flocks/plugins/skills/tdp-use/references/browser-workflow.md b/.flocks/plugins/skills/tdp-use/references/browser-workflow.md index 1c8ca8c14..9302dd4f1 100644 --- a/.flocks/plugins/skills/tdp-use/references/browser-workflow.md +++ b/.flocks/plugins/skills/tdp-use/references/browser-workflow.md @@ -1,6 +1,9 @@ # TDP 威胁检测平台浏览器自动化 +> 本文档统一按 `browser-use` 的 `cdp-direct` 流程执行。进入浏览器模式后,先跑 `flocks browser --doctor`;doctor 通过后只使用 `flocks browser`。 +> 对后台任务 / 定时任务,或系统不支持可视化,使用 `browser-use` 的 `cdp-headless` 模式。 + ## 零、登录认证 State 文件路径:`~/.flocks/browser/tdp/auth-state.json`(固定,全局唯一)。 @@ -8,20 +11,55 @@ State 文件路径:`~/.flocks/browser/tdp/auth-state.json`(固定,全局 ### 首次登录 / Session 过期重新登录 ```bash -# 用 --headed 打开浏览器,人工完成登录(含短信验证码/MFA等) -agent-browser close -agent-browser --headed open "https:///login" +flocks browser --doctor +``` + +如果 `flocks browser --doctor` 提示浏览器已运行,但 daemon 或 active browser connection 不可用,必须直接提示用户: + +```text +browser: not connected — 请确保 Chrome / Chromium / Edge 已打开,然后访问对应浏览器的 inspect 页面(例如 chrome://inspect/#remote-debugging 或 edge://inspect/#remote-debugging)并勾选 Allow remote debugging +``` + +然后等待用户进一步指示,不要直接操作。 + +当用户确认已开启 remote debugging 后: + +1. 执行 `flocks browser --setup` 触发交互式 attach,不要用短超时包装该命令。 +2. 再运行 `flocks browser --doctor` 做只读确认。 +3. 如果还失败,先执行 `flocks browser --reload` 清理旧 daemon,再重新执行 `flocks browser --setup`,避免因为残留 daemon 造成干扰。 +4. 只有随后 `--doctor` 通过后,才继续后面的登录或页面操作。 + +```bash +flocks browser -c ' +tid = new_tab("https:///login", activate=True) +wait_for_load() +print(tid) +print(page_info()) +' ``` -等待用户登录结束,收到通知后继续 +等待用户登录结束,收到通知后继续: + ```bash -# 登录成功后立即保存 state -agent-browser state save ~/.flocks/browser/tdp/auth-state.json +flocks browser state save ~/.flocks/browser/tdp/auth-state.json ``` +### 已知用户名 / 密码时的自动登录流程 + +适用前提:登录页是标准账号密码表单,且只有协议勾选,不涉及验证码、短信验证码、滑块、多因子认证或人工确认。 + +1. 使用 `flocks browser -c '...'` 打开 `/login` 页面后,先执行 `page_info()`,确认用户名输入框、密码输入框、协议 checkbox 和登录按钮的 DOM 结构。 +2. 填写账号密码时,优先使用原生 `HTMLInputElement.prototype.value` setter 赋值,避免被 React / Angular 一类前端框架拦截,导致页面看起来有值但内部状态未更新。 +3. 勾选协议 checkbox 后,对相关输入控件补发 `input` 和 `change` 事件,确保页面的响应式绑定同步生效。 +4. 确认表单状态正常后,点击登录按钮提交。 +5. 等待约 3 秒,再次执行 `page_info()` 检查是否已跳转到 `/dashboard`;只有 URL 确认跳转后,才视为登录成功。 +6. 登录成功后,立即执行 `flocks browser state save ~/.flocks/browser/tdp/auth-state.json` 保存 session,供后续 CLI 和浏览器复用。 +7. 如果页面没有跳转、按钮不可点击,或者出现验证码 / 二次确认,则停止自动登录流程,改为人工登录,不要在同一方式上重复重试。 + ### CLI 认证失败时的恢复流程 当 CLI 调用出现以下任一情况,优先判定为认证问题(**不要立刻要求用户重新登录**): + - 返回 HTTP `401` / `403` - 返回内容包含 `Unauthorized`、`login`、未登录、认证失败 - `auth-state.json` 存在,但 CLI 请求仍失败 @@ -29,23 +67,19 @@ agent-browser state save ~/.flocks/browser/tdp/auth-state.json **恢复步骤(最多尝试 1 次)**: ```bash -# 1) close 并重新加载 state(强制刷新浏览器会话) -agent-browser close -agent-browser state load ~/.flocks/browser/tdp/auth-state.json - -# 2) 访问受保护页面验证 -agent-browser open "https:///dashboard" -agent-browser wait --load networkidle +flocks browser state load ~/.flocks/browser/tdp/auth-state.json --url "https:///dashboard" +``` -# 3) 根据结果决策 -URL=$(agent-browser get url) +```bash +URL=$(flocks browser -c ' +info = page_info() +print(info.get("url", "")) +' | tail -n 1) if [[ "$URL" == *"/login"* ]]; then echo "Session 仍无效,需重新登录" - # → 走上方「首次登录 / 重新登录」流程 else - agent-browser state save ~/.flocks/browser/tdp/auth-state.json + flocks browser state save ~/.flocks/browser/tdp/auth-state.json echo "Session 已恢复,可重试 CLI" - # → 重试一次 CLI;若仍失败,再走重新登录,不要无限循环 fi ``` @@ -56,9 +90,12 @@ fi **进入页面首选直接拼接 URL**(比菜单点击更稳定): ```bash -agent-browser open "https:///" -agent-browser wait --load networkidle -agent-browser get text body +flocks browser -c ' +tid = new_tab("https:///", activate=True) +wait_for_load() +print(page_info()) +print(js("document.body.innerText.slice(0, 2000)")) +' ``` | 模块 | 子功能 | URL 路径 | 主要用途 | @@ -89,8 +126,6 @@ agent-browser get text body | | 攻击者分析 | `/attacker` | 分析特定攻击者IP的攻击行为全貌 | | **处置** | 取证溯源 | `/endpoint_forensics` | 对失陷主机发起取证,获取终端行为记录 | ---- - > 如找不到功能入口或遇到 404,查阅 [tdp-menu.md](tdp-menu.md) 获取完整 URL。 ## 二、数据查询与调查 @@ -112,66 +147,68 @@ agent-browser get text body - 用户说"**威胁事件**"、"**攻击事件**"、"**有哪些事件**" → 走 `事件查询` - 未明确区分时:**默认走 `告警日志查询`**,除非用户明确要求"事件聚合"或"事件总览" ---- - ### 2.1 告警日志调查(原始告警日志,SQL查询) 查的是每一条原始检测记录,而非聚合后的事件。适合:精细条件筛选、查某事件关联的所有原始告警、分析某 IP 的完整行为轨迹。 #### 默认方式:CLI 直接调用 API(推荐) -比浏览器操作更稳定,**优先用此方式**。默认返回的是与页面列表一致的常用字段;只有在**明确指定 `--full`**,或**按指定告警 `threat.id` 拉单条详情**时,才使用全字段模式: - ```bash THREATBOOK_BASE_URL=https:// \ THREATBOOK_COOKIE_FILE=~/.flocks/browser/tdp/auth-state.json \ THREATBOOK_SSL_VERIFY=false \ -python scripts/tdp_cli.py \ +uv run python scripts/tdp_cli.py \ logs search [--sql ""] [时间参数] [--limit <条数>] ``` -**时间参数**(三选一,优先级从高到低): -- `--from "2026-03-10 09:00" --to "2026-03-10 18:00"`:指定任意时间段(支持 `时间戳 / 日期 / 日期+时间`,UTC+8) -- `--hours 6`:最近 N 小时 -- `--days 3`:最近 N 天(默认 1) - -**其他参数**: -- `--sql` / `-s`:SQL 过滤条件(也可作为位置参数传入) -- `--limit`:显示条数,默认 20 -- `--net-data-type`:流量类型,默认 `attack risk action`,可多次指定 -- `--full` / `-f`:全字段模式。**仅在明确要求完整字段,或按 `threat.id` 查询单条告警详情/分析告警时使用** -- 默认输出原始 JSON;如需 Rich 表格输出,显式加 `--table-output` - -**使用原则**: -- 常规批量筛查、统计、溯源入口定位:使用默认字段模式,不开 `--full` -- 只有用户明确要求“完整字段/全部字段/详情模式”,或你已经拿到具体 `threat.id` 要查看单条告警详情/分析告警时,才加 `--full` - -常见示例: -```sql --- 查询攻击成功告警 -threat.level = 'attack' AND threat.result = 'success' -``` - > CLI 用法和查询示例见 [cli-reference.md](cli-reference.md) - #### 备用方式:浏览器操作(需查看 PCAP / 原始报文时使用) ```bash -agent-browser open "https:///investigation/logquery" -agent-browser wait --load networkidle -# 点击"高级查询" -agent-browser find text "高级查询" click -agent-browser wait --load networkidle -# 填入查询语句并执行 -agent-browser find placeholder "请输入查询语句" fill "查询语句" -agent-browser find text "查询" click -agent-browser wait --fn "document.querySelectorAll('tbody tr').length > 0" +flocks browser -c ' +tid = new_tab("https:///investigation/logquery", activate=True) +wait_for_load() +print(tid) +print(page_info()) +print(js("document.body.innerText.slice(0, 1200)")) +' ``` -点击具体日志条目可查看完整的 HTTP 请求/响应、PCAP 原始报文等详细信息。 +执行高级查询的推荐方式,是直接用 `js(...)` 选择按钮和输入框,而不是再依赖 `snapshot/@eN/find`: ---- +```bash +flocks browser -c ' +attach_tab("") +js(""" +Array.from(document.querySelectorAll("button")) + .find(el => el.textContent?.trim() === "高级查询") + ?.click() +""") +wait(1.0) +input_el = js(""" +(() => { + const el = document.querySelector("textarea, input[placeholder*=查询], input[placeholder*=SQL]"); + if (!el) return false; + el.focus(); + el.value = "查询语句"; + el.dispatchEvent(new Event("input", {bubbles: true})); + el.dispatchEvent(new Event("change", {bubbles: true})); + return true; +})() +""") +print({"input_ready": input_el}) +js(""" +Array.from(document.querySelectorAll("button")) + .find(el => el.textContent?.trim() === "查询") + ?.click() +""") +wait(1.5) +print(js("document.body.innerText.slice(0, 2000)")) +') +``` + +点击具体日志条目可查看完整的 HTTP 请求/响应、PCAP 原始报文等详细信息。 ### 2.2 威胁事件查询 @@ -179,57 +216,41 @@ agent-browser wait --fn "document.querySelectorAll('tbody tr').length > 0" #### 默认方式:CLI 直接调用 API(推荐) -比浏览器操作更稳定、返回完整数据,**优先用此方式**: - ```bash THREATBOOK_BASE_URL=https:// \ THREATBOOK_COOKIE_FILE=~/.flocks/browser/tdp/auth-state.json \ THREATBOOK_SSL_VERIFY=false \ -python scripts/tdp_cli.py \ +uv run python scripts/tdp_cli.py \ monitor threats [--sql ""] [时间参数] [--limit <条数>] ``` -**时间参数**(三选一,优先级从高到低): -- `--from "2026-03-10 09:00" --to "2026-03-10 18:00"`:指定任意时间段(支持 `时间戳 / 日期 / 日期+时间`,UTC+8) -- `--hours 6`:最近 N 小时 -- `--days 3`:最近 N 天(默认 1) - -**其他参数**: -- `--sql` / `-s`:SQL 过滤条件,默认 `(threat.level IN ('attack')) AND threat.type NOT IN ('recon')` -- `--limit` / `-l`:显示条数(等同于 `--page-size`),默认 20 -- `--page` / `--page-size`:分页控制,默认第1页每页20条 -- 默认输出原始 JSON;如需 Rich 表格输出,显式加 `--table-output` - - > CLI 用法和查询示例见 [cli-reference.md](cli-reference.md) - #### 备用方式:浏览器操作(需交互式探索或查看页面详情时使用) -> 当查询涉及时间范围时,优先通过 URL 操作,需要把用户要求转为时间戳查询(UTC+8时区)。 -> 示例:`?time_from=&time_to=` +当查询涉及时间范围时,优先通过 URL 操作,把用户要求转为时间戳查询(UTC+8 时区),例如: + +`https:///threatMonitor?time_from=&time_to=` ```bash -agent-browser open "https:///threatMonitor" -agent-browser wait --load networkidle -# 点击"高级查询",ref 以实际 snapshot 为准 -agent-browser snapshot -i -agent-browser click @eN +flocks browser -c ' +tid = new_tab("https:///threatMonitor", activate=True) +wait_for_load() +print(tid) +print(page_info()) +print(js("document.body.innerText.slice(0, 1600)")) +' ``` -#### 特定类型威胁事件入口(浏览器) +需要展开筛选或点进详情时,优先写成明确的 JS 片段,例如按文字点“高级查询”或点第一行数据,具体模式见 [browser-tips.md](browser-tips.md)。 -**外部攻击事件**(外部对内网的攻击) -→ `/attack`(按事件聚合视图)或 `/incidents/external`(原始事件列表) - -**内网渗透事件**(内网主机之间的横向移动) -→ `/lateralconverge`(按事件聚合视图)或 `/incidents/lateral`(原始事件列表) +#### 特定类型威胁事件入口(浏览器) -**失陷破坏**(已失陷主机的对外通信) -→ `/incidents/compromise` +**外部攻击事件**:`/attack`(按事件聚合视图)或 `/incidents/external`(原始事件列表) ---- +**内网渗透事件**:`/lateralconverge`(按事件聚合视图)或 `/incidents/lateral`(原始事件列表) +**失陷破坏**:`/incidents/compromise` ### 2.3 资产查询 @@ -237,12 +258,12 @@ agent-browser click @eN | 功能 | URL | 说明 | |------|-----|------| -| 全部服务 | `/asset/serviceList` | 服务汇总(数据库/Web/远程登录/认证等);左侧分类是自定义组件,需用 eval 切换(见三) | +| 全部服务 | `/asset/serviceList` | 服务汇总(数据库/Web/远程登录/认证等);左侧分类为自定义组件,优先用 `js(...)` 切换 | | Web 应用 | `/asset/webapp` | 框架指纹(Spring/Shiro/OA/CMS等)、对外开放、关联 URL | | 数据库资产 | `/asset/serviceList` 左侧"数据库"分类 | 查有哪些数据库服务在运行;查攻击告警用 `logs search "net.app_proto IN ('mysql','redis','oracle') AND threat.level = 'attack'"` | | 域名资产 | `/asset/domains` | 内部域名、解析IP,可过滤是否对外开放 | | 登录入口 | `/asset/loginApi` | 所有暴露的认证入口(SSH/RDP/Web/数据库),重点看爆破/弱口令数量 | -| 主机资产 | `/asset/allDevices` | 全部主机,含服务、Web框架、开放端口 | +| 主机资产 | `/asset/allDevices` | 全部主机,含服务/Web框架、开放端口 | | 上传接口 | `/asset/uploadApi` | 重点关注"对外开放"和"存在风险"标记 | | API | `/risk/api` | 含敏感信息的 API(身份证/银行卡/AK·SK) | @@ -254,92 +275,46 @@ agent-browser click @eN | 弱口令 | `/asset/weakPwd` | 重点关注**登录结果为"成功"**的条目(已被利用) | | 敏感信息 | `/asset/sensitive` | 传输中检测到的敏感数据,可按身份证/手机/邮箱过滤 | ---- - ## 三、浏览器操作原则 > 浏览器只用于以下场景:查看 PCAP / 原始报文 / 页面详情,或 CLI 无法完成的交互式探索。复杂定位、调试、截图时再阅读 [browser-tips.md](browser-tips.md)。 -1. **优先直接拼 URL**:TDP 是 SPA,`agent-browser open "https:///"` 比菜单点击更稳定。 -2. **定位优先级**:优先使用 `find`;`find` 不适用或失败时再用 `eval`。不要在同一个目标上反复重复 `click`。 -3. **自定义组件处理**:TDP 大量按钮/Tab/折叠项是自定义组件,`click` 失败很常见;失败后立即改用 `eval`,不要继续重试相同点击。 -4. **等待规则**:导航后用 `wait --load networkidle`;动态表格、异步结果、loading 消失等场景优先用 `wait --fn`。 -5. **滚动规则**:只能使用 JavaScript 滚动;`agent-browser scroll` 不能可靠触发 TDP 的动态加载。 -6. **refs 生命周期**:每次点击、滚动或页面变化后,`@eN` refs 都会失效,必须重新 `snapshot -i`。 -7. **需要继续下钻时的判断**:如果列表内容被截断、页面有大量空白、出现“加载更多”,或还没看到预期数据,应继续滚动、展开或进入详情,而不是基于当前摘要下结论。 +1. **优先直接拼 URL**:TDP 是 SPA,`new_tab("https:///")` 比菜单点击更稳定。 +2. **定位优先级**:先 `page_info()`,再 `js(...)` 读取 DOM 和页面文本;能稳定定位 DOM 时,直接用 `js(...)` 点击或填值。 +3. **自定义组件处理**:TDP 大量按钮 / Tab / 折叠项是自定义组件;不要依赖旧的 ref 语义,优先用 XPath、`querySelectorAll` 或按文字遍历元素。 +4. **等待规则**:导航后先 `wait_for_load()`;页面异步刷新后用 `wait(...)`,然后重新 `page_info()` 或 `js(...)` 验证结果是否出现。 +5. **滚动规则**:只能使用 JavaScript 滚动;滚动后必须重新读取页面状态。 +6. **需要继续下钻时的判断**:如果列表内容被截断、页面有大量空白、出现“加载更多”,或还没看到预期数据,应继续滚动、展开或进入详情,而不是基于当前摘要下结论。 常用最小模板: ```bash -# 导航后等待页面稳定 -agent-browser open "https:///" -agent-browser wait --load networkidle - -# 文本/输入框优先用 find -agent-browser find text "高级查询" click -agent-browser find placeholder "输入查询内容" fill "查询语句" - -# 动态结果优先用 wait --fn -agent-browser wait --fn "document.querySelectorAll('tbody tr').length > 0" +flocks browser -c ' +attach_tab("") +print(page_info()) +print(js("document.body.innerText.slice(0, 1200)")) +' ``` ---- - ## 四、重要提醒 -1. **必须查看告警详情**:列表页只展示摘要,必须点击条目进入详情才能获取完整的 HTTP 请求/响应、原始报文、PCAP 等信息 - -2. **结论必须基于详细数据**:调查和溯源场景中,列表摘要信息不足以支撑结论。需遵循以下原则: - - 在下判断前必须点击进入事件/告警详情,确认攻击结果(success/failed)、payload 内容、原始报文等关键字段 - - 一个事件包含多条告警时,需抽查关键告警明细,不能只看第一条 - - 数据不足或信息不明确时,继续点击获取更多明细,**不基于摘要推断结论** - -3. **等待加载**:导航或操作后,务必执行 `wait --load networkidle` 等待页面完全加载 - -4. **使用 headed 模式**:除非用户明确要求,默认使用 `--headed` 参数: - ```bash - agent-browser --headed open - ``` - -5. **不要主动关闭浏览器**:除非用户明确要求,否则不要执行 `agent-browser close` 关闭当前浏览器会话。 - -6. **查询结果为空时直接告知用户**:若页面显示无数据或表格为空,不要反复调整条件重试,直接告诉用户当前条件下查询结果为空即可。 - - **严禁擅自修改查询条件**:用户要求“最近 1 小时”就只查最近 1 小时,不能自行扩大到最近 6 小时、24 小时或更长时间范围 - - **严禁擅自放宽过滤条件**:不能自行删减 SQL 条件、改关键词、改 IP、改资产范围、改事件类型来“试试看有没有数据” - - 返回结果必须忠实反映用户原始条件;若无数据,直接明确告知“按当前条件未查到结果” - -7. 当查询涉及时间范围时,且使用浏览器操作,优先通过 URL 操作,需要把用户要求转为时间戳查询(UTC+8时区)。 -> 示例:`?time_from=&time_to=` -> -> 时间戳转换: -> ```python -> from datetime import datetime, timezone, timedelta -> utc8 = timezone(timedelta(hours=8)) -> start_local = datetime(2026, 3, 10, 16, 0, 0, tzinfo=utc8) -> end_local = datetime(2026, 3, 10, 17, 0, 0, tzinfo=utc8) -> print(int(start_local.astimezone(timezone.utc).timestamp())) -> print(int(end_local.astimezone(timezone.utc).timestamp())) -> ``` - -8. **Session 管理**:详见[零、登录认证](#零登录认证)。任务开始时先验证 session 有效性再执行任务;CLI 认证失败时先走恢复流程,不要立刻重新登录。 - -9. **禁止连续失败循环**: +1. **必须查看告警详情**:列表页只展示摘要,必须点击条目进入详情才能获取完整的 HTTP 请求 / 响应、原始报文、PCAP 等信息。 +2. **结论必须基于详细数据**:调查和溯源场景中,列表摘要信息不足以支撑结论;一个事件包含多条告警时,需抽查关键告警明细,不能只看第一条。 +3. **不要主动关闭浏览器**:除非用户明确要求,否则不要关闭用户当前浏览器或用户已有 tab。 +4. **查询结果为空时直接告知用户**:若页面显示无数据或表格为空,不要反复调整条件重试。 +5. **Session 管理**:详见[零、登录认证](#零、登录认证)。任务开始时先验证 session 有效性再执行任务;CLI 认证失败时先走恢复流程,不要立刻重新登录。 +6. **禁止连续失败循环**: - 同一个目标操作最多尝试 **3 次** - - 第一次失败后,必须更换方法(例如 `click` 改 `eval`),不要重复同样操作 + - 第一次失败后,必须更换方法,不要重复同样操作 - 同一页面连续失败达到 **5 次**,直接停止本页面操作,不再继续尝试 - **以下错误属于需要用户干预的基础设施问题,立即停止所有重试,直接告知用户处理**: - - `ERR_CERT_AUTHORITY_INVALID`:TDP 站点证书不被本机信任,使用--ignore-https-errors 或 请求用户处理。 - - `ERR_NAME_NOT_RESOLVED`:TDP 域名无法解析。告知用户确认域名是否正确,或检查 DNS / hosts 配置。 + - `ERR_CERT_AUTHORITY_INVALID`:TDP 站点证书不被本机信任,请求用户处理 + - `ERR_NAME_NOT_RESOLVED`:TDP 域名无法解析,告知用户确认域名是否正确,或检查 DNS / hosts 配置 ## 附加资源 - **CLI 参考**:[cli-reference.md](cli-reference.md) - - 威胁事件查询(`monitor threats`)和告警日志搜索(`logs search`)完整说明,含请求体结构、参数说明、SQL 字段速查表、常用查询示例 - **事件详情查看与分析**:[event-detail.md](event-detail.md) - - 浏览器点击进入事件/告警详情的操作路径、告警分析要点(攻击结果/payload/PCAP) - **TDP 完整菜单结构与 URL 映射**:[tdp-menu.md](tdp-menu.md) - - 遇到 404、找不到功能入口时查阅,涵盖所有模块的完整路径 - **高级查询字段和操作符说明**:[instruction.md](instruction.md) - - TDP 官方字段定义和操作符说明(60+ 字段原始文档) - **浏览器操作详细技巧**:[browser-tips.md](browser-tips.md) - - 动态元素点击进阶方法、调试技巧、截图操作 diff --git a/.flocks/plugins/skills/tdp-use/references/event-detail.md b/.flocks/plugins/skills/tdp-use/references/event-detail.md index 10979a66e..359b05d4e 100644 --- a/.flocks/plugins/skills/tdp-use/references/event-detail.md +++ b/.flocks/plugins/skills/tdp-use/references/event-detail.md @@ -4,28 +4,44 @@ ## 操作路径 -**第一步:点击事件/告警表格行进入详情** +**第一步:点击事件 / 告警表格行进入详情** ```bash -agent-browser eval "document.querySelectorAll('tbody tr')[0]?.click()" -agent-browser wait --load networkidle -agent-browser get text body +flocks browser -c ' +attach_tab("") +js("document.querySelectorAll(\"tbody tr\")[0]?.click()") +wait(1.0) +print(page_info()) +print(js("document.body.innerText.slice(0, 2000)")) +' ``` -**第二步:若页面有"查看详情"链接,继续进入告警明细列表** +**第二步:若页面有“查看详情”链接,继续进入告警明细列表** ```bash -agent-browser eval "Array.from(document.querySelectorAll('a')).find(a => a.textContent.includes('查看详情'))?.click()" -agent-browser wait --load networkidle +flocks browser -c ' +attach_tab("") +js(""" +Array.from(document.querySelectorAll("a")) + .find(a => a.textContent?.includes("查看详情")) + ?.click() +""") +wait(1.0) +print(page_info()) +' ``` -**第三步:滚动到"威胁明细"表格,点击具体告警行查看原始报文/PCAP** +**第三步:滚动到“威胁明细”表格,点击具体告警行查看原始报文 / PCAP** ```bash -agent-browser eval "window.scrollTo(0, document.body.scrollHeight)" -agent-browser eval "document.querySelectorAll('tbody tr')[0]?.click()" -agent-browser wait --load networkidle -agent-browser get text body +flocks browser -c ' +attach_tab("") +js("window.scrollTo(0, document.body.scrollHeight)") +wait(0.8) +js("document.querySelectorAll(\"tbody tr\")[0]?.click()") +wait(1.0) +print(js("document.body.innerText.slice(0, 2500)")) +' ``` ## 告警详情分析要点 @@ -43,6 +59,6 @@ agent-browser get text body ## 重要原则 -- 列表摘要不足以支撑结论,调查和溯源场景中必须进入详情 -- 一个事件包含多条告警时,要抽查关键告警明细,不能只看第一条 -- 数据不足时继续深挖,不基于摘要推断结论 +- 列表摘要不足以支撑结论,调查和溯源场景中必须进入详情。 +- 一个事件包含多条告警时,要抽查关键告警明细,不能只看第一条。 +- 数据不足时继续深挖,不基于摘要推断结论。 diff --git a/flocks/browser/admin.py b/flocks/browser/admin.py index b22955c2e..04e167007 100644 --- a/flocks/browser/admin.py +++ b/flocks/browser/admin.py @@ -487,7 +487,7 @@ def row(label: str, ok: bool, detail: str = "") -> None: browser_running, "" if browser_running else "start Chrome, Chromium, or Edge and rerun `flocks browser --setup`", ) - row("daemon alive", daemon, "" if daemon else "run `flocks browser --setup` to attach") + row("daemon alive", daemon, "" if daemon else "not running; run `flocks browser --setup` to attach") row("active browser connections", bool(connections), str(len(connections))) for conn in connections: page = conn.get("page") diff --git a/flocks/server/routes/provider.py b/flocks/server/routes/provider.py index 53e579a27..34976bb36 100644 --- a/flocks/server/routes/provider.py +++ b/flocks/server/routes/provider.py @@ -1714,7 +1714,7 @@ async def delete_provider_credentials(provider_id: str): Provider._providers.pop(provider_id, None) log.info("provider.credentials.deleted", {"provider_id": provider_id}) - return {"success": True, "cleared_defaults": affected_defaults} + return {"success": True, "cleared_defaults": bool(affected_defaults)} except HTTPException: diff --git a/flocks/tool/file/edit.py b/flocks/tool/file/edit.py index adfa434b6..abc0566cc 100644 --- a/flocks/tool/file/edit.py +++ b/flocks/tool/file/edit.py @@ -270,12 +270,12 @@ def _count_occurrences( def _get_not_found_error(filepath: str, edit_index: int, total_edits: int) -> str: if total_edits == 1: return ( - f"Could not find the exact text in {filepath}. " - "The oldString must match exactly including all whitespace and newlines." + f"Could not find oldString in {filepath}. " + "Re-read the file and provide a slightly larger unique snippet from the current file contents." ) return ( f"Could not find edits[{edit_index}] in {filepath}. " - "The oldString must match exactly including all whitespace and newlines." + "Re-read the file and provide a slightly larger unique snippet from the current file contents." ) diff --git a/tests/tool/test_tools.py b/tests/tool/test_tools.py index e35e29cdc..0d5b03f72 100644 --- a/tests/tool/test_tools.py +++ b/tests/tool/test_tools.py @@ -1451,6 +1451,8 @@ async def test_edit_not_found(self, tool_context, temp_dir): assert not result.success assert "could not find" in result.error.lower() + assert "re-read the file" in result.error.lower() + assert "slightly larger unique snippet" in result.error.lower() @pytest.mark.asyncio async def test_edit_create_new_file(self, tool_context, temp_dir):