agentsclimarketplace

Rails security

Skill tuannv14/claude-team-toolkit/skills/rails-security

Use when auditing a Rails app for SQL injection, XSS, CSRF, mass-assignment, or Gemfile.lock CVEs, or when reviewing only NEW security regressions in a PR vs base branch.From its SKILL.md

Install
npx -y skills add tuannv14/claude-team-toolkit --skill rails-security

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
  • runs commandsInstructs the agent to run 8 commands, including `gem install brakeman bundler-audit` and 7 more.

SKILL.md

6.2 KB, ~1.9k tokens by cl100k_base, as published. Nobody here has run it

/rails-security — Brakeman + bundler-audit

Combined Rails security scan. No credentials. Subcommands: vulns, cves, audit, diff, ignore, update.

Overview

Combined Brakeman (static analysis) + bundler-audit (CVE) scan for Rails apps. diff mode is the killer feature: shows only NEW issues vs base branch via git worktree (non-destructive — never touches your working tree).

When to Use

  • Pre-PR security gate (new SQL injection, XSS, CSRF, mass-assignment)
  • Auditing Gemfile.lock for known CVEs
  • PR review: only see what THIS PR introduced, not pre-existing noise
  • Adding ignored issues with documented reason for audit trail

When NOT to Use

  • Non-Rails projects → Brakeman is Rails-specific
  • Runtime / dynamic security testing → use OWASP ZAP, not static scanners
  • Dependency updates not security-related → use Dependabot / Renovate
  • Auditing infrastructure (Docker, k8s) → wrong scope

Dependencies

gem install brakeman bundler-audit

If a tool is missing, the relevant subcommand is skipped with a clear message.

Helpers

brakeman_cmd() {
  if [ -f "${1:-.}/Gemfile" ] && grep -q "brakeman" "${1:-.}/Gemfile" 2>/dev/null; then
    echo "bundle exec brakeman"
  else
    echo "brakeman"
  fi
}

Dispatch

vulns [path] [--severity high|medium|low|all] — Brakeman scan

PATH_ARG="${1:-.}"; SEV="${SEV:-medium}"
mapfile -t CMD < <(brakeman_cmd "$PATH_ARG" | tr ' ' '\n')
"${CMD[@]}" -p "$PATH_ARG" -f json -o /tmp/brakeman.json --no-progress 2>/dev/null

jq -r --arg sev "$SEV" '
  .warnings[] | select(
    ($sev=="all") or
    ($sev=="high" and .confidence=="High") or
    ($sev=="medium" and (.confidence=="High" or .confidence=="Medium")) or
    ($sev=="low" and true))
  | "[\(.confidence)] \(.warning_type): \(.message)\n  \(.file):\(.line)"
' /tmp/brakeman.json

jq -r '.warnings | group_by(.confidence) | .[] | "\(.[0].confidence): \(length)"' /tmp/brakeman.json

cves [path] — bundler-audit CVE scan

PROJ="${1:-.}"
[ -f "$PROJ/Gemfile.lock" ] || { echo "No Gemfile.lock" >&2; return 1; }

bundle-audit update --quiet 2>/dev/null || true
(cd "$PROJ" && bundle-audit check --format json) > /tmp/bundler-audit.json 2>/dev/null

