Skip to content

Fix security findings from looper-defend hunt#93

Merged
nschneble merged 3 commits into
mainfrom
security/looper-defend-2026-07-23
Jul 23, 2026
Merged

Fix security findings from looper-defend hunt#93
nschneble merged 3 commits into
mainfrom
security/looper-defend-2026-07-23

Conversation

@nschneble

@nschneble nschneble commented Jul 23, 2026

Copy link
Copy Markdown
Owner

Remediates 5 findings from a looper-defend whole-repo vulnerability hunt.

Changed:

  • lib/abilities/users.rb: Removed can :read, User, role: :user, which let any authenticated non-admin read every other non-admin user's id/email through the GraphQL users query
  • app/services/billing/create_checkout_session_service.rb: Validates price_id against the two configured Stripe plan prices before calling Stripe, instead of trusting the client-supplied value outright
  • config/initializers/rack_attack.rb: Throttles sign-in, email-change, and GraphQL requests
  • app/graphql/schemas/app_schema.rb, app/graphql/schemas/demo/app_schema.rb: Capped the two unbounded root list fields at 100 records.
  • config/initializers/super_admin.rb: authorization_adapter was set to :cancancan, but the installed super_admin gem version has no CancanAdapter, so it silently fell back to DefaultAdapter

Upstream:

Adding rack-attack originally exposed a real, pre-existing bug in the vendored super_admin gem fork: its engine initializer ran require "rack-attack" (wrong path) the moment Rack::Attack was defined, always crashing boot. Fixed properly upstream instead of carrying a workaround here.

Remediates the high/medium findings from a proactive whole-repo
vulnerability hunt (bundler-audit + brakeman clean; findings from
targeted LLM review of billing, auth, authorization, GraphQL, and the
demo namespace).

- GraphQL `users` query let any authenticated non-admin read every
  other non-admin user's id/email (lib/abilities/users.rb granted
  `read` on all `role: :user` records instead of just the caller's
  own). Removed the redundant, over-broad grant -- self-read already
  comes from the existing `can :manage, User, id: user.id` rule.
- Stripe checkout accepted a client-supplied `price_id` with no
  allowlist check, letting a user request a checkout session for any
  price ID rather than only the configured plans.
- Added rack-attack throttles on sign-in (IP + email), email-change,
  and GraphQL requests -- none of these had any rate limiting, so an
  unauthenticated actor could mail-bomb arbitrary addresses via the
  sign-in flow or brute-force the GraphQL endpoint. This also covers
  GraphQL query-cost abuse, since the app's GraphQL gem (a custom
  rails-graphql fork, not graphql-ruby) has no complexity/depth-limit
  primitive to configure directly.
- Capped the two unbounded GraphQL list fields at 100 records.
- Corrected a dangling `super_admin` authorization_adapter config:
  it was set to `:cancancan`, but the installed gem version has no
  CancanAdapter, so it silently fell back to DefaultAdapter. Made the
  real check (admin-only) explicit via the `:proc` adapter instead of
  relying on an undocumented rescue fallback.

Adding rack-attack exposed a pre-existing bug in the vendored
super_admin gem fork: its engine initializer runs
`require "rack-attack"` (wrong path) the moment Rack::Attack is
defined, which always crashes boot. Since that gem is pulled via a
pinned git ref, vendor/rack_attack_shim/ supplies a shim satisfying
that broken require until upstream fixes the typo.

Every fix has corresponding RSpec coverage written/updated alongside
it; full suite (483 examples) and rubocop pass clean.
@nschneble nschneble self-assigned this Jul 23, 2026
nschneble/super_admin#2 fixes the require-path typo directly (and
removes the now-provably-dead require entirely, since the guard it
sat behind already proves Rack::Attack is loaded). Bumping to that
commit removes the need for vendor/rack_attack_shim/ altogether.

Full suite (483 examples) passes with the shim gone, confirming the
upstream fix actually resolves the crash rather than just relocating
where it's worked around.
nschneble/super_admin#2 merged. Moving off the branch-commit pin now
that main carries the fix.
@nschneble
nschneble marked this pull request as ready for review July 23, 2026 17:15
@nschneble
nschneble merged commit 2fef35d into main Jul 23, 2026
8 checks passed
@nschneble
nschneble deleted the security/looper-defend-2026-07-23 branch July 23, 2026 17:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant