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 %>
-
+
+
Example page diff --git a/app/views/admin/forms/new.html.erb b/app/views/admin/forms/new.html.erb index 24291377d..0fb94bdef 100644 --- a/app/views/admin/forms/new.html.erb +++ b/app/views/admin/forms/new.html.erb @@ -31,17 +31,18 @@ <%- if @templates.present? %>
    <% @templates.each do |form| %> -
  • +
  • -

    +

    <%= form.name %>

    -
    +
    <%= to_markdown(form.notes) %>
    -