jq -r '
  .results[]? | "[\(.advisory.criticality // "unknown" | ascii_upcase)] \(.gem.name) \(.gem.version)
  CVE: \(.advisory.cve // .advisory.id)  Fix: upgrade to \(.advisory.patched_versions | join(", "))
  \(.advisory.title)"
' /tmp/bundler-audit.json

audit [path] — both + pre-PR gate (exit non-zero on HIGH or any CVE)

"$0" vulns "$1"; echo ""; "$0" cves "$1"

HIGH_BR=$(jq -r '[.warnings[] | select(.confidence=="High")] | length' /tmp/brakeman.json 2>/dev/null || echo 0)
CVES=$(jq -r '(.results // []) | length' /tmp/bundler-audit.json 2>/dev/null || echo 0)

[ "$HIGH_BR" -gt 0 ] || [ "$CVES" -gt 0 ] && {
  echo "GATE: $HIGH_BR HIGH Brakeman + $CVES CVEs"
  return 1
}

diff [base-branch] — only NEW issues vs base (non-destructive)

Uses git worktree add — your working tree is NEVER modified.

BASE="${BASE:-main}"
WT="$(mktemp -d)/rails-security-base"
trap "git worktree remove --force $WT 2>/dev/null; rm -rf $WT" EXIT INT TERM

git worktree add --quiet "$WT" "$BASE" || { echo "Cannot worktree at $BASE" >&2; return 1; }

mapfile -t CMD < <(brakeman_cmd "." | tr ' ' '\n')
(cd "$WT" && "${CMD[@]}" -f json -o /tmp/brakeman-base.json --no-progress 2>/dev/null) || true
"${CMD[@]}" -f json -o /tmp/brakeman-head.json --no-progress 2>/dev/null

echo "=== NEW Brakeman ==="
jq -s '(.[0].warnings|map(.fingerprint)) as $b | .[1].warnings | map(select(.fingerprint as $f | $b | index($f) | not)) | .[] | "[\(.confidence)] \(.warning_type) — \(.file):\(.line)"' /tmp/brakeman-base.json /tmp/brakeman-head.json

(cd "$WT" && bundle-audit check --format json) > /tmp/ba-base.json 2>/dev/null || echo '{}' > /tmp/ba-base.json
bundle-audit check --format json > /tmp/ba-head.json 2>/dev/null || echo '{}' > /tmp/ba-head.json

echo ""; echo "=== NEW CVEs ==="
jq -s '(.[0].results//[]|map(.advisory.id)) as $b | (.[1].results//[]) | map(select(.advisory.id as $id | $b | index($id) | not)) | .[] | "\(.gem.name) \(.gem.version) — \(.advisory.cve // .advisory.id)"' /tmp/ba-base.json /tmp/ba-head.json

Why worktree, not stash: stash is destructive on Ctrl-C/OOM, swallows merge conflicts. Worktree creates isolated checkout in temp dir.

ignore brakeman <fingerprint> [note] / ignore cve <CVE-ID> [note]

# Brakeman:
mkdir -p config; [ -f config/brakeman.ignore ] || echo '{"ignored_warnings":[]}' > config/brakeman.ignore
jq --arg fp "$FP" --arg note "${NOTE:-no reason given}" \
  '.ignored_warnings += [{fingerprint:$fp, note:$note}]' config/brakeman.ignore > config/brakeman.ignore.tmp \
  && mv config/brakeman.ignore.tmp config/brakeman.ignore

# CVE:
[ -f .bundler-audit.yml ] || echo "ignore: []" > .bundler-audit.yml
echo "Edit .bundler-audit.yml to add: $CVE_ID  # ${NOTE:-add reason}"

ALWAYS require a reason — without it future maintainers can't audit decisions.

update — refresh advisory DB

bundle-audit update     # hits GitHub; fallback: bundle-audit check --no-update

Common Mistakes

  • Ignoring without a reason → future maintainers can't audit decisions
  • Running scanner on full codebase every PR → use diff mode for noise reduction
  • Stash-based diff instead of worktree → destructive on Ctrl-C / OOM
  • Skipping bundle-audit update → stale CVE database misses recent vulns
  • Brakeman ignore by line number → use fingerprint (survives refactors)
  • Treating Brakeman Low as noise → some are real, just rare paths

Safety

  • Scan output reveals attack surface (file paths, gem versions). Don't paste raw output publicly.
  • brakeman.ignore and .bundler-audit.yml MUST be committed (audit trail of accepted-risk decisions).
  • Never bypass scanners in CI without an approved exception (PR comment + ignore entry with reason).
  • Use diff mode for PR review — only see NEW issues, drastically cuts noise.

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Gives 0 of the 12 instructions most vulnerability scanning skills give in ~1.9k tokens

Counted across 223 of the 238 authors here whose files we hold, read 2026-09-06

  • Fix critical findings immediatelyin 21 of 223, across 14 files
  • Fix high findings before productionin 18 of 223, across 12 files
  • Check and install AgentShield before scanningin 18 of 223, across 12 files
  • Scaffold a secure configuration with initin 16 of 223, across 10 files
  • Add the AgentShield GitHub Action to CIin 16 of 223, across 10 files
  • Run the three-agent opus pipeline for deeper analysisin 14 of 223, across 8 files
  • Apply safe auto-fixes onlyin 14 of 223, across 8 files
  • Use JSON output for CI/CD integrationin 13 of 223, across 7 files
  • Filter findings with a minimum severityin 12 of 223, across 10 files
  • Classify each finding by severityin 10 of 223
  • Use parameterized queries for all database accessin 10 of 223, across 9 files
  • Write tests before writing the rulein 9 of 223, across 4 files

Said here and by no other author read

  • Run diff mode for PR review
  • Update the advisory database before scanning
  • Require a documented reason for every ignore
  • Commit both ignore files to the repository
  • Ignore Brakeman warnings by fingerprint, not line number
  • Use git worktree, not stash, for base comparison

Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.