redact-for-public-disclosure
关于
This skill redacts reverse-engineering findings for public disclosure, preserving methodology and generalizable patterns while removing sensitive details. It implements a secure workflow using a private/public repo split, deny-list patterns, and orphan-commit publishing to prevent `git log` leaks. Use it to safely publish CLI tool findings, upstream proposals, or archive research via a `check-redaction.sh` CI gate.
快速安装
Claude Code
推荐npx skills add pjt222/agent-almanac -a claude-code/plugin add https://github.com/pjt222/agent-almanacgit clone https://github.com/pjt222/agent-almanac.git ~/.claude/skills/redact-for-public-disclosure在 Claude Code 中复制并粘贴此命令以安装该技能
技能文档
Redact for Public Disclosure
Split reverse-eng research repo → private source-of-truth + public-disclosure subset via redaction checker, deny-lists, orphan-commit publish. Methodology travels; specific findings stay private.
Use When
- Publish methodology findings re: closed-source CLI harness you integrate w/
- Prep upstream proposal/bug report → project not yours
- Archive private research repo → public ref
- Promote investigation notes (Phase 1-4) → public guide
- Establish publish pipeline before findings stack → leak risk no backup
- Clean up after near-miss draft almost shipped sensitive id
In
- Required: Private research repo w/ mixed-sensitivity (source of truth)
- Required: Target public mirror (separate repo, or
public/worktree) - Optional: Existing draft slated for publication
- Optional: Version-lag policy (default "current + 1 prior stay private")
- Optional: List of vendor ids, flag prefixes, namespaces known sensitive
Do
Step 1: Categorize Each Fact
Before write/promote, sort each fact → 1 of 4 categories. Determines if/when ships.
| Category | Definition | Shareable? |
|---|---|---|
| methodology | The how of investigation, independent of any specific finding | Always |
| generic pattern | Class-level observations (e.g., "harnesses commonly use a single-prefix flag namespace") | Yes |
| version-specific finding | Concrete observation tied to a specific release (e.g., "in vN.M, the gate defaults off") | Only after the version-lag cool-off |
| live internal | Minified names, byte offsets, dark flag names, current-version gate logic, PRNG/salt constants, internal codenames | Never |
Annotate each draft section, capture log, note → category before review for publication. Section mixing categories splits → methodology lifts clean, rest private.
→ Each fact has category. Public drafts contain only methodology + generic-pattern (+ version-specific older than cool-off).
If err: fact resists categorization → treat as live internal default. Re-categorize only after explicit review vs version-lag policy.
Step 2: Set Version-Lag Cool-Off
Decide upfront how many versions sit between "current" + "shareable". Two typical: current + 1 prior private, older patterns may discuss. Write to private repo (REDACTION_POLICY.md) → no re-derive later.
# Redaction Policy
Version-lag cool-off: **2 releases**.
- Current release (vN): all version-specific findings PRIVATE.
- Previous release (vN-1): all version-specific findings PRIVATE.
- Releases vN-2 and earlier: version-specific findings may move to public draft after Step 5 review.
Source of truth for "current": output of `monitor-binary-version-baselines`.
Owner: <name>. Reviewed quarterly.
"Current" must be empirical (read from installed binary), not admin. Tie policy to baseline scanner, not calendar.
→ Committed REDACTION_POLICY.md in private repo w/ explicit cool-off + owner.
If err: stakeholders disagree → default most conservative. Cool-offs shorten later; recall leak cannot.
Step 3: Build Deny-List Scanner
Maintain patterns in single executable script = source of truth for redaction policy. Lives in private repo (tools/check-redaction.sh), runs vs public mirror.
#!/usr/bin/env bash
set -u
PUBLIC_REPO="${1:-./public}"
LEAKS=0
PATTERNS=(
"minified identifier shape|<regex matching short bundle-style identifiers>"
"vendor-prefixed flag|<regex matching the vendor's flag prefix>"
"PRNG/salt constant|<regex matching the specific constants>"
)
for entry in "${PATTERNS[@]}"; do
desc="${entry%%|*}"
pattern="${entry##*|}"
if rg -q "$pattern" "$PUBLIC_REPO"; then
echo "LEAK: $desc"; LEAKS=$((LEAKS+1))
fi
done
exit $LEAKS
Each entry: human label + regex. One per sensitive id shape (not literal — shapes survive version churn). Exit code = leak count; clean exits 0.
→ tools/check-redaction.sh ./public-mirror runs <1s on small repo, exits 0 when no match.
If err: no rg → fall back grep -rqE. Patterns too broad (every run leaks) → narrow source not suppress.
Step 4: Maintain Deny-List Before Drafting
When Phase 1-4 finding could leak via draft → extend scanner before draft. Drafts cheap; teaching scanner durable.
Workflow:
- New finding lands in private repo (e.g., new flag prefix)
- Ask: "If leaked, what scanner catch?"
- Add pattern entry to
tools/check-redaction.sh(label + regex) - Run scanner vs entire public mirror → confirm new pattern not tripped by legit content
- Only then draft public content touching area
Inverts usual order: scanner first, draft second. Scanner = executable spec of "too sensitive to publish", draft can't outpace.
→ Pattern entries in tools/check-redaction.sh predate any public-mirror content matching. git log tools/check-redaction.sh shows scanner updates landing before related drafts.
If err: scanner lags drafts → audit public mirror vs new pattern immediately. Redact, then commit scanner update w/ note.
Step 5: Private/Public File-Set Split
Define explicit allow-list of files syncing to public mirror. New files default private; promotion needs redaction-check clearance.
# tools/public-allowlist.txt
README.md
LICENSE
guides/methodology-overview.md
guides/category-classification.md
docs/contributing.md
tools/sync-to-public.sh reads allow-list, copies only those files, exits non-zero if missing file (catches typos).
#!/usr/bin/env bash
set -eu
PRIVATE_ROOT="${1:?private repo path required}"
PUBLIC_ROOT="${2:?public mirror path required}"
ALLOWLIST="$PRIVATE_ROOT/tools/public-allowlist.txt"
while IFS= read -r path; do
[ -z "$path" ] && continue
case "$path" in \#*) continue ;; esac
src="$PRIVATE_ROOT/$path"
dst="$PUBLIC_ROOT/$path"
if [ ! -e "$src" ]; then
echo "MISSING: $path"; exit 2
fi
mkdir -p "$(dirname "$dst")"
cp -a "$src" "$dst"
done < "$ALLOWLIST"
Promotion needs 3 in order: file added to allow-list, passes redaction check, reviewer confirms category labels (Step 1).
→ Public mirror = exactly files in tools/public-allowlist.txt. No file in mirror missing from allow-list.
If err: file in mirror missing allow-list → leak event. Investigate arrival, remove or formally promote after review.
Step 6: Publish via Orphan Commit
Public mirror = single git commit --orphan-rooted commit recreated each publish. Prevents git log exposing pre-redaction drafts.
# In the public mirror (separate repo or worktree)
cd /path/to/public-mirror
git checkout --orphan publish-tmp
git rm -rf . # Clear the index
# Sync from private using the allow-list
bash /path/to/private/tools/sync-to-public.sh /path/to/private .
git add -A
git commit -m "Publish: <date>"
git branch -D main 2>/dev/null || true
git branch -m main
git push --force origin main
Public repo git log shows exactly 1 commit. Prior drafts + redaction iter stay in private. No git log -p, git reflog, branch listing on public can recover pre-redaction → never committed there.
→ git log --oneline on public mirror shows single commit per publish. No refs to private repo history (no parent SHAs, merge commits, tags from private) appear.
If err: git push --force rejected (branch protection) → open single-commit PR from clean orphan branch instead. Never solve rejection by pushing private history.
Step 7: Wire CI Gate
Run tools/check-redaction.sh on every commit to public-sync branch. Failed check blocks publish, not warns.
# .github/workflows/redaction-check.yml (in the public mirror repo)
name: redaction-check
on:
push:
branches: [main, publish-*]
pull_request:
branches: [main]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install ripgrep
run: sudo apt-get update && sudo apt-get install -y ripgrep
- name: Fetch redaction scanner
env:
GH_TOKEN: ${{ secrets.PRIVATE_REPO_TOKEN }}
run: |
gh api repos/<org>/<private-repo>/contents/tools/check-redaction.sh \
--jq .content | base64 -d > check-redaction.sh
chmod +x check-redaction.sh
- name: Run scanner
run: ./check-redaction.sh .
2 design choices:
- Scanner pulled from private at CI time → deny-list never lives in public (patterns themselves sensitive — publishing tells reader exactly what to look for)
- Job exits w/ scanner exit code; non-zero blocks workflow
→ Pushes introducing deny-listed pattern fail CI; publish doesn't land. Maintainers see failing label (e.g., LEAK: vendor-prefixed flag) w/o regex itself.
If err: private-repo token can't grant to public CI → embed only minimum-leak portion in public (broad shape patterns not identifying vendor) + run full scanner pre-push from private.
Step 8: False Positives Honestly
When scanner trips on legit content → narrow pattern not add ignore-line. Broad deny-lists w/ local suppressions rot fast — 6 mo later no one remembers why line suppressed, next leak slides past.
Decision tree:
- Match actually safe? Re-categorize via Step 1. If turns out live internal in disguise → redact, no suppress.
- Pattern too broad? Tighten regex so safe content no match. Doc tightening w/ comment in
check-redaction.shlinking case. - Only if 1 + 2 fail — pattern structurally too entangled w/ legit to narrow → single-line suppress w/
# REASON:comment stating why safe. Date comment.
# Bad — mystery suppression
echo "API endpoint pattern" >> ignore.txt
# Good — narrowed pattern with rationale
# Pattern v2: tightened from `\bgate\(` to `\bgate\(['\"][a-z]+_phase` after
# legitimate `gate(true)` calls in our own SDK examples started matching. 2026-04-15.
PATTERNS+=("vendor flag predicate|\\bgate\\(['\"][a-z]+_phase")
→ Each scanner pattern has 0 or 1 inline comment explaining tightening. Suppressions, if any, carry date + rationale.
If err: suppressions accumulate (>1/quarter) → deny-list mis-shaped. Schedule policy review + rebuild patterns from categorized inventory.
Step 9: Periodic Sweeps
Not all redaction incident-driven. Run periodic sweep (monthly typical) → re-categorize recent additions + re-run scanner. Drift catches itself before incident-grade.
Sweep checklist:
- Re-read version-lag policy; confirm empirical "current" unchanged or update
- Audit last month private-repo commits for new uncategorized findings (Step 1)
- Run
tools/check-redaction.shvs public mirror (should still exit 0) - Review scanner patterns added since last sweep — too broad? Tighten if so
- Any version aged past cool-off → ID findings now eligible for promotion
- Confirm
tools/public-allowlist.txtmatches actual public-mirror file set
→ Short sweep log per month in private repo (sweeps/2026-04.md) w/ checklist outcomes + actions.
If err: sweep repeatedly skipped → automate calendar reminder. Sweep keeps finding same drift → workflow upstream is problem. Investigate why categorization skipped at draft time.
Check
- Every public mirror file on
tools/public-allowlist.txt -
tools/check-redaction.sh ./public-mirrorexits 0 -
git log --onelineon public mirror = single orphan commit per publish -
REDACTION_POLICY.mdexists in private repo w/ explicit version-lag cool-off - Every Phase 1-4 finding has category label (methodology/pattern/version/internal)
- Public CI runs scanner on every push; deliberate test pattern fails build
- Deny-list scanner itself does NOT live in public repo
- Most recent monthly sweep log dated <35 days
Traps
- "Just one example to make concrete." Including one specific finding "to ground methodology" = most common leak path. Use synthetic placeholders (
acme_widget_v3,widget_handler_42) — clearly invented, never traceable. git rebase/filter-branchto scrub leak in place on public. Force-push rewritten history still leaves traces in clones + forks. Orphan-commit = structural fix; ad-hoc rewriting not.- Suppressions vs tightening. Scanner w/ 20 suppressions = 0 meaningful coverage. Each suppression = future leak.
- Public CI warns vs fails. Warnings ignored. Gate must block publish (non-zero exit, no merge button).
- Allow-list drift. New private files don't auto belong on allow-list. Default-deny only safe.
- Encryption ≠ redaction. Encoding, hashing, rot13-ing sensitive id + publishing = still publishes (original recoverable). Redact = "does not appear at all."
- Publishing deny-list. Patterns themselves = finding catalog. Reader seeing regex knows exactly what to grep in binary. Keep scanner private; only labels (
LEAK: vendor-prefixed flag) appear in public CI logs. - Private repo as draft pile. Source of truth not scratch space. Same versioning, review, backup as production.
→
monitor-binary-version-baselines— Phase 1, baselines feed version-lag policyprobe-feature-flag-state— Phases 2-3, classification enters pipeline at Step 1conduct-empirical-wire-capture— Phase 4, capture artifacts need redaction before public refsecurity-audit-codebase— both pipelines benefit from deny-list scanningmanage-git-branches— orphan-commit pattern needs branch hygiene
GitHub 仓库
相关推荐技能
himalaya-email-manager
通信这个Claude Skill通过Himalaya CLI工具提供IMAP邮箱管理功能,支持使用自然语言查询搜索、总结和删除邮件。它特别适合开发者快速获取每日邮件摘要和执行批量邮件操作,所有功能都通过Python脚本封装,简化了环境配置和命令执行流程。关键特性包括支持富文本表格输出、多文件夹分类处理,以及完整的Unicode字符和表情符号显示。
imsg
通信imsg是一个macOS命令行工具,让开发者能通过终端直接访问和操作iMessage/SMS。它支持查看聊天列表、获取历史记录、实时监控消息以及发送文本和附件。这个工具特别适合需要自动化处理消息或与Messages.app集成的开发工作流。
internationalization-i18n
通信这个Skill为开发者提供全面的国际化(i18n)和本地化实现指南,适用于构建多语言应用和支持国际用户。它涵盖消息提取、翻译管理、复数规则、日期/时间/数字格式化以及RTL语言支持等关键功能,并集成i18next和gettext等主流库。开发者可借助此Skill快速设置本地化工作流,处理语言切换和区域特定格式需求。
wacli
通信wacli是一个通过命令行管理WhatsApp的工具,支持消息同步、搜索和发送。它通过WhatsApp Web协议工作,适用于需要自动化处理WhatsApp消息或与外部系统集成的开发场景。关键功能包括持续同步消息、历史记录检索以及向指定联系人发送文本和文件。
