All skills
simota avatar

/hone

@c805268
by shingo imotasimota/agent-skills85 stars
15

Auditing AI CLI configs and designing, configuring, or debugging Claude Code hooks. Use for Codex/agy/Claude Code config reviews, hook lifecycle automation, quality gates, or MCP governance.

Use this Skill: https://skilld.dev/gh/simota/agent-skills/hone

This session only. Nothing lands on disk.

referencehookssecurity-guard-hook.md

≈2.2k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Security Guard Hook

Reference for Hone's security recipe. PreToolUse hooks for blocking dangerous operations, secret detection, sensitive file protection, and MCP tool ACL.

Defense-in-depth alongside _common/BOUNDARIES.md. Hooks enforce at runtime; agent boundaries inform intent.


1. PreToolUse Decision Model

PreToolUse hook stdin:

{
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash" | "Write" | "Edit" | "mcp__*",
  "tool_input": { ... },
  "session_id": "uuid",
  "cwd": "/path"
}

Output (stdout JSON):

{
  "permissionDecision": "allow" | "deny" | "ask" | "defer",
  "reason": "human-readable rationale",
  "updatedInput": { ... }   // optional, only with "allow"
}

Or exit code 2 to immediately block (simpler for deny-only logic).


2. Dangerous Bash Patterns

#!/bin/bash
# ~/.claude/scripts/security-bash-guard.sh
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r .tool_name)

[ "$TOOL" != "Bash" ] && { echo '{"permissionDecision":"allow"}'; exit 0; }

CMD=$(echo "$INPUT" | jq -r .tool_input.command)

# Patterns to deny
DENY_PATTERNS=(
  'rm -rf /'
  'rm -rf ~'
  'rm -rf \*'
  'chmod -R 777'
  'mkfs\.'
  'dd if=.+of=/dev/'
  ':\(\)\{ :\|:& \};:'  # fork bomb
  'curl .+ \| (sudo )?bash'
  'wget .+ \| (sudo )?bash'
  '> /dev/sda'
)

for PATTERN in "${DENY_PATTERNS[@]}"; do
  if echo "$CMD" | grep -qE "$PATTERN"; then
    jq -n --arg reason "Blocked: matches $PATTERN" \
      '{permissionDecision:"deny",reason:$reason}'
    exit 0
  fi
done

# git push --force to main/master
if echo "$CMD" | grep -qE 'git push.+--force.+(main|master)\b' || \
   echo "$CMD" | grep -qE 'git push.+--force\b.*\b(main|master)\s*$'; then
  jq -n '{permissionDecision:"deny",reason:"Blocked: force-push to main/master"}'
  exit 0
fi

echo '{"permissionDecision":"allow"}'

3. Sensitive File Protection (Write/Edit)

#!/bin/bash
# ~/.claude/scripts/security-file-guard.sh
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r .tool_name)

case "$TOOL" in
  Write|Edit) ;;
  *) echo '{"permissionDecision":"allow"}'; exit 0 ;;
esac

PATH_VAL=$(echo "$INPUT" | jq -r .tool_input.file_path)

# Sensitive file patterns
SENSITIVE_PATTERNS=(
  '\.env$'
  '\.env\.'
  '/id_rsa$'
  '/id_ed25519$'
  '\.pem$'
  '\.p12$'
  '\.key$'
  '/secrets\.json$'
  '/credentials(\.json)?$'
  '/\.npmrc$'   # may contain auth tokens
  '/\.aws/credentials$'
  '/\.gcloud/credentials\.db$'
)

for PATTERN in "${SENSITIVE_PATTERNS[@]}"; do
  if echo "$PATH_VAL" | grep -qE "$PATTERN"; then
    jq -n --arg p "$PATH_VAL" \
      '{permissionDecision:"ask",reason:"Sensitive file: \($p) requires confirmation"}'
    exit 0
  fi
done

echo '{"permissionDecision":"allow"}'

For higher security: use "deny" instead of "ask".


4. Secret Regex in Tool Input

Detect secrets being written/passed:

#!/bin/bash
# ~/.claude/scripts/security-secret-guard.sh
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r .tool_name)

case "$TOOL" in
  Write|Edit) CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content // .tool_input.new_string') ;;
  Bash) CONTENT=$(echo "$INPUT" | jq -r .tool_input.command) ;;
  *) echo '{"permissionDecision":"allow"}'; exit 0 ;;
esac

# Common secret patterns
SECRET_PATTERNS=(
  'AKIA[0-9A-Z]{16}'                               # AWS access key
  'aws_secret_access_key.*[A-Za-z0-9/+=]{40}'      # AWS secret
  'sk-[A-Za-z0-9]{40,}'                            # OpenAI / Stripe / generic
  'sk-ant-api[0-9]{2}-[A-Za-z0-9_-]{90,}'          # Anthropic API key
  'ghp_[A-Za-z0-9]{36}'                            # GitHub PAT
  'github_pat_[A-Za-z0-9_]{82}'                    # GitHub fine-grained PAT
  'xox[baprs]-[A-Za-z0-9-]{10,}'                   # Slack token
  'eyJ[A-Za-z0-9_-]{20,}\.eyJ[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}'  # JWT
  '-----BEGIN [A-Z ]+PRIVATE KEY-----'             # PEM private key
)

for PATTERN in "${SECRET_PATTERNS[@]}"; do
  if echo "$CONTENT" | grep -qE "$PATTERN"; then
    jq -n --arg p "$(echo "$PATTERN" | head -c 30)..." \
      '{permissionDecision:"deny",reason:"Secret detected: \($p)"}'
    exit 0
  fi
done

echo '{"permissionDecision":"allow"}'

Auto-redact (for logging hooks, not for blocking)

