Install
$ agentstack add skill-hlsitechio-claude-skills-security-rails-security ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Ruby on Rails Security Audit
Audit Rails applications (Rails 6, 7, 8).
When this skill applies
- Reviewing controllers, models, views
- Auditing strong parameters / mass assignment
- Reviewing ActiveRecord queries for injection
- Checking Devise / Pundit / CanCanCan setup
- Auditing secrets and credential management
Workflow
Follow ../_shared/audit-workflow.md.
Phase 1: Stack detection
grep -E "rails" Gemfile | head
bundle exec rails --version 2>/dev/null
Phase 2: Inventory
# Controllers
find app/controllers -name '*.rb' | head
# Models
find app/models -name '*.rb' | head
# Routes
cat config/routes.rb 2>/dev/null | head -100
# Initializers (security-relevant)
ls config/initializers/
# Brakeman recommended
which brakeman 2>/dev/null || echo "Install: gem install brakeman"
Phase 3: Detection — the checks
Strong parameters
- RLS-SP-1 Every controller action accepting params for create/update uses a
permitallowlist:
``ruby def user_params params.require(:user).permit(:email, :name) # role, admin flags explicitly NOT in permit end ``
- RLS-SP-2 No
params.permit!(allows everything — equivalent to no protection). - RLS-SP-3 Nested attributes use
permit(:foo, addresses_attributes: [:street, :city])notpermit!.
SQL injection (ActiveRecord)
- RLS-SQL-1
where("name = '#{params[:name]}'")is injection. Use placeholders:
``ruby User.where("name = ?", params[:name]) User.where(name: params[:name]) ``
- RLS-SQL-2 Dynamic ORDER BY:
User.order(params[:sort])— Rails 6+ raises on unknown columns, but the safe path isUser.order(sort_column => sort_direction)with allowlisted values. - RLS-SQL-3
find_by_sql("SELECT * WHERE id = #{params[:id]}")is injection. Use placeholders. - RLS-SQL-4 Raw SQL via
connection.executereviewed.
Template XSS (ERB)
- RLS-XSS-1 ERB auto-escapes `
.(double equals) andraw(...),htmlsafe,.htmlsafe` skip escaping. - RLS-XSS-2
sanitize(html, ...)used for partial HTML; safer thanraw. - RLS-XSS-3
link_to(text, params[:url])— Rails normalizesjavascript:URLs inlink_to? Confirm — depends on version; validate URLs server-side. - RLS-XSS-4
content_tag(:a, name, href: params[:url])does not validate URL.
CSRF
- RLS-CSRF-1
protect_from_forgery with: :exceptionin ApplicationController (Rails 5+ default; verify not removed). - RLS-CSRF-2 API-only controllers (
ActionController::API) skip CSRF by default — verify auth is token/JWT-based (not cookie). - RLS-CSRF-3 No
skip_before_action :verify_authenticity_tokenon state-changing endpoints unless replaced with equivalent (webhook signatures). - RLS-CSRF-4 Rails 7+
Origincheck by default for non-GET; verify config.
Authentication (Devise)
- RLS-AUTH-1 Devise modules appropriate:
:database_authenticatable,:registerable,:recoverable,:trackable,:lockable,:timeoutable,:validatable. - RLS-AUTH-2
:lockableenabled to prevent brute force. - RLS-AUTH-3 Password reset tokens single-use and time-limited (Devise defaults reasonable).
- RLS-AUTH-4 Email enumeration prevented: same response for "email sent" whether or not the email exists.
- RLS-AUTH-5
before_action :authenticate_user!on protected controllers.
Authorization (Pundit / CanCanCan)
- RLS-AZ-1 Every controller action uses
authorize @resource(Pundit) orload_and_authorize_resource(CanCanCan). - RLS-AZ-2 Pundit's
verify_authorizedandverify_policy_scopedinafter_actionto catch missed authz. - RLS-AZ-3 Policy scopes restrict index queries to current_user-visible.
- RLS-AZ-4 Default-deny: missing policies → 403.
class ApplicationController < ActionController::Base
include Pundit::Authorization
after_action :verify_authorized, except: :index
after_action :verify_policy_scoped, only: :index
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
private
def user_not_authorized
render plain: 'Forbidden', status: :forbidden
end
end
Mass assignment via attribute hash
- RLS-MA-1
User.new(params[:user])without strong params = mass assignment. - RLS-MA-2
User.update(params[:user])similarly. - RLS-MA-3
attr_accessible/attr_protected(deprecated; pre-Rails 4) not relied on.
Open redirects
- RLS-OR-1
redirect_to params[:return_to]— validate the URL against allowlist or useredirect_to params[:return_to], allow_other_host: false(Rails 7+ default for safety). - RLS-OR-2 Devise
after_sign_in_path_fordoesn't blindly usestored_location_for.
Insecure deserialization
- RLS-DES-1
Marshal.load(user_input)is RCE. Never deserialize untrusted data with Marshal. - RLS-DES-2
YAML.load(user_input)(not safe) → RCE. UseYAML.safe_load. - RLS-DES-3
JSON.loadinvokesObject.from_jsonand is unsafe; useJSON.parse.
Secrets and credentials
- RLS-SEC-1
config/credentials.yml.encencrypted;master.keynot committed (in.gitignore). - RLS-SEC-2
Rails.application.credentials.secret_key_baseset in production. - RLS-SEC-3 Environment-specific credentials (
config/credentials/production.yml.enc) used. - RLS-SEC-4 No secrets in
config/secrets.yml(deprecated) committed.
Cookies and sessions
- RLS-CK-1 Session store config secure:
Rails.application.config.session_store :cookie_store, key: ..., secure: true, httponly: true, same_site: :lax. - RLS-CK-2 Cookie store has size limit (4KB); if storing large data, use Redis/DB store.
- RLS-CK-3 Signed/encrypted cookies used for sensitive data (
cookies.signed[:foo],cookies.encrypted[:foo]).
File uploads (Active Storage, CarrierWave, Shrine)
- RLS-UP-1 Active Storage's allowlist of content types if exposing direct uploads.
- RLS-UP-2 Active Storage variants and previews not run on untrusted content (ImageMagick CVEs).
- RLS-UP-3 S3 / blob URLs not directly user-controllable.
Rails Admin / ActiveAdmin
- RLS-ADM-1 Admin gem authentication enforced separately from app auth.
- RLS-ADM-2 Admin actions audited (paper_trail, audited gem).
- RLS-ADM-3 Admin URL changed from default
/adminfor security through obscurity.
Headers
- RLS-HDR-1
config.force_ssl = truein production. - RLS-HDR-2 Custom CSP via
config.content_security_policyblock. - RLS-HDR-3
secure_headersgem or Rails defaults for X-Frame-Options, X-Content-Type-Options.
Brakeman
- RLS-BR-1 Brakeman run in CI.
brakeman --no-pager -Ashould pass with high/critical confidence findings = 0. - RLS-BR-2 Brakeman ignores (
config/brakeman.ignore) reviewed; false positives documented.
Dependencies
- RLS-DEP-1
bundle updaterecent; Gemfile.lock current. - RLS-DEP-2
bundle auditclean. - RLS-DEP-3 Rails version current LTS; old versions unpatched.
Phase 4: Triage
Critical: params.permit! on user model; YAML.load(user_input); raw SQL with string interpolation; CSRF disabled globally.
Phase 5: Report
Use ../_shared/findings-schema.md. Prefix IDs with RLS-.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: hlsitechio
- Source: hlsitechio/claude-skills-security
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.