Publish gate secrets scanning. Use for: safe_to_publish, scan for secrets, redact in-place. Determines publish gate status. Scan files for secrets (locations only - NEVER prints secret content)...
Secrets scanning and redaction for publish gates. High-risk surface with strict output contract.
Always invoke via the shim:
bash .claude/scripts/demoswarm.sh secrets <command> [options]
Do not set PATH or call helpers directly. The shim handles resolution.
This skill has a strict output contract:
<prefix>...<suffix> (e.g., ghp_...abcd)Violations of this contract are security incidents.
This skill does not run git or gh. File lists are passed as arguments.
Primary:
secrets-sanitizer (the publish gate agent)Secondary (read-only scan):
repo-operator (for hygiene checks)Not allowed:
| Command | Purpose |
|---|---|
secrets scan |
Scan files for secrets (locations only) |
secrets redact |
Redact specific secret type in file |
# Scan a file or directory
bash .claude/scripts/demoswarm.sh secrets scan \
--path ".runs/feat-auth/signal" \
--output ".runs/feat-auth/signal/secrets_scan.json"
# stdout: CLEAN | SECRETS_FOUND | SCAN_PATH_MISSING | PATTERN_ERROR
# JSON findings written to --output file
Output JSON format:
{
"status": "SECRETS_FOUND",
"findings": [
{
"file": ".runs/feat-auth/signal/github_research.md",
"type": "github-token",
"lines": "42,87"
}
],
"skipped_count": 0
}
The skipped_count field indicates how many files/directories were skipped due to I/O errors (permission denied, etc.).
# Scan with verbose mode to see skipped paths
bash .claude/scripts/demoswarm.sh secrets scan \
--path ".runs/feat-auth/signal" \
--output ".runs/feat-auth/signal/secrets_scan.json" \
--verbose
# stderr: Warning: skipped /path/to/file: failed to read file: Permission denied
# stdout: CLEAN
When --verbose (or -v) is enabled, skipped paths and reasons are logged to stderr.
# Scan using additional patterns from a config file
bash .claude/scripts/demoswarm.sh secrets scan \
--path ".runs/feat-auth/signal" \
--output ".runs/feat-auth/signal/secrets_scan.json" \
--patterns-file "secret-patterns.json"
bash .claude/scripts/demoswarm.sh secrets scan \
--path ".runs/feat-auth/signal/github_research.md" \
--output ".runs/feat-auth/signal/secrets_scan.json"
# stdout: CLEAN (if no secrets found)
# Redact GitHub tokens in a file
bash .claude/scripts/demoswarm.sh secrets redact \
--file ".runs/feat-auth/signal/github_research.md" \
--type "github-token"
# stdout: ok | FILE_NOT_FOUND | null
# File is modified in-place
# Redact a custom secret type defined in patterns file
bash .claude/scripts/demoswarm.sh secrets redact \
--file ".runs/feat-auth/signal/config.md" \
--type "custom-api-key" \
--patterns-file "secret-patterns.json"
You can extend the built-in patterns by providing a JSON or YAML configuration file.
{
"patterns": [
{
"pattern": "xoxb-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24}",
"type": "slack-bot-token"
},
{
"pattern": "sq0atp-[0-9A-Za-z\\-_]{22}",
"type": "square-access-token"
}
]
}
patterns:
- pattern: "xoxb-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24}"
type: slack-bot-token
- pattern: "sq0atp-[0-9A-Za-z\\-_]{22}"
type: square-access-token
When --patterns-file is provided:
All custom patterns are validated at load time. If any pattern has invalid regex syntax, the scan will fail with PATTERN_ERROR status and an error message in the output JSON.
| Type | Pattern | Replacement |
|---|---|---|
github-token |
gh[pousr]_[A-Za-z0-9_]{36,} |
[REDACTED:github-token] |
aws-access-key |
AKIA[0-9A-Z]{16} |
[REDACTED:aws-access-key] |
stripe-key |
sk_live_[0-9a-zA-Z]{24,} |
[REDACTED:stripe-key] |
private-key |
-----BEGIN .*PRIVATE KEY----- |
[REDACTED:private-key] |
jwt-token |
eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]* |
[REDACTED:jwt-token] |
CLEAN | SECRETS_FOUND | SCAN_PATH_MISSING | PATTERN_ERROR)ok | FILE_NOT_FOUND | null)--output file path, not stdout0 always (errors expressed in output, not exit code). In CI or human scripts, fail-fast via exit code is appropriate; agents should record PATTERN_ERROR in the JSON and hand off to the orchestrator rather than exiting abruptly.# stdout: SCAN_PATH_MISSING
{
"status": "SCAN_PATH_MISSING",
"findings": [],
"skipped_count": 0
}
# stdout: PATTERN_ERROR
{
"status": "PATTERN_ERROR",
"error": "Invalid regex in patterns file at index 0: pattern='[invalid', type='bad-pattern'",
"findings": [],
"skipped_count": 0
}
# stdout: FILE_NOT_FOUND
# stdout: null
# stdout: null
# stderr: Unknown secret type: <type>
In secrets-sanitizer:
secrets-tools -- bash .claude/scripts/demoswarm.sh secrets ...CLEAN, SECRETS_FOUND, SCAN_PATH_MISSING, or PATTERN_ERROR--output file, not stdoutsecrets redact for allowlist artifactsExample pattern:
# Scan the publish surface
SCAN_OUTPUT=".runs/${RUN_ID}/${FLOW}/secrets_scan.json"
STATUS=$(bash .claude/scripts/demoswarm.sh secrets scan \
--path ".runs/${RUN_ID}/${FLOW}" \
--output "$SCAN_OUTPUT")
if [[ "$STATUS" == "SECRETS_FOUND" ]]; then
# Read findings from JSON file
FINDINGS=$(cat "$SCAN_OUTPUT" | jq -r '.findings[] | "\(.file) \(.type)"')
# Redact each finding type
bash .claude/scripts/demoswarm.sh secrets redact \
--file ".runs/${RUN_ID}/${FLOW}/github_research.md" \
--type "github-token"
fi
# Scan with organization-specific patterns
SCAN_OUTPUT=".runs/${RUN_ID}/${FLOW}/secrets_scan.json"
STATUS=$(bash .claude/scripts/demoswarm.sh secrets scan \
--path ".runs/${RUN_ID}/${FLOW}" \
--output "$SCAN_OUTPUT" \
--patterns-file ".config/secret-patterns.yaml")
# Handle PATTERN_ERROR status
if [[ "$STATUS" == "PATTERN_ERROR" ]]; then
ERROR=$(cat "$SCAN_OUTPUT" | jq -r '.error')
echo "Pattern configuration error: $ERROR"
exit 1
fi
The Rust implementation is preferred:
cargo install --path tools/demoswarm-runs-tools --root .demoswarm
The shim will automatically use the installed binary.