CanCanCommunity/cancancan
The authorization Gem for Ruby on Rails.
Healthy across all four use cases
Permissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓45+ active contributors
- ✓Distributed ownership (top contributor 42% of recent commits)
- ✓MIT licensed
Show 3 more →Show less
- ✓CI configured
- ✓Tests present
- ⚠Stale — last commit 1y ago
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/cancancommunity/cancancan)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/cancancommunity/cancancan on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: CanCanCommunity/cancancan
Generated by RepoPilot · 2026-05-10 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/CanCanCommunity/cancancan shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across all four use cases
- 45+ active contributors
- Distributed ownership (top contributor 42% of recent commits)
- MIT licensed
- CI configured
- Tests present
- ⚠ Stale — last commit 1y ago
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live CanCanCommunity/cancancan
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/CanCanCommunity/cancancan.
What it runs against: a local clone of CanCanCommunity/cancancan — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in CanCanCommunity/cancancan | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch develop exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 498 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of CanCanCommunity/cancancan. If you don't
# have one yet, run these first:
#
# git clone https://github.com/CanCanCommunity/cancancan.git
# cd cancancan
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of CanCanCommunity/cancancan and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "CanCanCommunity/cancancan(\\.git)?\\b" \\
&& ok "origin remote is CanCanCommunity/cancancan" \\
|| miss "origin remote is not CanCanCommunity/cancancan (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify develop >/dev/null 2>&1 \\
&& ok "default branch develop exists" \\
|| miss "default branch develop no longer exists"
# 4. Critical files exist
test -f "lib/cancan/ability.rb" \\
&& ok "lib/cancan/ability.rb" \\
|| miss "missing critical file: lib/cancan/ability.rb"
test -f "lib/cancan/controller_helpers.rb" \\
&& ok "lib/cancan/controller_helpers.rb" \\
|| miss "missing critical file: lib/cancan/controller_helpers.rb"
test -f "lib/cancan/rules.rb" \\
&& ok "lib/cancan/rules.rb" \\
|| miss "missing critical file: lib/cancan/rules.rb"
test -f "cancancan.gemspec" \\
&& ok "cancancan.gemspec" \\
|| miss "missing critical file: cancancan.gemspec"
test -f "README.md" \\
&& ok "README.md" \\
|| miss "missing critical file: README.md"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 498 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~468d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/CanCanCommunity/cancancan"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
CanCanCan is a Ruby on Rails authorization gem that defines and enforces user permissions in a single Ability class, eliminating scattered permission logic across controllers, views, and queries. It provides declarative ability rules (e.g., can :read, Post, public: true) and helper methods like can? and cannot? to check permissions at runtime, supporting both simple attribute-based and complex database-backed ability definitions. Classic Rails gem structure: core authorization logic in lib/ (inferred from include CanCan::Ability pattern), generator for scaffold (rails g cancan:ability), RSpec tests throughout, and documentation as a static VitePress site in docs/.vitepress/. No monorepo; single package distributed via RubyGems (cancancan.gemspec).
👥Who it's for
Rails developers building multi-user applications who need role-based access control (RBAC) and resource-level authorization without duplicating permission logic. Used by teams ranging from startups to enterprises (Pennylane, Membean, Goboony sponsor it) who want centralized, testable permission management.
🌱Maturity & risk
Production-ready and actively maintained. The project has substantial test coverage (279k+ lines of Ruby), CI/CD via GitHub Actions (.github/workflows/test.yml), Appraisals for multi-version Rails support, and a comprehensive documentation site (docs/.vitepress). The sponsor roster and active community indicate sustained adoption.
Low risk for production use; however, it's primarily single-maintainer (coorasse per sponsor link), so feature velocity depends on one person's availability. No visible breaking-change warnings in the snippet, but authorization libraries require careful testing on upgrades. Dependency footprint appears minimal (no package.json visible for heavy npm deps).
Active areas of work
Active documentation site build and maintenance (.vitepress/dist recently generated); CI pipeline runs on each commit via GitHub Actions. Based on CHANGELOG.md and CONTRIBUTING.md presence, the team accepts community contributions and maintains backward compatibility.
🚀Get running
git clone https://github.com/CanCanCommunity/cancancan.git
cd cancancan
bundle install
bundle exec rspec
Daily commands:
Development workflow: bundle exec rspec to run tests, bundle exec rubocop to lint. For documentation: cd docs && npm install && npm run dev (based on VitePress config in .vitepress/config.mts). No traditional 'server' — this is a library, not an app.
🗺️Map of the codebase
lib/cancan/ability.rb— Core ability definition engine where all authorization rules are defined; every developer must understand how to structure permissions herelib/cancan/controller_helpers.rb— Provides authorize_resource! and load_resource helpers that integrate CanCanCan into Rails controllers; essential for understanding request-level authorization flowlib/cancan/rules.rb— Implements the Rule class that stores individual ability rules; critical for understanding how permissions are matched and evaluatedcancancan.gemspec— Gem manifest defining dependencies and runtime requirements; necessary for understanding library compatibility and installationREADME.md— Primary entry point explaining CanCanCan's purpose, installation, and basic usage patternslib/cancan/model_adapters/active_record_adapter.rb— Adapter that translates ability rules into Active Record queries; essential for understanding how authorization integrates with database queries
🧩Components & responsibilities
- Ability (Ruby) — Core authorization engine storing rules and checking permissions for subjects
- Failure mode: Misconfigured rules grant unintended access; no explicit rules deny access by default
- Rule Matcher (Ruby) — Evaluates individual rules against subjects using conditions (blocks, hashes, procs)
- Failure mode: Incorrect block logic or missing edge cases in conditions lead to authorization bypass
- Controller Helpers (Rails controllers) — Integrates authorization checks into Rails request cycle via before_action filters
- Failure mode: Forgotten authorize calls result in unprotected actions; incorrect resource loading causes wrong authorization checks
- Model Adapters (Active Record, Mongoid) — Translates ability rules into database-specific query syntax for accessible_by filtering
- Failure mode: Block-based conditions cannot be translated; queries may be inefficient or incorrect for complex rules
- Access Denial Handler (Rails exception handling) — Raises and handles AccessDenied exceptions when authorization fails
- Failure mode: Missing rescue_from handler exposes error details; incorrect redirect/render response hurts UX
🔀Data flow
Rails Request→Controller— HTTP request routed to controller actionController→Ability (with current_user)— Controller instantiates Ability with authenticated user contextAbility→Rule Matcher— Ability queries rules to check if action is permitted on subjectRule Matcher→Model/Subject— Evaluates block conditions and hash attributes against the resource being accessedModel Adapter→Database— accessible_by translates rules into SQL/MongoDB queries to fetch only authorized recordsAuthorization Check Result→Controller Response— True allows action to proceed; false raises AccessDenied exception caught by Rails
🛠️How to make changes
Add a new authorization rule
- Open or create app/models/ability.rb with an Ability class inheriting from CanCan::Ability (
app/models/ability.rb (user-created)) - Inside the initialize method, define can/cannot rules: can :read, Post if you want to allow read access (
lib/cancan/ability.rb (reference the can method)) - Use block syntax for complex conditions: can :edit, Post { |post| post.user == user } (
lib/cancan/rules.rb (see how conditions are matched))
Protect a controller action with authorization
- In your Rails controller, include CanCan::ControllerAdditions at class level (
lib/cancan/controller_helpers.rb (defines the mixin)) - Call authorize_resource! in before_action or load_and_authorize_resource! to load and protect in one step (
lib/cancan/controller_resource.rb (implements resource loading logic)) - Catch AccessDenied exceptions in a rescue_from block or use the built-in error handler (
lib/cancan/access_denied.rb (the exception class))
Query only authorized records from database
- Define abilities in Ability class as described above, using attribute-based conditions when possible (
app/models/ability.rb (user-created)) - Call Model.accessible_by(current_ability) to get an AR relation filtered by rules (
lib/cancan/model_adapters/active_record_adapter.rb (implements accessible_by)) - The adapter translates ability rules into SQL WHERE clauses automatically (
lib/cancan/model_adapters/base_adapter.rb (abstract adapter interface))
🔧Why these technologies
- Ruby on Rails — CanCanCan is deeply integrated with Rails controllers, Active Record models, and view helpers; Rails is the primary target framework
- Active Record ORM — Primary database adapter for translating ability rules into SQL queries via accessible_by; critical for efficient authorization filtering
- Mongoid — Secondary database adapter providing parity with MongoDB-backed Rails applications
- RSpec — BDD testing framework used throughout the test suite for ability and controller authorization testing
⚖️Trade-offs already made
-
Rule matching via Ruby blocks vs pure hash conditions
- Why: Blocks enable complex, dynamic authorization logic; hashes allow database query translation
- Consequence: Blocks cannot be translated to SQL, forcing in-memory filtering; developers must be aware which rules support accessible_by
-
Centralized Ability class vs distributed authorization checks
- Why: Single Ability file reduces duplication and makes authorization logic auditable in one place
- Consequence: Ability class can grow large; encourages monolithic design rather than modular authorization services
-
Automatic resource loading via load_and_authorize_resource!
- Why: Reduces boilerplate in controllers by automatically loading and checking authorization
- Consequence: Magic behavior can be confusing for newcomers; hides authorization logic from action code
🚫Non-goals (don't propose these)
- Does not provide authentication; assumes a user is already identified (via Devise, Auth0, etc.)
- Does not manage roles or user groups directly; integrates with custom role implementations or external identity systems
- Not designed for row-level security in the database itself; authorization is evaluated in Ruby application code
🪤Traps & gotchas
- Ability initialization context: The
initialize(user)method in custom Ability classes runs once per request; avoid expensive DB queries or API calls here. 2. Database-backed abilities: Rules with complex queries (e.g.,can :read, Post, user_id: user.id) may generate N+1 queries if not scoped carefully; use.accessible_by(current_ability)for efficient loading. 3. Action namespacing: CanCan matches both Rails action names (create,update) and custom symbols; inconsistent naming across ability rules and controllers causes silent permission failures. 4. Test isolation: Ability tests must reset or rebuild the Ability instance per test; default test setup in older Rails versions may share Ability state.
🏗️Architecture
💡Concepts to learn
- Role-Based Access Control (RBAC) — CanCanCan's entire DSL is built around RBAC patterns; understanding how roles map to abilities (e.g.,
admin?user can read all Posts) is fundamental to defining correct rules - Attribute-Based Access Control (ABAC) — CanCanCan's rule syntax like
can :read, Post, public: trueis ABAC; you'll need to understand how attribute matching on model instances works for complex permission logic - Active Record Query Optimization (Scopes & eager loading) — CanCanCan's
accessible_bymethod generates SQL scopes from ability rules; understanding N+1 queries, eager loading (.includes), and query generation is critical to avoid performance issues - DSL (Domain-Specific Language) Design — CanCanCan is itself a DSL for declaring permissions; learning how its
canandcannotmethods parse arguments and build internal rule objects teaches metaprogramming patterns used in Rails - Middleware & Request Context — CanCanCan's
current_userintegration with Rails controller context relies on middleware patterns; understanding request-scoped data is essential for knowing when Ability rules run - Strategy Pattern (for authorization rules) — CanCanCan uses the Strategy pattern internally to evaluate different rule types (attribute-based, block-based, scoped); understanding this helps with debugging custom ability rules
🔗Related repos
varvet/pundit— Alternative authorization gem using plain Ruby classes and explicit policy objects instead of CanCanCan's DSL; direct competitor solving the same Rails authorization problemlaserlemon/authority— Older Rails authorization gem with a different DSL and approach; historical context for understanding CanCanCan's design decisionsactiveadmin/activeadmin— Admin framework for Rails that uses CanCanCan for its authorization layer; key downstream user and integration pointrails/rails— Core Rails framework that CanCanCan extends with authorization helpers; direct dependency and context for understanding controller/view integrationrspec/rspec-rails— Testing framework used throughout CanCanCan's test suite (.rspecconfig); required for understanding and writing tests
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive test coverage for VitePress documentation build pipeline
The repo has a fully built VitePress documentation site (docs/.vitepress/dist) but no visible test or validation workflow. Adding automated tests to ensure documentation builds successfully, links are valid, and code examples are syntax-correct would prevent broken docs deployments. This is critical for a library where authorization rules need clear, working examples.
- [ ] Create a new GitHub Actions workflow file (.github/workflows/docs-build.yml) to build and validate the VitePress docs on each push
- [ ] Add a script in Rakefile or package.json to run
vitepress buildand validate HTML output - [ ] Add link checking via a tool like linkinator or broken-link-checker to catch broken internal/external references in docs/.vitepress/dist
- [ ] Document the docs build process in CONTRIBUTING.md
Add integration tests for Rails controller helpers across multiple Rails versions
The Appraisals file indicates this gem supports multiple Rails versions, but the test.yml workflow and test suite structure aren't fully visible. Controller helpers (referenced in docs/controller_helpers.md) are critical integration points that need tested across Rails 6.0+, 7.0, and 8.0. Current test coverage likely doesn't validate behavior changes across Rails versions.
- [ ] Review existing test structure in spec/ directory and identify gaps in controller helper integration tests
- [ ] Create spec/integration/controller_helpers_spec.rb with tests for authorize!, load_and_authorize_resource, and skip_authorization across Rails versions
- [ ] Ensure .github/workflows/test.yml matrix includes Rails 6.0, 7.0, and 8.0 with explicit version testing
- [ ] Add a test appraisal gemfile if missing (e.g., gemfiles/rails80.gemfile) to ensure CI validates against all supported versions
Add mutation testing configuration to catch untested code paths in ability definitions
Authorization logic is security-critical and untested edge cases could lead to privilege escalation bugs. The repo has RuboCop and CodeClimate configured but no mutation testing. Adding tools like mutant would reveal gaps in the test suite where ability definitions aren't properly validated, especially around complex rule combinations documented in docs/combine_abilities.md.
- [ ] Add mutant gem to Gemfile with a specific section (e.g., group :test, :development)
- [ ] Create a .mutant.yml configuration file to define which lib/ files should be mutated and timeout thresholds
- [ ] Add a Rake task (in Rakefile) to run mutation tests locally and in CI
- [ ] Integrate mutation test results into .github/workflows/test.yml as an optional check with clear reporting of killed/survived mutants
🌿Good first issues
- Add integration tests for Pundit-like block syntax (e.g.,
can :read, Article do |article| ... end) if not fully tested inspec/cancan/ability/; currently has rule matching but block evaluation coverage gaps. - Expand documentation in
docs/for theaccessible_byscope method with real-world examples (pagination, sorting, N+1 prevention); currently only basic examples in README. - Add explicit test coverage for Rails 8.0+ compatibility in
.github/workflows/test.ymlAppraisals matrix; verify no regressions with newer ActiveRecord versions.
⭐Top contributors
Click to expand
Top contributors
- @coorasse — 42 commits
- @AlexWayfer — 4 commits
- @Liberatys — 3 commits
- @Juleffel — 3 commits
- @mishina2228 — 3 commits
📝Recent commits
Click to expand
Recent commits
1100093— Update sponsors (Alessandro Rodi)c63cde1— Fix linter by capitalizing the first letter (coorasse)61ded41— Bump version (coorasse)eab3019— Update friendly_id.md (#831) (shuaibzahda)3329e59— Update CONTRIBUTING.md (#829) (entei)0cda236— Update define_check_abilities.md (#804) (jayqui)5e41ade— Update rules_compression.md (#828) (entei)a47f989— Add documentation for id_param to authorize_resource (#836) (0llirocks)0cdbba7— In permitted_attributes, switch from database column detection to Rails attributes detection. Addresses #838 (#839) (kalsan)ac7c686— Fix: rule_spec should honor DB setting (#847) (tardate)
🔒Security observations
CanCanCan appears to be a well-maintained authorization library with standard CI/CD practices (GitHub Actions, Code Climate integration). However, the analysis is limited by incomplete visibility into actual source code and dependencies. Key concerns are: (1) inability to verify dependency versions for known vulnerabilities without seeing Gemfile/gemspec content, (2) missing security best practices documentation for implementers, (3) lack of formal vulnerability disclosure policy. The library's purpose is security-focused (authorization), which is positive. Recommendation: provide complete dependency information and enhance security documentation for library users.
- Medium · Incomplete Dependency Information —
package.json, Gemfile, cancancan.gemspec. The package.json file only contains type module declaration and lacks comprehensive dependency listings. For a Ruby gem (CanCanCan), the actual dependencies should be declared in the Gemfile and cancancan.gemspec, which were not provided for analysis. This prevents verification of known vulnerable dependency versions. Fix: Provide complete Gemfile and cancancan.gemspec content for dependency vulnerability scanning. Implement automated dependency checking using tools like Bundler Audit or Dependabot. - Low · Vitepress Documentation Cache Exposure —
docs/.vitepress/cache/. The docs/.vitepress/cache directory contains build cache files and dependencies that are typically generated during development. These should be excluded from version control using .gitignore to prevent potential information disclosure about the build environment and dependencies. Fix: Add docs/.vitepress/cache to .gitignore. Ensure documentation build artifacts are not committed to the repository. - Low · Missing Security Headers Documentation —
README.md, docs/. As an authorization library that helps protect Rails applications, there is no visible documentation about security best practices for implementing the library. The README lacks guidance on common authorization pitfalls. Fix: Add documentation section covering common security mistakes, such as: not checking authorization in all controller actions, improper condition handling, and attribute-based authorization pitfalls. - Low · No SECURITY.md File —
Repository root. The repository lacks a SECURITY.md file which should define the security vulnerability reporting process and policy. This is a standard security practice for open-source projects. Fix: Create a SECURITY.md file that outlines the vulnerability disclosure process, including how to responsibly report security issues without public disclosure.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.