rubyforgood / rubyforgood/awbw
Rate-limit the public, unauthenticated write endpoints
Nobody has claimed this yet.
- 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 |
/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_ipresolves 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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