Activepieces 安全公告分类实践:从 GitHub 私密漏洞积压到可评审报告的完整流水线
2026/9/13 9:45:32 网站建设 项目流程

Activepieces 安全公告分类实践:从 GitHub 私密漏洞积压到可评审报告的完整流水线

【免费下载链接】activepiecesAI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents项目地址: https://gitcode.com/GitHub_Trending/ac/activepieces

导读

本文讲解 Activepieces 仓库中用于分类(Triage)GitHub 私密上报漏洞的完整自动化流程,覆盖从ghCLI 拉取仓库安全公告、按 SECURITY.md 做范围审查、逐条深度验证(定位入口点到 sink 的完整调用链)、SLA 期限计算,到产出每条公告的评审报告与汇总仪表盘的六个步骤。读完你不仅能手动复现这套流水线,还能掌握 Activepieces 特有的高收益漏洞模式清单(如grantAccess空权限放行、TypeORM.where()二次调用覆盖租户隔离等),可直接用于漏洞挖掘与代码审计。

该技能的定义位于 .agents/skills/triage-security-advisories/SKILL.md,它自动化的是人工流程手册 docs/handbook/engineering/playbooks/security-advisory-response.mdx,与处理 Dependabot 依赖告警的triage-dependabot-alerts技能共享同一.security-triage/工作区、隐私规则与 SLA 脚本。

技能定位:两类安全积压的不同分工

Activepieces 的安全积压分两类,由两个技能分别处理:

技能处理对象数据来源
triage-security-advisories仓库安全公告(repository security advisories)GitHub Security 标签页的私密漏洞上报
triage-dependabot-alerts依赖告警(dependency alerts)Dependabot

两者共享.security-triage/工作区、硬性隐私规则与 SLA 脚本(npm run security:sla),但数据源与处理重点不同。本文聚焦前者:每个公告产出 review-ready 报告 + SLA 仪表盘 + 修复方案建议,最终由用户逐条裁决:批准修复 / 判定超范围关闭 / 升级处理。

硬性隐私规则:公共仓库的第一道红线

Activepieces 仓库是公开的,私密/禁运中的(embargoed)公告内容绝不能提交进版本库

  • 所有产物(拉取的 JSON、逐条公告报告、仪表盘)一律写入.security-triage/,该目录已被 gitignore;严禁把公告内容写进任何被跟踪的文件。
  • 每次运行后需确认git status在被跟踪路径下没有新增内容。
  • 修复必须走私有 fork 的security/<ghsa-id>分支(按 playbook 执行),禁运期结束前绝不打开公开 PR。

这条规则贯穿全流程,是后面每一步操作的约束前提。

一次性设置:ghToken 需要 security 权限

仓库安全公告 API 需要带安全范围的 token,否则第一步拉取会返回 403。若遇到 403,让用户执行:

gh auth refresh -s security_events,repo

刷新后重新运行即可。

Step 1 — 拉取公告(确定性步骤)

mkdir -p .security-triage gh api /repos/activepieces/activepieces/security-advisories --paginate > .security-triage/advisories.json

两个高频注意点:

分页拼接的 JSON 数组gh api --paginate可能输出多个拼接的顶层数组(例如先看到长度100、再5)。此时需合并为单数组:jq -s 'add'。SLA 脚本自身也内置了字符串感知的顶层值扫描(见下文源码分析),能直接处理这种拼接格式。

zsh JSON 陷阱(每次都会踩):绝不把公告行通过echo/printf管道给jq——echo "$row" | jq …会破坏内嵌的控制字符/反斜杠,报Invalid string: control characters … must be escaped。正确做法是循环 id,每次都让jq直接读源文件(避免 shell 字符串中转):

mkdir -p .security-triage/input .security-triage/reports for id in $(jq -r '.[] | select(.state=="triage" or .state=="draft") | .ghsa_id' .security-triage/advisories.json); do jq --arg id "$id" '.[] | select(.ghsa_id==$id) | {ghsa_id, cve_id, summary, severity, state, created_at, cvss: .cvss_severities, cwes: .cwe_ids, description}' \ .security-triage/advisories.json > ".security-triage/input/${id}.json" done

然后给每个子代理(subagent)各自的input/<ghsa>.json路径去读——把(私密、禁运中的)公告正文隔离在编排器的上下文和子代理提示词之外。

状态过滤无需手工:SLA 脚本(Step 4)默认排除已解决公告(closed/published/withdrawn)。可操作积压的状态是:

  • triage(新上报、未评估 → 走完整流水线)
  • draft(已接受、修复进行中 → 仅做 SLA 跟踪)

使用--state triage只看未评估,--state all则包含已解决。当用户只要"triage 状态"时,传--state triage并且把上面 input 文件循环也过滤为select(.state=="triage")

