Maintainer-only workflow for handling GitHub Secret Scanning alerts on OpenClaw. Use when Codex needs to triage, redact, clean up, and resolve secret leakage found in issue comments, issue bodies, PR comments, or other GitHub content.
Maintainer-only. This skill requires repo admin / maintainer permissions to edit or delete other users' comments and resolve secret scanning alerts.
Use this skill when processing alerts from https://github.com/openclaw/openclaw/security/secret-scanning.
Language rule: All notification comments and replacement comments MUST be written in English.
All mechanical operations (API calls, temp file management, security enforcements) are handled by:
$REPO_ROOT/.agents/skills/openclaw-secret-scanning-maintainer/scripts/secret-scanning.mjs
The script enforces:
hide_secret=true on all alert fetches (no plaintext secrets in stdout)mktemp with random UUIDs for all temp files-F body=@file for all body uploads (no inline shell quoting).secret or .body to stdoutSupports single or multiple alerts. For multiple alerts, process in ascending order.
For each alert:
fetch-alert + fetch-content to get metadata and bodyredact-body for issue/PR body; skip for comments (delete directly)delete-comment + recreate-comment for comments; cannot purge body historynotify posts the right template per location typeresolve closes the alertsummary prints formatted results# 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>'
The fetch-content output includes:
body_file: path to temp file with full body contentauthor: who posted itissue_number / pr_number: where it isedit_history_count: number of existing editstype: location type for routingdiscussion_comment, it also includes comment_node_id, discussion_node_id, and reply_to_node_id when the original comment was a reply.| type | Flow |
|---|---|
issue_comment | Comment: delete+recreate |
pull_request_comment | Comment: delete+recreate |
pull_request_review_comment | Comment: delete+recreate |
discussion_comment | Discussion comment: delete+recreate (GraphQL) |
issue_body | Body: redact in place |
pull_request_body | Body: redact in place |
commit | Notify only |
| other | Skip and report |
The agent reads the body file from fetch-content output and:
[REDACTED <secret_type>] — no partial values, no prefix/suffixThis is the only step that requires semantic understanding. Everything else is mechanical.
Do NOT redact. Skip directly to Step 4 (delete + recreate). PATCHing before DELETE creates an unnecessary edit history revision.
node secret-scanning.mjs redact-body <issue|pr> <NUMBER> <redacted-body-file>
For issue/PR comments:
# 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>
For discussion comments (uses 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]
The fetch-content output for discussion_comment includes comment_node_id and discussion_node_id for these commands. When the original discussion comment was a reply, it also includes reply_to_node_id; pass that optional third argument so the redacted replacement stays in the original thread.
The recreated comment should follow this format:
> **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>
Editing creates an edit history revision with the pre-edit plaintext. This cannot be cleared via API.
Output to maintainer terminal only (never in public comments):
⚠️ 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.
CRITICAL: Do NOT mention edit history or the "edited" button in any public comment or resolution_comment.
Cannot clean. Notify author to delete branch or force-push (for unmerged PRs).
node secret-scanning.mjs notify <TARGET> <AUTHOR> <LOCATION_TYPE> <SECRET_TYPES> [REPLY_TO_NODE_ID]
<TARGET> is the issue/PR number.discussion_comment, <TARGET> is the discussion_node_id returned by fetch-content.discussion_comment locations, pass the optional reply_to_node_id from fetch-content so the notification stays in the same thread.Secret types are comma-separated: "Discord Bot Token,Feishu App Secret"
The script picks the right template:
node secret-scanning.mjs resolve <ALERT_NUMBER>
# or with custom resolution:
node secret-scanning.mjs resolve <ALERT_NUMBER> revoked "Custom comment"
Resolution is revoked by default. As maintainers we cannot control whether users rotate — our responsibility is to redact + notify. The revoked means "this secret should be considered leaked", not "I confirmed it was revoked".
After processing, create a JSON results file and pass it to the summary command:
node secret-scanning.mjs summary /tmp/results.json
The script outputs a block delimited by ---BEGIN SUMMARY--- and ---END SUMMARY---. You MUST output the content between these markers verbatim to the user. Do NOT rephrase, reformat, abbreviate, or create your own summary. The script already includes full URLs for every alert and location.
The JSON format:
[
{
"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
}
]
For unsupported types, add "skipped": true, "unsupported_type": "<type>".