rubyforgood / rubyforgood/awbw

Rate-limit the public, unauthenticated write endpoints

Open
#2,252 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
Ruby
Stars
15
Forks
26
Avg merge
12h 42m
Merged PRs (30d)
242

Description

Four endpoints accept unauthenticated POSTs and create records / send email. None are throttled — the only defence is a hidden honeypot field, which stops naive bots but nothing deliberate.

Endpoints

Endpoint Controller Creates
POST /f/:slug PublicFormsController#create Person, FormSubmission, 2 emails
POST /events/:event_id/public_registrations Events::PublicRegistrationsController#create Person, EventRegistration, FormSubmission, emails
POST /events/:event_id/bulk_payment_form_submissions Events::BulkPaymentFormSubmissionsController#create Person, FormSubmission, emails
POST /contact_us ContactUsController#create email

/f/:slug is the most exposed: it is always open, whereas the event endpoints are gated behind ensure_registerable.

Approach

Rails 8.1 ships ActionController::RateLimiting, so no new gem:

rate_limit to: 5, within: 1.minute, only: :create,
  with: -> { redirect_to public_form_path(@form.slug), alert: "Too many submissions — please wait a minute." }

Keys on request.remote_ip by default and scopes per controller.

Storage

Uses config.cache_store, which is already suitable in both real environments:

  • production — :solid_cache_store (DB-backed, so the counter is shared across processes)
  • development — :memory_store

Decided: no spec for the rate limit

Test env is :null_store, so increment no-ops and a limit never trips — it can't be spec'd without first changing the test cache store (stub Rails.cache, pass an explicit store:, or switch the env to :memory_store).

We are deliberately not doing that. Writing the spec means solving the cache-store question and adds roughly 15 minutes to every suite run for a one-line, framework-provided guard. Not worth it. Ship the rate_limit calls unspec'd and verify by hand in development (where :memory_store makes the limit live).

Revisit only if a future limit carries real logic in its by: or with: lambda.

Notes

  • Confirm request.remote_ip resolves correctly behind the production proxy before trusting per-IP keys.
  • Deliberately scoped as its own PR rather than folded into #2237: this is a cross-cutting policy that should cover all four endpoints at once.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the four named create actions: PublicFormsController#create, Events::PublicRegistrationsController#create, Events::BulkPaymentFormSubmissionsController#create, and ContactUsController#create. Add the Rails rate limits described for each endpoint, then verify the limit manually in development using the memory cache; also confirm request.remote_ip behaves correctly behind the production proxy.

Written by the indexing model from the issue text.

Assessment

Tech stack
rails, ruby
Domain
backend, security
Issue type
Feature
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.