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