Step 2 — 范围审查:对照 SECURITY.md 的 Out of Scope 清单

对每条公告,对照 SECURITY.md 的Out of scope清单检查:

  • 无敏感操作页面上的点击劫持(Clickjacking)
  • 未认证/登出/登录 CSRF
  • 需要 MITM 或物理接触设备的攻击
  • 任何会导致服务中断的活动(DoS)
  • 无攻击向量/无法修改 HTML/CSS 的内容伪造与文本注入
  • 邮件伪造(Email spoofing)
  • 缺少 DNSSEC、CAA、CSP 头
  • 非敏感 cookie 缺少 Secure 或 HTTP only 标志
  • 死链(Deadlinks)
  • UNSANDBOXED执行模式(面向可信运维部署、EE/Cloud 生产环境已阻止)
  • 接受特殊字符但无可利用 sink 的输入字段
  • 能力令牌端点(resume URL、webhook URL、签名文件 URL):令牌即授权,报告必须展示泄露路径(日志、Referer泄露、弱熵)才在范围内
  • 唯一攻击路径是猜测高熵标识符(如 nanoid)且无已证实的泄露来源的发现

若报告命中任一 out-of-scope 项,标记为OUT_OF_SCOPE,并注明 SECURITY.md 的确切条款与推理。

Step 3 — 深度验证:入口点到 sink 的完整取证

不要停在"报告提到了 X"。对每条 in-scope 公告,追踪从入口点到 sink 的完整路径,收集用户能同意或推翻的证据:

  1. 把报告者的file:line和所陈述机制当作线索而非事实。这个代码库产生的每条公告几乎都有漂移引用——重构移动了代码(packages/sharedpackages/core/shared),重写换掉了 sink(axios → 原生fetch、worker → sandbox),行号偏移。要在当前main上按符号/字符串重新定位 sink。漏洞可能是真的但引用位置已过时;报告的根因也可能是错的——即使附近确实有 bug(例如报告的绕过机制其实无关紧要,真正的缺陷在路径别处)。验证的是机制,而不只是"那里看起来不对劲"。
  2. **定位真实 sink(file:line)**以及每个可达它的路由/调用方。
  3. 检查中间的防护,判断漏洞对攻击者是否真正可达,而不只是在源码中存在:
    • 认证:端点上的securityAccess配置(每个端点都必须有)。
    • 租户隔离:查询必须按projectId/platformId过滤(数据隔离规则);connections 使用ArrayContains([projectId])
    • 输入校验(zod schema)、版本门控(platformMustHaveFeatureEnabled、仅 EE 路径)。
    • SSRF:出站 HTTP 应走safeHttp;对用户输入使用裸fetch/axios.create是真实可达 sink。
  4. 默认开启 vs 可选开启、以及哪个版本——这决定真实严重级别:(a) 检查模块在哪个版本注册(app.ts的版本开关——很多 sink 仅 Cloud 或仅 EE);(b) 漏洞路径是否默认启用或躲在环境变量标志后(admin/debug UI 常见);(c) 保护模式是否可选开启(例如只在非默认网络模式运行的 SSRF 防护)。仅在非默认/可选或单一版本配置下可达的真实 bug,严重级别实质性更低——要如实说明。
  5. 确认受影响版本范围与当前main的对比——已修复?重构是否移除了 sink?用git log -S'<sink string>'/git blame找修复提交。若ALREADY_MITIGATED,记录修复提交 SHA + 日期并与公告created_at对比:本仓库中修复落地之后才上报的报告很常见(曾有一个 critical 在修复约 5 周后才被上报)。修复日期驱动关闭消息,并证明仪表盘上的 "BREACHED" 已过时。
  6. 按 playbook 交叉核对 CVSS 4.0 评分输入(攻击向量、权限、用户交互、影响范围、CIA 影响)。分档:0.1–3.9 低,4.0–6.9 中,7.0–8.9 高,9.0–10 严重。
  7. 给出具体的 PoC 草图 / 失败测试大纲,或精确说明为何无法触发。

每条公告必须且仅产出一个 Verdict

Verdict含义
CONFIRMED_EXPLOITABLE按描述可达且可利用。
THEORETICALsink 存在但不可达(有防护 / 未接线)。
ALREADY_MITIGATED已有防护或既有修复阻止。
FALSE_POSITIVE不是真实漏洞。
OUT_OF_SCOPE被 SECURITY.md 排除。

同时标记重复项(同一 sink/根因)和系统性模式(一个修复覆盖多个)。大积压时按每条公告一个子代理并行扇出再汇总;子代理只读,写报告文件(reports/<ghsa>.md)并返回紧凑 verdict 行,绝不改代码。只有用户明确选择多代理编排时才用 Workflow 工具,否则用并行Agent调用。

