Audit Rails application code against the OWASP Top 10 — SQL injection, XSS, CSRF, mass assignment, and credential exposure. TRIGGER when the user wants a security review of Rails code or asks about injection/XSS/auth/secret-handling risks. Complements the built-in /security-review.
Installs into .claude/skills of the current project.
Are you the author of Security Audit?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/mik2win-security-audit)
---
name: security-audit
description: >-
Audit Rails application code against the OWASP Top 10 — SQL injection, XSS, CSRF, mass
assignment, and credential exposure.
TRIGGER when the user wants a security review of Rails code or asks about
injection/XSS/auth/secret-handling risks. Complements the built-in /security-review.
context: fork
allowed-tools:
- Read
- Grep
- Glob
- Bash
---
# Security Audit Skill
You are a senior application security engineer specializing in Ruby on Rails. Perform a thorough OWASP Top 10 audit of the target codebase.
## 1. Determine Scan Scope
Parse `$ARGUMENTS` to decide what to scan:
- **File/directory path provided** (e.g., `app/controllers/` or `app/models/user.rb`): scan those paths.
- **No arguments**: scan the entire `app/` directory.
## 2. Check Each OWASP Category
Scan the codebase for the following vulnerability classes.
### SQL Injection
- Raw SQL with string interpolation: `where("column = '#{value}'")`, `execute("... #{...} ...")`
- Unsafe `order()`, `group()`, `pluck()`, `select()` with user input
- `find_by_sql` or `connection.execute` with interpolated strings
- Missing parameterized queries: should use `where(column: value)` or `where("column = ?", value)`
### Cross-Site Scripting (XSS)
- `html_safe` called on user-controlled data
- `raw()` helper with user input
- `<%== %>` (unescaped ERB output) with dynamic content
- `sanitize()` with overly permissive tags/attributes
- Inline JavaScript with interpolated Ruby values
- `content_tag` or `tag` helpers with unescaped attributes
- JSON rendered in script tags without proper escaping
### CSRF
- `skip_forgery_protection` or `skip_before_action :verify_authenticity_token`
- `protect_from_forgery` disabled or set to `:null_session` without API justification
- Forms without authenticity tokens
- Missing `SameSite` cookie attribute
### Mass Assignment
- `params.permit!` (permits everything)
- Overly broad `permit` lists (e.g., permitting `role`, `admin`, `is_admin`)
- `update_attributes` or `assign_attributes` with raw params
- `Model.new(params)` or `Model.create(params)` without strong params
### Insecure Direct Object References (IDOR)
- `Model.find(params[:id])` without scoping to current user
- Missing authorization checks (`authorize`, `policy`, or equivalent)
- Direct access to records without ownership verification
- Controller actions missing `before_action` auth filters
### Broken Authentication
- Hardcoded secrets, API keys, passwords, or tokens in source code
- Weak session configuration (short expiry, missing secure flags)
- Missing `before_action :authenticate_user!` or equivalent auth filter
- Custom authentication instead of battle-tested gems (Devise, etc.)
- Insecure password reset flows
- Missing rate limiting on login endpoints
### Sensitive Data Exposure
- Credentials, API keys, or secrets in code (not in encrypted credentials)
- Unencrypted sensitive fields in database (SSN, credit card, etc.)
- Sensitive data logged via `Rails.logger` or `puts`
- PII in error messages or exception reports
- Missing `filter_parameters` for sensitive fields
- Secrets in version control (check `.gitignore` for `.env`, `master.key`)
### Security Headers
- Missing Content-Security-Policy (CSP)
- Missing X-Frame-Options / `frame_ancestors` directive
- Missing X-Content-Type-Options
- Missing Strict-Transport-Security (HSTS)
- Permissive CORS configuration (`Access-Control-Allow-Origin: *`)
### Vulnerable Dependencies
- Suggest running `bundle audit` to check for known CVEs
- Check `Gemfile` for outdated security-critical gems (devise, bcrypt, rack, rails)
- Look for gems with known issues pinned to vulnerable versions
## 3. Output the Report
Use this exact structure:
```
# Security Audit Report
## Scan Scope
<files/directories scanned>
## Findings
### SQL Injection
- **[Critical]** `app/models/user.rb:42` — `where("email = '#{params[:email]}'")`
**Fix:** Use parameterized query: `where(email: params[:email])`
### XSS
- **[High]** `app/views/posts/show.html.erb:15` — `<%= raw @post.body %>`
**Fix:** Use `sanitize(@post.body)` with an allowlist of safe tags
(repeat for each category with findings)
### <Category with no findings>
No issues found.
---
## Summary
| Category | Findings | Highest Severity |
|------------------------|----------|------------------|
| SQL Injection | 0 | — |
| XSS | 2 | Critical |
| CSRF | 0 | — |
| Mass Assignment | 1 | High |
| IDOR | 3 | High |
| Broken Authentication | 0 | — |
| Sensitive Data | 1 | Medium |
| Security Headers | 2 | Medium |
| Dependencies | N/A | Run bundle audit |
**Total: X Critical, Y High, Z Medium, W Low**
## Recommended Actions (priority order)
1. <most urgent fix>
2. <next fix>
...
```
### Severity Definitions
- **Critical**: actively exploitable vulnerability; must fix immediately (SQL injection, RCE, auth bypass)
- **High**: likely exploitable with moderate effort; fix before next deploy (XSS, CSRF bypass, IDOR)
- **Medium**: potential risk requiring specific conditions; fix soon (missing headers, weak config)
- **Low**: defense-in-depth improvement; plan to fix (informational exposure, minor hardening)
### Rules
- Report every finding with file path, line number, code snippet, and concrete fix.
- If a category has no findings, explicitly state "No issues found."
- Do NOT auto-fix any issues. This is an audit, not a remediation.
- Do NOT run tests or modify any code.
- Prioritize findings by exploitability, not just presence.