Rails security
47 Claude skills for security tradecraft across AppSec, Pentest, Blue Team, and GRC. NL/EU regulatory-anchored. Pattern-level discipline.
npx -y skills add roodlicht/accans-sec-skills --skill rails-securityAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 4 stars4 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.
What its author says it does
Copied from the file, not written here
Rails security review — Brakeman integration, mass-assignment via strong_parameters, SQL injection in ActiveRecord, template injection via html_safe/raw, Devise hardening, credentials.yml.enc, force_ssl and CSP config, recent Rails/Rack CVE patterns.
SKILL.md
10.8 KB, as published. Nobody here has run it
Rails Security
When to use
This skill is the Rails-specific layer on top of secure-coding. Convention over configuration covers a lot, but anyone going against the grain (string interpolation in .where, .html_safe on user input, CSRF off because "it was hard") gets vulnerabilities back.
Triggers on:
- A question like "review this Rails app", "Brakeman triage", "Devise hardening", "mass-assignment check", "is our CSP OK", "force SSL".
- Presence of
Gemfile,config/application.rb,config/environments/production.rb,config/routes.rb, Devise setup underconfig/initializers/devise.rb, Pundit/CanCanCan policies. - A PR that touches
html_safe,raw,.where("... #{x} ..."),skip_before_action :verify_authenticity_token, orskip_forgery_protection. - A Brakeman report that needs triaging.
- A handoff from
security-reviewwhere Rails is in the stack.
When NOT (handoff)
- General Ruby secure-coding →
secure-coding. - API design (Rails API-only mode overlaps with
api-security) → that skill for OWASP API Top 10, here for Rails-specific implementation. - SAST tool orchestration →
sast-orchestrator. Brakeman is Rails-specific and belongs here. - Dep vulns in gems →
cve-triage(viabundle auditor OSV-scanner). - Container/K8s deploy →
container-hardening/k8s-security. - Secrets in
config/master.keyorcredentials.yml.encon disk →secrets-scanner. This skill only covers the Rails credentials model.
Approach
Six phases. Brakeman does the heavy static-analysis lifting; this skill adds human context to what Brakeman outputs and catches what it misses.
1. Brakeman and dependency scanning
Brakeman (OSS, Rails-specific, since 2011). Static analysis that knows Rails idioms. Default tool for this skill.
bundle add brakeman --group=development
bundle exec brakeman --no-pager -o brakeman-report.json -f json
CI integration via the Brakeman action or plain bundle exec brakeman --exit-on-warn. Fail on new warnings via a baseline workflow (--compare against a previous run).
Triage Brakeman warnings with the same discipline as sast-orchestrator phase 5: baseline at introduction, suppress with rule ID plus reason (ignore.json), revisit periodically.
bundler-audit (OSS) for Gemfile.lock vulns; pass results on to cve-triage. Dependabot/Renovate for auto-updates.
2. Mass-assignment and strong_parameters
The classic Rails vulnerability. Pre-Rails-4 the whitelist was opt-in; since then it has been required via strong_parameters.
- Controller idiom:
params.require(:user).permit(:name, :email). Only those fields are accepted by@user.update(user_params). - Foot-gun:
params.require(:user).permit!— the bang variant permits everything, effectively mass-assignment without a whitelist. Search for.permit!in every review. - Nested attributes:
permit(addresses_attributes: [:street, :city])— explicit per level. - Polymorphic / hash-type input:
permit(preferences: {})is an empty whitelist and accepts an entire hash without key checking. Limit to known keys. - Admin flags never in permit:
is_admin,role,stripe_customer_id— hardcode in the controller, do not allow via params.
3. SQL injection in ActiveRecord
ActiveRecord parameterizes via hash syntax and positional placeholders. Issues arise with string interpolation.
Safe (all parameterized):
User.where(name: name)
User.where("name = ?", name)
User.where("name = :n AND age > :a", n: name, a: 18)
Unsafe:
User.where("name = '#{name}'") # string interpolation
User.where("name = '" + name + "'") # concatenation
User.order(params[:sort]) # order-by user input = vulnerable
User.find_by_sql("SELECT ... #{query}") # raw SQL
Specific sub-patterns:
order(params[:sort])— allowlist of permitted column names.User.order(params[:sort].presence_in(%w[name created_at]) || :id).group(...),having(...),pluck(...)— accept string fragments; do not compose from user input.sanitize_sql_array/sanitize_sql_likefor cases where you really do need raw SQL.find_by_sql,connection.execute— always parameterized; Brakeman catches this.
4. XSS and template escape
Rails escapes by default in ERB (<%= %>). XSS arises where you opt out.
raw(user_input)— renders raw, no escaping.<%= user_input.html_safe %>— same effect, method on string.safe_concat,content_tag(..., user_input)withescape: false— variants.sanitize(user_html, tags: ..., attributes: ...)— Rails' built-in HTML sanitizer; acceptable for user-generated HTML provided the allowlist is tight. Refer to the Rails docs for the default allowlist.javascript_tag do ... endwith user input in the body — JS-context XSS, not covered by HTML escape.- Custom helpers that return
html_safeare a common bug source.def my_helper(input); "<b>#{input}</b>".html_safe; endis broken ifinputis user input. Fix:content_tag(:b, input).
5. Auth: Devise, CSRF, sessions, CSP
- CSRF:
protect_from_forgery with: :exception(default since Rails 5.2).skip_before_action :verify_authenticity_tokenis a security-review trigger. API controllers extendingActionController::APIdo not need CSRF (stateless); mixed controllers (web + JSON) do. - Devise hardening:
- Modules:
:database_authenticatable,:registerable,:recoverable,:rememberable,:validatable. Turn on:confirmablefor email verification,:lockablefor brute-force mitigation,:timeoutablefor inactive sessions. - Password hashing: Devise uses bcrypt; stretches >= 12 for prod.
devise-two-factorfor TOTP MFA.- Password reset flow: verify the token is time-bound (
reset_password_withinreasonably short) and enumeration-safe (same response for existing/non-existing email).
- Modules:
- Session config:
config.session_store :cookie_store, key: '_app_session', secure: Rails.env.production?, httponly: true, same_site: :lax.expire_afterreasonable (e.g. 2 hours of inactivity). - Content Security Policy: since Rails 5.2 via
config/initializers/content_security_policy.rb. Build the default policy without'unsafe-inline'or'unsafe-eval'; nonce-based if inline is needed. force_ssl:config.force_ssl = truein production.rb. Or at the reverse-proxy level, not both redundantly.
6. CVE patterns and verification-loop
Rails/Rack CVEs to know:
- CVE-2019-5418 ActionView file disclosure — Accept-header-based path traversal. Historical but canonical; every Rails version <5.2.2.1 / 4.2.11.1 is vulnerable. Reviewer reflex: is the Rails version recent enough?
- CVE-2022-32224 ActiveRecord YAML deserialization RCE — via
serialize :column. Fixed in 5.2.8.1, 6.0.5.1, 6.1.6.1, 7.0.3.1. Search code forserialize :colwithout a type argument; that triggers the legacy YAML path. - CVE-2023-22795 ActionDispatch ReDoS — header-parsing regex DoS.
- Rack 3.x security stream (2024) — multiple CVEs in Rack's request parser.
[verify against https://github.com/rack/rack/security/advisories]for current status. - Other:
[verify against https://rubyonrails.org/security/]for Rails-specific,[verify against https://github.com/rack/rack/security/advisories]for Rack.
File uploads and Active Storage:
has_one_attached :avatar— content type is stored viacontent_typeon the blob, but the MIME check on upload is the controller's responsibility. Usevalidates :avatar, content_type: [...]via theactive_storage_validationsgem.- Variants with the
ImageMagickbackend: a CVE-intensive library. Prefervipsas the backend. - Private storage for non-public files (
ActiveStorage::Current.url_options); do not share blob URLs directly without a signed URL plus expiry.
Verification-loop: Layer 1 scope (Gemfile versions, production.rb settings, routes + controller permits all walked through?), assumptions (protect_from_forgery active on all web controllers?). Layer 2 (CVE IDs verified against rubyonrails.org/security, Brakeman rule IDs are real, no invented Devise modules).
Output
Rails security review — <app>
Rails: <x.y.z> | Ruby: <x.y.z> | Gems with vuln: <N via cve-triage>
Brakeman:
Warnings total: N (baseline: M, new: K)
High-confidence: <list>
Grouped by class
Code patterns:
skip_before_action :verify_authenticity_token: <list>
.permit!, mass-assignment risks: <list>
String interpolation in .where/.order: <list>
html_safe/raw on user input: <list>
Devise:
Modules present: <confirmable, lockable, timeoutable?>
MFA active: <devise-two-factor | none>
stretches: <N>
enumeration-safe: <yes/no on reset flow>
Deploy settings:
config.force_ssl: <true | false>
session-cookie flags: <secure, httponly, same_site>
CSP: <default policy, no unsafe-inline?>
Active Storage (if applicable):
MIME validation: <present | missing>
ImageMagick vs vips: <backend>
Version check:
Rails security releases: <current | N months behind>
Rack CVE scope: <verified>
Findings (severity-sorted, follow security-review format)
Verification-loop: ...
References
- Rails Security Guide — https://guides.rubyonrails.org/security.html. Official security guide, maintained with core-team input.
- Rails Security Announcements — https://rubyonrails.org/security/. CVE feed.
- Brakeman — https://brakemanscanner.org/. Rails-specific SAST.
- bundler-audit — https://github.com/rubysec/bundler-audit. Gemfile.lock dep-CVE scan.
- Devise — https://github.com/heartcombo/devise and https://github.com/devise-security/devise-security for extra modules.
- Active Storage Validations — https://github.com/igorkasyanchuk/active_storage_validations. MIME/size checks that AS itself does not do.
- Rack Security Advisories — https://github.com/rack/rack/security/advisories. Rack touches every Rails app directly.
- OWASP Ruby on Rails Cheat Sheet — https://cheatsheetseries.owasp.org/cheatsheets/Ruby_on_Rails_Cheat_Sheet.html.
Categories
- appsec