REDACTED=$(echo "$CONTENT" | sed -E 's/(sk-[A-Za-z0-9]{40,})/[REDACTED-OPENAI]/g')
jq -n --arg c "$REDACTED" '{permissionDecision:"allow",updatedInput:{content:$c}}'

5. MCP Tool ACL

#!/bin/bash
# ~/.claude/scripts/security-mcp-acl.sh
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r .tool_name)

# Only filter MCP tools
[[ "$TOOL" != mcp__* ]] && { echo '{"permissionDecision":"allow"}'; exit 0; }

# Read denylist from env (comma-separated)
DENYLIST="${HONE_BLOCKED_MCP_TOOLS:-}"

IFS=',' read -ra DENIED <<< "$DENYLIST"
for D in "${DENIED[@]}"; do
  if [ "$TOOL" = "$D" ] || [[ "$TOOL" == "$D"* ]]; then
    jq -n --arg t "$TOOL" \
      '{permissionDecision:"deny",reason:"MCP tool \($t) is on denylist"}'
    exit 0
  fi
done

echo '{"permissionDecision":"allow"}'

Usage:

export HONE_BLOCKED_MCP_TOOLS="mcp__shell__exec,mcp__notion__delete_page"

6. CI Auto-Deny Escalation

In CI environments, "ask" cannot be answered. Escalate all asks to deny:

if [ "$CI" = "true" ] || [ -n "$GITHUB_ACTIONS" ]; then
  # In CI, asks become denies
  ORIGINAL_DECISION=$(echo "$DECISION_JSON" | jq -r .permissionDecision)
  if [ "$ORIGINAL_DECISION" = "ask" ]; then
    echo "$DECISION_JSON" | jq '.permissionDecision = "deny" | .reason = "Auto-denied (ask in CI): \(.reason)"'
    exit 0
  fi
fi

7. Compositions

Single hook composing all guards:

#!/bin/bash
# ~/.claude/scripts/security-all.sh
INPUT=$(cat)

# Run guards in order; first deny wins
for GUARD in bash file secret mcp; do
  RESULT=$(echo "$INPUT" | ~/.claude/scripts/security-${GUARD}-guard.sh)
  DECISION=$(echo "$RESULT" | jq -r .permissionDecision)
  if [ "$DECISION" = "deny" ] || [ "$DECISION" = "ask" ]; then
    echo "$RESULT"
    exit 0
  fi
done

echo '{"permissionDecision":"allow"}'

Settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "",
        "hooks": [
          { "type": "command", "command": "~/.claude/scripts/security-all.sh", "timeout": 2000 }
        ]
      }
    ]
  }
}

8. Audit Logging (PostToolUse, complementary)

Log all tool uses for security audit:

#!/bin/bash
# ~/.claude/scripts/security-audit-log.sh
INPUT=$(cat)
LOG=~/.claude/audit/$(date +%Y-%m).jsonl
mkdir -p "$(dirname "$LOG")"
echo "$INPUT" | jq -c '. + {logged_at: now}' >> "$LOG"
exit 0
{
  "PostToolUse": [
    {
      "matcher": "",
      "hooks": [{ "type": "command", "command": "~/.claude/scripts/security-audit-log.sh", "async": true }]
    }
  ]
}

9. Common Pitfalls

Pitfall Avoidance
Regex too broad → blocks legitimate ops Test against false-positive corpus
Regex too narrow → misses variants Test with obfuscated examples
Hook crashes → tool silently allowed Always exit 0 with explicit decision; use set -e carefully
Slow regex → tool feels laggy Keep <100ms per check
Secret patterns hardcoded → outdated Update list periodically; consider gitleaks rule sync
jq missing → hook fails Document install requirement
Asking for confirmation in CI → hangs Detect $CI; auto-deny
Allowing git push --force to PR branches Allowlist non-main branches
Blocking own .env edit when bootstrapping Provide BYPASS=1 env var with explicit user opt-in
Logging secrets to audit log Redact secrets before logging

10. Decision Walkthrough Template

Threat surface:
  □ Dangerous Bash (rm -rf /, fork bomb, curl|bash)
  □ Sensitive file write (.env, .pem, secrets)
  □ Secret leakage in input (API keys, JWTs, PEM)
  □ MCP tool ACL (denylist via env)

Decision behavior per match:
  □ deny (block + log)
  □ ask (interactive confirm)
  □ allow + redact (updatedInput)

CI behavior:
  □ Escalate ask → deny
  □ Allow opt-out via BYPASS=1 env

Performance budget: <100ms per hook

Audit:
  □ PostToolUse audit log (JSONL with redaction)
  □ Log path: ~/.claude/audit/YYYY-MM.jsonl

Setup:
  □ chmod +x scripts
  □ Test with malicious input examples
  □ Document BYPASS env var usage
  □ Session restart

11. References

  • Claude Code hooks documentation
  • gitleaks (secret pattern reference)
  • OWASP Cheat Sheet: Logging
  • AWS / GitHub / OpenAI / Slack secret format docs
  • Knight Capital 2012 incident (flag reuse) — referenced by Sweep / Hone

Source: SKILL.md on GitHub

1 warning13d4 checks · Risk SAFE
  • Gen Agent Trust Hub13d

    The skill is a security-focused configuration auditor and automation tool for AI CLI environments. It includes extensive features for detecting hardcoded secrets, blocking dangerous shell commands, and managing environment security through best practices. No malicious patterns were detected.

  • Socket13d

    No alerts

  • Snyk13d

    Risk: LOW · No issues

  • ZeroLeaks5mo

    1 finding · Score: 78/100

Signed by skilld at c805268. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 days ago.

Activeupdated 2 weeks ago

README badge

README badge for simota/agent-skills/hone