关于 openclaw-secret-scanning-maintainer
面向 OpenClaw 仓库维护者的技能,用于处理 GitHub Secret Scanning 警报。它按位置类型编排检测、脱敏、清理评论、通知和关闭流程,并通过脚本避免将明文秘密输出到终端。
主要功能
- 01按位置类型处理警报
- 02检测并脱敏秘密
- 03删除并重建评论
- 04发送通知并关闭警报
- 05生成处理结果摘要
适用场景
- 01清理议题或 PR 中泄露的凭据
- 02移除评论中的秘密并保留脱敏版本
- 03处理提交中的秘密泄露警报
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ openclaw/ openclaw --skill openclaw-secret-scanning-maintainer安装后在 Claude Code 中输入/openclaw-secret-scanning-maintainer确认已加载。
附带文件scripts/secret-scanning.mjs
详细介绍
OpenClaw 机密扫描维护者
仅限维护者使用。 此技能要求具备仓库管理员 / 维护者权限,才能编辑或删除其他用户的评论并解决机密扫描告警。
使用此技能处理来自 https://github.com/openclaw/openclaw/security/secret-scanning 的告警。
**语言规则:**所有通知评论和替换评论必须使用英文书写。
脚本
所有机械操作(API 调用、临时文件管理、安全约束)均由以下脚本处理:
$REPO_ROOT/.agents/skills/openclaw-secret-scanning-maintainer/scripts/secret-scanning.mjs
脚本会强制执行:
- 在所有告警获取中使用
hide_secret=true(标准输出中不含明文机密) - 对所有临时文件使用带随机 UUID 的
mktemp - 对所有正文上传使用
-F body=@file(不使用内联 shell 引号) - 按位置类型对通知模板进行分支处理
- 绝不将
.secret或.body打印到标准输出
整体流程
支持处理单个或多个告警。对于多个告警,按升序处理。
对于每个告警:
- 识别 — 使用
fetch-alert+fetch-content获取元数据和正文 - 判断 — Agent 读取正文文件,判断是否仍存在明文机密,并仅在需要时生成脱敏版本
- 脱敏 — 对 issue/PR 正文执行
redact-body-if-needed;评论则跳过(直接删除) - 清除 — 对评论执行
delete-comment+recreate-comment;无法清除正文历史记录 - 通知 —
notify根据位置类型发布相应模板,除非当前 issue/PR 正文已脱敏 - 解决 —
resolve关闭告警 - 汇总 —
summary打印格式化结果
第 1 步:识别
# List all open alerts
node secret-scanning.mjs list-open
# Fetch specific alert metadata + locations
node secret-scanning.mjs fetch-alert <NUMBER>
# Fetch content for each location (saves body to temp file)
node secret-scanning.mjs fetch-content '<location-json>'
fetch-content 的输出包括:
body_file:包含完整正文内容的临时文件路径author:发布者issue_number/pr_number:所在位置edit_history_count:现有编辑次数type:用于路由的位置类型- 对于
discussion_comment,如果原评论是一条回复,还会包含comment_node_id、discussion_node_id和reply_to_node_id
位置类型路由
| type | 流程 |
|---|---|
issue_comment | 评论:删除+重新创建 |
pull_request_comment | 评论:删除+重新创建 |
pull_request_review_comment | 评论:删除+重新创建 |
discussion_comment | 讨论评论:删除+重新创建(GraphQL) |
issue_body | 正文:原地脱敏 |
pull_request_body | 正文:原地脱敏 |
commit | 仅通知 |
| 其他 | 跳过并报告 |
第 2 步:判断(Agent)
Agent 读取 fetch-content 输出中的正文文件,然后:
- 识别内容中的所有机密(数量可能多于告警所标记的)
- 判断当前正文中是否仍存在任何明文凭据
- 将每个残留机密替换为
[REDACTED <secret_type>]— 不保留部分值,也不保留前缀/后缀 - 将脱敏后的内容保存到新的临时文件中
这是唯一需要语义理解的步骤。其余操作均为机械操作。
对于 issue_body 和 pull_request_body:如果作者已对当前正文进行脱敏,且不再存在明文凭据,不要发布公开通知评论。使用仅维护者可见的解决评论来解决告警,例如:
node secret-scanning.mjs resolve <ALERT_NUMBER> revoked "Current issue/PR body is already redacted; no public notification posted."
这样可以避免新增一条公开指向历史敏感内容的引用。
第 3 步:脱敏
对于评论(issue_comment / PR 评论)
请勿脱敏。 直接跳至第 4 步(删除+重新创建)。在 DELETE 之前执行 PATCH 会产生不必要的编辑历史修订版本。
对于 issue_body / pull_request_body
node secret-scanning.mjs redact-body-if-needed <issue|pr> <NUMBER> <current-body-file> <redacted-body-file> <result-file>
将 fetch-content 的 body_file 用作 <current-body-file>。该命令会将 notify_required 写入 <result-file>,且仅当脱敏后的文件与当前正文不同时才会 PATCH 正文。
第 4 步:清除编辑历史
评论 — 删除并重新创建
对于 issue/PR 评论:
# Delete original (all edit history gone)
node secret-scanning.mjs delete-comment <COMMENT_ID>
# Recreate with redacted content
node secret-scanning.mjs recreate-comment <ISSUE_NUMBER> <body-file>
对于讨论评论(使用 GraphQL):
# Delete original
node secret-scanning.mjs delete-discussion-comment <COMMENT_NODE_ID>
# Recreate with redacted content
node secret-scanning.mjs recreate-discussion-comment <DISCUSSION_NODE_ID> <body-file> [REPLY_TO_NODE_ID]
fetch-content 对 discussion_comment 的输出包含这些命令所需的 comment_node_id 和 discussion_node_id。如果原始讨论评论是回复,输出中还会包含 reply_to_node_id;请传入这个可选的第三个参数,以便脱敏后的替代评论仍位于原线程中。
重新创建的评论应遵循以下格式:
> **Note:** The original comment by @<AUTHOR> has been removed due to secret leakage. Below is the redacted version of the original content.
---
<redacted original content>
issue_body / pull_request_body — 无法清除编辑历史
编辑会创建包含编辑前明文的修订历史。无法通过 API 清除这些历史。
不要公开建议作者删除并重新创建 issue,或关闭并重新打开 PR。这可能会引起他人对历史内容的注意。清除历史的指导仅供维护者使用。
仅输出到维护者终端(绝不可出现在公开评论中):
⚠️ Issue/PR body edit history still contains plaintext secrets.
Contact GitHub Support to purge: https://support.github.com/contact
Request purge of issue/PR #{NUMBER} userContentEdits.
**重要:**绝不可在任何公开评论或 resolution_comment 中提及编辑历史或“已编辑”按钮。
提交
无法清理。通知作者删除分支或强制推送(针对未合并的 PR)。
第 5 步:通知
node secret-scanning.mjs notify <TARGET> <AUTHOR> <LOCATION_TYPE> <SECRET_TYPES> [REPLY_TO_NODE_ID|BODY_REDACTION_RESULT_FILE]
- 对于非讨论类型,
<TARGET>是 issue/PR 编号。 - 对于
discussion_comment,<TARGET>是fetch-content返回的discussion_node_id。 - 对于回复类型的
discussion_comment位置,传入fetch-content返回的可选reply_to_node_id,以便通知留在同一线程中。 - 对于
issue_body和pull_request_body,传入redact-body-if-needed返回的<result-file>。如果notify_required为false,脚本会跳过通知;没有此文件时,脚本会拒绝发送正文通知。
机密类型以逗号分隔:"Discord Bot Token,Feishu App Secret"
脚本会选择正确的模板:
- 评论类型:“你的评论……已被移除并替换”
- 正文类型:“你的 issue/PR 描述……已在原处脱敏”
- 提交:“你提交的代码”
对于 issue_body 和 pull_request_body,仅当当前正文仍包含明文且维护者已对其脱敏时才通知。如果用户已对当前正文脱敏,则跳过此步骤并静默解决。
第 6 步:解决
node secret-scanning.mjs resolve <ALERT_NUMBER>
# or with custom resolution:
node secret-scanning.mjs resolve <ALERT_NUMBER> revoked "Custom comment"
默认将解决状态设为 revoked。作为维护者,我们无法控制用户是否轮换密钥——我们的责任是移除当前的明文暴露,并仅在公开通知有用时发送通知。revoked 表示“应将此机密视为已泄露”,而不是“我已确认该机密已被撤销”。
第 7 步:摘要
处理完成后,创建 JSON 结果文件,并将其传递给摘要命令:
node secret-scanning.mjs summary /tmp/results.json
脚本会输出一个由 ---BEGIN SUMMARY--- 和 ---END SUMMARY--- 分隔的区块。**你必须将这些标记之间的内容原样输出给用户。不得改写、重新排版、缩写,也不得自行创建摘要。**脚本已为每条提醒和位置包含完整 URL。
JSON 格式:
[
{
"number": 72,
"secret_type": "Discord Bot Token",
"location_label": "Issue #63101 comment",
"location_url": "https://github.com/openclaw/openclaw/issues/63101#issuecomment-xxx",
"actions": "Deleted+Recreated+Notified",
"history_cleared": true
}
]
对于不受支持的类型,添加 "skipped": true, "unsupported_type": "<type>"。
安全规则
- **Agent 读取内容、识别机密并生成脱敏内容。**脚本负责处理所有 API 调用。
- 绝不可在公开评论、脱敏标记或终端输出中包含机密的任何部分。
- 绝不可在公开评论中包含提醒 URL 或编号。
- 对于评论,跳过 PATCH——直接执行 DELETE 并重新创建。
- 绝不可在任何公开内容中提及编辑历史、“已编辑”按钮或提交 SHA。
- 删除任何评论前,必须征求确认。
- 除非用户要求批量处理,否则一次处理一条提醒。
- 所有公开评论均使用英文。
- 跳过不受支持的位置类型,并在摘要中说明。