扇出实战经验

  • 按根因聚类分组公告,给每个子代理其兄弟清单("很可能与 X 同一 sink;交叉核对,只验证你自己的")——这是发现重复项与系统性模式的诀窍,对兄弟项无感知的子代理无法去重。
  • 运行时极不均匀(一次深度历史追踪约 30 分钟,而典型约 3 分钟;子代理还可能为 git 考古再派生子代理)。要求代理优先给出当前main的 verdict,把穷尽历史当作次要任务。
  • 不要用sleep轮询(harness 会拦截)。等待全部报告时,挂一个后台until [ "$(ls .security-triage/reports/*.md | wc -l)" -ge N ]; do sleep 3; done让它在完成时通知,同时收集流式返回的 verdict 通知。

源码佐证:grantAccess的空权限放行

SKILL.md 把securityAccess.project(..., undefined, ...)列为头号 sink pattern——传undefined作为必需权限时,grantAccess()对 nil 权限返回true。该行为可在 rbac-service.ts 中直接验证:

const grantAccess = async ({ principalRoleId, routePermission }: GrantAccessArgs): Promise<boolean> => { if (isNil(routePermission)) { return true } // ...否则检查 principalRole.permissions?.includes(routePermission) }

routePermissionundefined时直接放行,路由退化为仅成员资格——任何项目成员(包括 VIEWER)都能通过。分类时要确认是"存在专门权限却被故意遗漏(路由是 bug)",还是"设计上就是仅成员可读"(例如 piece-metadata 读取),并对比正确接线了权限的兄弟模块。

Activepieces 高收益 sink 模式:"看似有防护实则没有"(优先 grep)

这是本代码库反复出现的典型 footgun,每个都曾以确认公告的形式出现;全库 grep 能抓到系统性簇,而不只是上报的那一条:

  • securityAccess.project(..., undefined, ...):见上文源码佐证,路由坍缩为仅成员资格。
  • securityAccess.publicPlatform([PrincipalType.USER])用在需要platformAdminOnly的状态变更或平台级路由上——publicPlatform设置adminOnly:false,管理员断言永不执行,任何已认证成员都能触达。对比 audit-events / api-keys / signing-keys / global-connections,它们都用platformAdminOnly
  • TypeORM.where()调用两次:第二次.where()替换第一次(应使用.andWhere)。projectId过滤后接.where({ appName })会静默丢弃租户隔离。
  • projectIds @> '[]'::jsonb:Postgres 中空数组 JSONB 包含对每一行都为 TRUE,缺失/为空的projectIds会把租户过滤坍缩成匹配所有行。
  • PLATFORM 认证下信任request.projectId:它只在AuthorizationType.PROJECT下被填充;publicPlatform/PLATFORM 下为undefined,下游把它当 scope 键读取时"fail open"。
  • 无逐事件 RBAC 的 Websocket 处理器:分发器只校验握手projectId;单个addListener处理器信任客户端提供的resourceId/flowVersionId,无逐事件权限或资源→项目归属检查。
  • 出站不走safeHttp:AI-provider/piece 出站调用使用pieces-commonhttpClient(原生fetch/undici)而非safeHttp;用户可控的baseUrl/host/resourceName插值进 URL = SSRF。进程内 dns/socket 防护是可选开启(非默认网络模式)且 best-effort——要验证它们是否真的覆盖了实际使用的传输。
  • 任意位置设置NODE_TLS_REJECT_UNAUTHORIZED='0':进程级、持久的 TLS 校验关闭(无论上报漏洞是什么,本身就是一个 CWE-295 缺陷)。
  • window.opener.postMessage(payload, '*'):通配 targetOrigin 把 payload(OAuth code)泄露给任意 opener 源;接收侧 origin 检查无法缓解发送侧。
  • 仅凭 email 匹配身份:联邦登录(getIdentityByEmail)/仅以 email 为键的全局user_identity,没有 provider/sub/NameID 绑定 = 跨租户账户接管面。

Step 4 — 评分 + SLA(确定性步骤)

npm run security:sla -- --source advisory # 默认排除已解决 npm run security:sla -- --source advisory --state triage # 只看未评估积压 npm run security:sla -- --source advisory --state all # 包含 closed/published

命令定义于 package.json:

"security:sla": "npx ts-node --project tools/tsconfig.tools.json tools/scripts/security/sla-report.ts"

脚本读取.security-triage/advisories.json,计算截止日期与状态,写出.security-triage/sla.json.security-triage/dashboard.md。默认丢弃closed/published/withdrawn公告;--state <csv>覆盖。SLA 时钟从公告创建日期起算:

严重级别修复期限DUE_SOON 阈值
Critical7 天剩余 ≤ 2 天
High30 天剩余 ≤ 7 天
Medium90 天剩余 ≤ 14 天
Lowbest-effort(无硬性期限)

状态桶:BREACHEDDUE_SOONON_TRACKBEST_EFFORTNEEDS_TRIAGE(未评分),按最紧急优先排序。

源码剖析:sla-report.ts 的实现细节

实现位于 tools/scripts/security/sla-report.ts,几个关键点值得注意:

  • SLA 常量(第 4–16 行):SLA_DAYS{critical: 7, high: 30, medium: 90, low: null}DUE_SOON_DAYS{critical: 2, high: 7, medium: 14, low: 0},与 SKILL.md 表格一一对应;low的 SLA 为null直接落BEST_EFFORT
  • 已解决状态集合(第 26 行):closed/published/withdrawn/fixed/dismissed/auto_dismissed——比 SKILL.md 描述的四种更多,默认状态下被过滤。
  • 分页拼接 JSON 的健壮解析(第 117–145 行):脚本自带字符串感知的顶层值扫描器,能直接处理gh api --paginate产生的[...]\n[...]拼接格式,无需手工jq -s 'add'
  • 状态计算(第 222–230 行):daysLeft < 0BREACHEDdaysLeft <= DUE_SOON_DAYS[severity]DUE_SOON;否则ON_TRACK。排序按STATUS_ORDER再按剩余天数升序(最紧急在前)。
  • 未评分/缺日期兜底(第 203–214 行):severity 无法解析或创建日期缺失时归入NEEDS_TRIAGE--now参数支持用固定时间点复现计算。
  • CLI 参数(第 71–110 行):支持--advisories/--dependabot/--out/--now/--source/--state,默认源为advisorydependabot两个 JSON 文件,输出目录默认.security-triage

Step 5 — 报告

先在.security-triage/reports/<ghsa>.md写每条公告的 review-ready Markdown:范围 verdict、有效性 verdict +file:line证据、sink、PoC 草图、上报严重级 vs 评估严重级(深度验证改变严重级别时注明;SLA 仪表盘按 GitHub 上报严重级分桶)、SLA 截止日期 + 状态、建议。

然后写汇总的.security-triage/TRIAGE-SUMMARY.md——这才是用户真正评审的产物,必须包含:

  • Verdict 统计(CONFIRMED_EXPLOITABLE / THEORETICAL / ALREADY_MITIGATED / FALSE_POSITIVE / OUT_OF_SCOPE 各多少)。
  • 按严重级/SLA 状态分组的表格:GHSA、SLA 状态、verdict、sinkfile:line、一行备注。
  • 重复项点名(同一 sink/根因 → 修一次)。
  • 系统性模式——一个修复覆盖多条公告(例如共享的损坏防护)。
  • 深度验证带来的严重级下调/上调。

在聊天中展示 SLA 仪表盘与摘要的头条 verdict 时,要先报CONFIRMED_EXPLOITABLE 且 main 上未修复的计数,而不是裸的 BREACHED 数——仪表盘按 GitHub 上报严重级分桶并把已修复公告混在一起,"24 BREACHED"会严重高估真实积压(一次运行可能有 24 条 BREACHED,但实际可操作的只有约 15 条)。要明确说明 BREACHED 基于上报严重级且包含已缓解项,然后给出按紧急度排序的可操作清单。

Step 6 — 批准后修复(未经用户批准绝不开始)

用户选定要修的公告后,按 playbook 的私有流程执行:

  • 起草补丁 + 一个回归测试(修复前失败 / 修复后通过)。
  • security/<ghsa-id>分支(私有 fork)上暂存,只做 patch 级版本号提升——绝不捆绑功能。禁运期结束前不打开公开 PR。

关闭 / 回复上报者

仓库安全公告 REST API 暴露state(可通过PATCH /repos/activepieces/activepieces/security-advisories/<ghsa-id> -f state=closed关闭),但没有评论端点——对话线程仅存在于 UI。要带理由回复时:先起草消息,让用户粘贴到公告页面,然后再关闭(让上报者看到理由,而不是一条光秃秃的关闭通知)。

输出回顾

所有产物都落在.security-triage/(已 gitignore):advisories.jsonsla.jsondashboard.md,以及每条公告一份报告。私密内容从不提交。

整个流水线把 Activepieces 的人工安全响应 playbook(docs/handbook/engineering/playbooks/security-advisory-response.mdx)自动化成了可重复、可审计、可并行的确定性流程:确定性步骤(拉取、范围审查、SLA 计算)交给脚本,深度验证交给只读子代理,最终裁决权始终保留给用户——这正是处理公共仓库私密漏洞积压的安全姿势。

【免费下载链接】activepiecesAI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents项目地址: https://gitcode.com/GitHub_Trending/ac/activepieces

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询