From 186a6b620257aef73b5c4b2d1c92860e8419ce80 Mon Sep 17 00:00:00 2001
From: Ryan Wold <64987852+ryanwoldatwork@users.noreply.github.com>
Date: Tue, 25 Feb 2025 09:12:19 -0800
Subject: [PATCH 1/2] check the path string rather than interpret the route
* checking routing invokes authentication, which isn't necessary here
* !! forces a boolean return
* add redis to circleCI
---
.circleci/config.yml | 4 ++
config/initializers/rack_attack.rb | 10 +----
spec/helpers/application_helper_spec.rb | 7 +++-
spec/requests/rack_attack_spec.rb | 55 +++++++++++++++++++++++++
4 files changed, 65 insertions(+), 11 deletions(-)
create mode 100644 spec/requests/rack_attack_spec.rb
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 840b83308..242aa4569 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -15,6 +15,10 @@ jobs:
PGHOST: 127.0.0.1
PGUSER: root
+ - image: cimg/redis:7.2.7
+ environment:
+ REDIS_URL: redis://redis:6379/1
+
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
diff --git a/config/initializers/rack_attack.rb b/config/initializers/rack_attack.rb
index 1738c1668..f4ce0edc2 100644
--- a/config/initializers/rack_attack.rb
+++ b/config/initializers/rack_attack.rb
@@ -15,15 +15,7 @@ class Rack::Attack
# Is the request to the form submission route?
def self.submission_route?(req)
- begin
- recognized_route = Rails.application.routes.recognize_path(req.path, method: req.request_method)
- recognized_route[:controller] == 'submissions' &&
- recognized_route[:action] == 'create' &&
- recognized_route[:touchpoint_id].present? &&
- recognized_route[:format] == 'json'
- rescue ActionController::RoutingError
- false
- end
+ !!(req.path =~ %r{^/submissions/[\h]{1,8}\.json$}i)
end
# Response for throttled requests
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index c67037d88..95f073c8f 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -1,12 +1,15 @@
require 'rails_helper'
+include ActiveSupport::Testing::TimeHelpers
RSpec.describe ApplicationHelper, type: :helper do
describe "#format_submission_time" do
let(:time_zone) { "Eastern Time (US & Canada)" }
it "returns time for today's submissions" do
- datetime = Time.zone.now
- expect(helper.format_submission_time(datetime, time_zone)).to eq(datetime.in_time_zone(time_zone).strftime("%-I:%M %p"))
+ travel_to Time.zone.local(2025, 2, 17, 14, 30) do
+ datetime = Time.zone.now
+ expect(helper.format_submission_time(datetime, time_zone)).to eq(datetime.in_time_zone(time_zone).strftime("%-I:%M %p"))
+ end
end
it "returns month and day for submissions from this year (but not today)" do
diff --git a/spec/requests/rack_attack_spec.rb b/spec/requests/rack_attack_spec.rb
new file mode 100644
index 000000000..1833ce6e1
--- /dev/null
+++ b/spec/requests/rack_attack_spec.rb
@@ -0,0 +1,55 @@
+require 'rails_helper'
+
+RSpec.describe 'Rack::Attack throttling', type: :request do
+ before do
+ Rack::Attack.reset! # Reset throttle counters before each test
+ end
+
+ let(:ip) { '1.2.3.4' }
+ let(:headers) { { 'REMOTE_ADDR' => ip } }
+ let(:valid_submission_path) { "/submissions/1234abcd.json" }
+
+ it 'allows up to 10 requests per minute' do
+ 10.times do
+ post valid_submission_path, headers: headers
+ expect(response).not_to have_http_status(:too_many_requests)
+ end
+ end
+
+ it 'blocks the 11th request within a minute' do
+ 10.times { post valid_submission_path, headers: headers }
+
+ post valid_submission_path, headers: headers
+ expect(response).to have_http_status(:too_many_requests)
+ end
+
+ it 'does not throttle requests from different IPs' do
+ 10.times do |i|
+ post valid_submission_path, headers: { 'REMOTE_ADDR' => "192.168.1.#{i}" }
+ expect(response).not_to have_http_status(:too_many_requests)
+ end
+ end
+
+ it 'does not throttle non-matching routes' do
+ 20.times do
+ post "/other_path", headers: headers
+ expect(response).not_to have_http_status(:too_many_requests)
+ end
+ end
+
+ it 'recognizes both numeric and short UUID paths' do
+ valid_paths = ["/submissions/123.json", "/submissions/abc123de.json"]
+ valid_paths.each do |path|
+ post path, headers: headers
+ expect(response).not_to have_http_status(:too_many_requests)
+ end
+ end
+
+ it 'does not throttle invalid submission paths' do
+ invalid_paths = ["/submissions/too_long_uuid_1234.json", "/submissions/.json"]
+ invalid_paths.each do |path|
+ post path, headers: headers
+ expect(response).not_to have_http_status(:too_many_requests)
+ end
+ end
+end
From d1afc801aca617b55583b5791d76d0a1fb526bdd Mon Sep 17 00:00:00 2001
From: Ryan Wold <64987852+ryanwoldatwork@users.noreply.github.com>
Date: Tue, 25 Feb 2025 12:15:19 -0800
Subject: [PATCH 2/2] Empty trash
---
.circleci/cron.sh | 9 ++++++---
Gemfile | 5 ++++-
Gemfile.lock | 12 +++---------
app/models/event.rb | 2 ++
app/views/admin/forms/example.html.erb | 3 ++-
app/views/admin/forms/new.html.erb | 9 +++++----
app/views/admin/submissions/flag.js.erb | 2 +-
app/views/admin/submissions/mark.js.erb | 2 +-
app/views/admin/submissions/unarchive.js.erb | 2 +-
app/views/admin/submissions/undelete.js.erb | 2 +-
app/views/admin/submissions/unflag.js.erb | 2 +-
app/views/admin/submissions/unmark.js.erb | 2 +-
lib/tasks/scheduled_jobs.rake | 10 ++++++++++
13 files changed, 38 insertions(+), 24 deletions(-)
diff --git a/.circleci/cron.sh b/.circleci/cron.sh
index a10a37ab5..2dd09a68d 100755
--- a/.circleci/cron.sh
+++ b/.circleci/cron.sh
@@ -8,7 +8,7 @@ echo "Logging into cloud.gov"
cf login -a $CF_API_ENDPOINT -u $CF_USERNAME -p $CF_PASSWORD -o $CF_ORG -s $CF_SPACE
#
-# === Staging environment =========================================================
+# === STAGING environment ======================================================
#
echo "Running tasks in Staging..."
@@ -24,11 +24,12 @@ cf run-task touchpoints-staging-sidekiq-worker -c "rake scheduled_jobs:send_week
cf run-task touchpoints-staging-sidekiq-worker -c "rake scheduled_jobs:check_expiring_forms"
cf run-task touchpoints-staging-sidekiq-worker -c "rake scheduled_jobs:archive_forms"
cf run-task touchpoints-staging-sidekiq-worker -c "rake scheduled_jobs:notify_form_managers_of_inactive_forms"
+# cf run-task touchpoints-staging-sidekiq-worker -c "rake scheduled_jobs:delete_submissions_trash"
echo "Staging tasks have completed."
#
-# === Demo environment =========================================================
+# === DEMO environment =========================================================
#
echo "Running tasks in Demo..."
@@ -44,13 +45,14 @@ cf run-task touchpoints-demo-sidekiq-worker -c "rake scheduled_jobs:send_weekly_
cf run-task touchpoints-demo-sidekiq-worker -c "rake scheduled_jobs:check_expiring_forms"
cf run-task touchpoints-demo-sidekiq-worker -c "rake scheduled_jobs:archive_forms"
cf run-task touchpoints-demo-sidekiq-worker -c "rake scheduled_jobs:notify_form_managers_of_inactive_forms"
+# cf run-task touchpoints-demo-sidekiq-worker -c "rake scheduled_jobs:delete_submissions_trash"
echo "Demo tasks have completed."
cf logout
#
-# === Production environment =========================================================
+# === PRODUCTION environment ===================================================
#
echo "Logging into cloud.gov"
@@ -69,6 +71,7 @@ cf run-task touchpoints-production-sidekiq-worker -c "rake scheduled_jobs:deacti
# cf run-task touchpoints-production-sidekiq-worker -c "rake scheduled_jobs:check_expiring_forms"
# cf run-task touchpoints-production-sidekiq-worker -c "rake scheduled_jobs:archive_forms"
cf run-task touchpoints-production-sidekiq-worker -c "rake scheduled_jobs:notify_form_managers_of_inactive_forms"
+# cf run-task touchpoints-production-sidekiq-worker -c "rake scheduled_jobs:delete_submissions_trash"
echo "Production tasks have completed."
diff --git a/Gemfile b/Gemfile
index 9de8a6e73..c782fe9a6 100644
--- a/Gemfile
+++ b/Gemfile
@@ -78,12 +78,15 @@ group :development, :test do
gem 'pry'
end
+group :development, :staging do
+ gem 'faker'
+end
+
group :development do
gem 'aasm-diagram'
gem "brakeman"
gem 'bullet'
gem "bundler-audit"
- gem 'faker', git: 'https://github.com/faker-ruby/faker.git', branch: 'main'
gem 'listen'
gem 'rails-erd'
gem "rubocop-rails"
diff --git a/Gemfile.lock b/Gemfile.lock
index aefa3c8a6..2641ea275 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -10,14 +10,6 @@ GIT
multi_json (~> 1.14)
omniauth (~> 2.0)
-GIT
- remote: https://github.com/faker-ruby/faker.git
- revision: 3a65e1e2e567cb3be3f6b9582484ba4d5ee5d8c6
- branch: main
- specs:
- faker (3.5.1)
- i18n (>= 1.8.11, < 2)
-
GEM
remote: https://rubygems.org/
specs:
@@ -242,6 +234,8 @@ GEM
factory_bot_rails (6.4.4)
factory_bot (~> 6.5)
railties (>= 5.0.0)
+ faker (3.5.1)
+ i18n (>= 1.8.11, < 2)
faraday (2.12.2)
faraday-net_http (>= 2.0, < 3.5)
json
@@ -674,7 +668,7 @@ DEPENDENCIES
devise (>= 4.8.1)
dotenv
factory_bot_rails
- faker!
+ faker
fog-aws (>= 3.15.0)
image_processing (~> 1.12)
importmap-rails (>= 2.0.0)
diff --git a/app/models/event.rb b/app/models/event.rb
index 041fbe54f..b9035fbf3 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -101,6 +101,8 @@ class Generic
organization_created: 'organization_created',
organization_updated: 'organization_updated',
organization_deleted: 'organization_deleted',
+
+ maintenance_submissions_deleted: 'maintenance_submissions_deleted',
}
def self.log_event(ename, otype, oid, desc, uid = nil)
diff --git a/app/views/admin/forms/example.html.erb b/app/views/admin/forms/example.html.erb
index ce9d7e8f5..bea84fc0e 100644
--- a/app/views/admin/forms/example.html.erb
+++ b/app/views/admin/forms/example.html.erb
@@ -53,9 +53,10 @@
<% else %>