Skip to main content

security

Security review of Rails code — tenant isolation, authorization on every entry point, injection, XSS, mass assignment, dangerous dispatch, deserialization, webhooks, uploads and config. Use for a security review of a diff or an area, or whenever code touches user input, authorization, secrets or shell commands.

Source facts

Repository
zoolutions/claude-plugins
Last source activity
October 17, 2026 at 18:59
Detected SKILL.md language
English
Stars
2
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
security
description
Security review of Rails code — tenant isolation, authorization on every entry point, injection, XSS, mass assignment, dangerous dispatch, deserialization, webhooks, uploads and config. Use for a security review of a diff or an area, or whenever code touches user input, authorization, secrets or shell commands.
argument-hint
diff (default), file, feature or area to review
allowed-tools
Bash(*), Read, Write, Edit, Glob, Grep, Agent, Skill
model
opus
# Security review: $ARGUMENTS **Read the repository first.** `AGENTS.md` → `## Workflow`: **Tenant**, **Authorization**, **Views**, **Verify** (the static scanners). Empty argument: review `git diff origin/main...HEAD`. For every touched entry point ask: whose data is this, who may do this, what does the user control, which dangerous API does it reach, and what happens on failure. ## 1. Entry points and tenant isolation Entry points: controller actions, reactive actions, API endpoints, webhooks, jobs, mailbox handlers, tool handlers. Each one authenticates (or says in a comment why it is public), loads through the tenant root (Workflow → Tenant) and authorizes with Workflow → Authorization. - IDOR: `Model.find(params[:id])` or `find_by(id:)` without the tenant root. Compare every action of a controller: one unscoped action is enough. - Authorization by allowlist (roles that may), never by denylist (roles that may not). - Jobs and services receive the tenant explicitly; a cross-tenant query in a job is the same leak. - Responses, exports, search results and broadcasts carry only the current tenant's rows. ## 2. Checklist | Category | Flag | Safe | |---|---|---| | SQL injection | interpolation in `where`, `order`, `group`, `having`, `pluck`, `select`, `joins`, `from`, `lock`, `find_by_sql`, `execute`; `exists?(params[:x])`; array literals built by string (`"{#{ids.join(',')}}"`) | hash conditions, `?` placeholders, `ARRAY[?]::text[]`, allowlisted sort columns | | XSS | `html_safe`/`raw`/`unsafe_raw`/`<%==` on user data; `link_to` with a user URL (`javascript:` is a valid scheme); `simple_format(...).html_safe` | auto-escaping; `sanitize` with an allowlist; URL validated to `http`/`https` on the model | | Mass assignment | `permit!`; permitting role, owner, tenant FK, status, balance, verified flags | explicit `permit` of user-editable fields | | Dangerous dispatch | `send`/`public_send`/`constantize`/`safe_constantize` on params or stored user data | allowlist constant, then dispatch | | Code / deserialization | `eval`; `Marshal.load` of anything external; `YAML.load` on untrusted input | JSON; `YAML.safe_load` | | Command injection | backticks, `system("… #{x}")`, `%x{}`, `IO.popen`, `Open3` with interpolation | array form: `system("cmd", arg1, arg2)` | | Files | no type/size check; user filename in a path (`../`); public storage without access control | content-type + size validation, generated names, signed or authorized URLs | | SSRF | fetching a user-supplied URL server-side | allowlisted hosts, or don't fetch | | Webhooks | no signature check; non-constant-time compare; no replay window | HMAC over `"#{timestamp}.#{raw_body}"`, `ActiveSupport::SecurityUtils.secure_compare`, reject timestamps older than ~5 minutes, then enqueue | | Sessions | no `reset_session` on login/logout; secrets in the session; no expiry | rotate on login, expire, store IDs only | | Tokens in URLs | guessable or permanent | `signed_id(expires_in:)`, `generates_token_for` | | Config | `force_ssl` off, `consider_all_requests_local` in production, `skip_forgery_protection` without a reason, wide `config.hosts`, missing `filter_parameters` (password, token, secret, otp, api_key) | the safe default, with a comment for any exception | | Errors | `e.message`, SQL or stack traces in a response; secrets in logs | generic message, details to the APM | | Dependencies | known CVEs | the audit commands in Workflow → Verify | ## 3. Run the scanners Workflow → Verify's security commands (brakeman, and the dependency audits it lists). A new brakeman ignore entry needs a note explaining why the finding is a false positive. ## Output ``` ### [CRITICAL|HIGH|MEDIUM|LOW] <title> File: path:line · Category: <category> Why: <what an attacker does with it> Fix: <the corrected code> ``` End with a verdict: `BLOCK` (any CRITICAL/HIGH), `FIX BEFORE MERGE`, or `CLEAN`. When asked to fix, write a failing spec for each finding first (a cross-tenant 404, a denied role, an escaped payload), then the fix.
View on GitHub