From f803eee572d93e950c91d9155b7bb3dc59f57ba0 Mon Sep 17 00:00:00 2001 From: John Paul Ashenfelter Date: Fri, 12 Sep 2025 13:46:13 -0400 Subject: [PATCH 1/8] Fix main branch sanity check --- .github/workflows/sanity-check-main.yml | 28 +++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/.github/workflows/sanity-check-main.yml b/.github/workflows/sanity-check-main.yml index 436f725cf..79916cea1 100644 --- a/.github/workflows/sanity-check-main.yml +++ b/.github/workflows/sanity-check-main.yml @@ -11,6 +11,18 @@ jobs: publish-badge: runs-on: ubuntu-latest + + services: + mysql: + image: mysql:8.0 + env: + MYSQL_ROOT_PASSWORD: '' + MYSQL_ALLOW_EMPTY_PASSWORD: 'yes' + MYSQL_DATABASE: awbw_test + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + steps: - uses: actions/checkout@v4 @@ -18,6 +30,22 @@ jobs: with: bundler-cache: true + - name: Setup database + run: | + bundle exec rake db:create db:schema:load + env: + RAILS_ENV: test + # AWS credentials + AWS_REGION: ${{ vars.AWS_REGION }} + AWS_S3_BUCKET: ${{ vars.AWS_S3_BUCKET }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + # SMTP settings + SMTP_SERVER: ${{ secrets.SMTP_SERVER }} + SMTP_PORT: ${{ secrets.SMTP_PORT }} + SMTP_USERNAME: ${{ secrets.SMTP_USERNAME }} + SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }} + - name: Run RSpec (SimpleCov JSON + summary via at_exit) env: RAILS_ENV: test From eca9120bf009c80c995737ab2bb30979e0816f51 Mon Sep 17 00:00:00 2001 From: John Paul Ashenfelter Date: Fri, 12 Sep 2025 14:04:36 -0400 Subject: [PATCH 2/8] Add security tools: bundler-audit and brakeman. Ignore existing issues --- .bundler-audit.yml | 10 +++ Gemfile | 2 + Gemfile.lock | 7 ++ bin/brakeman | 7 ++ bin/bundler-audit | 6 ++ config/brakeman.ignore | 196 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 228 insertions(+) create mode 100644 .bundler-audit.yml create mode 100755 bin/brakeman create mode 100755 bin/bundler-audit create mode 100644 config/brakeman.ignore diff --git a/.bundler-audit.yml b/.bundler-audit.yml new file mode 100644 index 000000000..6034773d2 --- /dev/null +++ b/.bundler-audit.yml @@ -0,0 +1,10 @@ +--- +ignore: + - CVE-2024-54133 + - CVE-2024-39308 + - CVE-2025-55193 + - CVE-2025-24293 + - CVE-2021-41182 + - CVE-2021-41183 + - CVE-2021-41184 + - CVE-2022-31160 diff --git a/Gemfile b/Gemfile index d51f5e3cd..bff616835 100644 --- a/Gemfile +++ b/Gemfile @@ -66,6 +66,8 @@ gem "json", ">= 2.6", "< 3" # or simply: gem "json", "~> 2.7" group :development, :test do gem 'better_errors' + gem "brakeman", require: false + gem "bundler-audit", require: false gem 'capybara', '~> 3.36' gem 'dotenv-rails' gem 'faker' diff --git a/Gemfile.lock b/Gemfile.lock index 66edf31f2..7efd810c7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -109,7 +109,12 @@ GEM bourbon (4.2.3) sass (~> 3.4) thor + brakeman (7.1.0) + racc builder (3.3.0) + bundler-audit (0.9.2) + bundler (>= 1.2.0, < 3) + thor (~> 1.0) capybara (3.40.0) addressable matrix @@ -470,6 +475,8 @@ DEPENDENCIES bootstrap-sass bootstrap-will_paginate bourbon (~> 4.2.2) + brakeman + bundler-audit capybara (~> 3.36) ckeditor (~> 4.3.0) cocoon (~> 1.2.6) diff --git a/bin/brakeman b/bin/brakeman new file mode 100755 index 000000000..ace1c9ba0 --- /dev/null +++ b/bin/brakeman @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby +require "rubygems" +require "bundler/setup" + +ARGV.unshift("--ensure-latest") + +load Gem.bin_path("brakeman", "brakeman") diff --git a/bin/bundler-audit b/bin/bundler-audit new file mode 100755 index 000000000..e2ef22690 --- /dev/null +++ b/bin/bundler-audit @@ -0,0 +1,6 @@ +#!/usr/bin/env ruby +require_relative "../config/boot" +require "bundler/audit/cli" + +ARGV.concat %w[ --config config/bundler-audit.yml ] if ARGV.empty? || ARGV.include?("check") +Bundler::Audit::CLI.start diff --git a/config/brakeman.ignore b/config/brakeman.ignore new file mode 100644 index 000000000..3076ad9d9 --- /dev/null +++ b/config/brakeman.ignore @@ -0,0 +1,196 @@ +{ + "ignored_warnings": [ + { + "warning_type": "SQL Injection", + "warning_code": 0, + "fingerprint": "23223b1e5030a4476088aea976cf673e2a30ece4dda7f63e1abd01a2fd8f939e", + "check_name": "SQL", + "message": "Possible SQL injection", + "file": "app/models/workshop.rb", + "line": 517, + "link": "https://brakemanscanner.org/docs/warning_types/sql_injection/", + "code": "all.order(:title).search_by_categories(params[:categories]).order(:title).search_by_sectors(params[:sectors]).order(:title).order(:led_count => :desc).where(:windows_type_id => params[:type].to_i).order(:title).find_by_sql(\"SELECT *, MATCH ( #{\"title, full_name, objective, materials, introduction, demonstration, opening_circle, warm_up, creation, closing, notes, tips, misc1, misc2\"} ) AGAINST ( '*#{params[:query]}*' IN BOOLEAN MODE ) AS all_score,\\n MATCH ( title ) AGAINST ( '*#{params[:query]}*' IN BOOLEAN MODE ) AS title_score\\n\\n FROM workshops WHERE MATCH ( #{\"title, full_name, objective, materials, introduction, demonstration, opening_circle, warm_up, creation, closing, notes, tips, misc1, misc2\"} )\\n AGAINST ( '*#{params[:query]}*' IN BOOLEAN MODE ) #{\"AND windows_type_id = #{params[:type]}\"} AND inactive is false ORDER BY title_score DESC;\")", + "render_path": null, + "location": { + "type": "method", + "class": "Workshop", + "method": "Workshop.search" + }, + "user_input": "params[:query]", + "confidence": "Weak", + "cwe_id": [ + 89 + ], + "note": "" + }, + { + "warning_type": "Mass Assignment", + "warning_code": 70, + "fingerprint": "3143e4690dfdaf7bb904980f061cc9764ede5382bad4602edabaa4b6a56c07b5", + "check_name": "MassAssignment", + "message": "Specify exact keys allowed for mass assignment instead of using `permit!` which allows any keys", + "file": "app/controllers/monthly_reports_controller.rb", + "line": 243, + "link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/", + "code": "params[:quotes].permit!", + "render_path": null, + "location": { + "type": "method", + "class": "MonthlyReportsController", + "method": "quotes_params" + }, + "user_input": null, + "confidence": "Medium", + "cwe_id": [ + 915 + ], + "note": "" + }, + { + "warning_type": "Remote Code Execution", + "warning_code": 24, + "fingerprint": "59ffdfd50cdef491cfd47d69eaa3edc1d3a291661c75c5f1b10c7afa173581af", + "check_name": "UnsafeReflection", + "message": "Unsafe reflection method `constantize` called on parameter value", + "file": "app/controllers/api/v1/resources_controller.rb", + "line": 3, + "link": "https://brakemanscanner.org/docs/warning_types/remote_code_execution/", + "code": "params[:type].constantize", + "render_path": null, + "location": { + "type": "method", + "class": "Api::V1::ResourcesController", + "method": "index" + }, + "user_input": "params[:type]", + "confidence": "High", + "cwe_id": [ + 470 + ], + "note": "" + }, + { + "warning_type": "Mass Assignment", + "warning_code": 70, + "fingerprint": "801ab5e957972d9cafe31dcbb1f53ca2416781cc09e5b8bd0fe62f05eeeda6c7", + "check_name": "MassAssignment", + "message": "Specify exact keys allowed for mass assignment instead of using `permit!` which allows any keys", + "file": "app/controllers/workshop_logs_controller.rb", + "line": 142, + "link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/", + "code": "params[:quotes].permit!", + "render_path": null, + "location": { + "type": "method", + "class": "WorkshopLogsController", + "method": "quotes_params" + }, + "user_input": null, + "confidence": "Medium", + "cwe_id": [ + 915 + ], + "note": "" + }, + { + "warning_type": "Mass Assignment", + "warning_code": 70, + "fingerprint": "abea946803acd0f0fe12c3bcd54e9b5b04d04f78ff6672e38a6cb8db02e3b5eb", + "check_name": "MassAssignment", + "message": "Specify exact keys allowed for mass assignment instead of using `permit!` which allows any keys", + "file": "app/controllers/reports_controller.rb", + "line": 270, + "link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/", + "code": "params[:quotes].permit!", + "render_path": null, + "location": { + "type": "method", + "class": "ReportsController", + "method": "quotes_params" + }, + "user_input": null, + "confidence": "Medium", + "cwe_id": [ + 915 + ], + "note": "" + }, + { + "warning_type": "Cross-Site Scripting", + "warning_code": 2, + "fingerprint": "b10de889c20028842db9a1821e6214044ff9adf7cd90f2ee6621a66c9c03a162", + "check_name": "CrossSiteScripting", + "message": "Unescaped model attribute", + "file": "app/views/workshop_variations/show.html.erb", + "line": 16, + "link": "https://brakemanscanner.org/docs/warning_types/cross_site_scripting", + "code": "WorkshopVariation.find(params[:id]).decorate.display_code", + "render_path": [ + { + "type": "controller", + "class": "WorkshopVariationsController", + "method": "show", + "line": 10, + "file": "app/controllers/workshop_variations_controller.rb", + "rendered": { + "name": "workshop_variations/show", + "file": "app/views/workshop_variations/show.html.erb" + } + } + ], + "location": { + "type": "template", + "template": "workshop_variations/show" + }, + "user_input": null, + "confidence": "High", + "cwe_id": [ + 79 + ], + "note": "" + }, + { + "warning_type": "Mass Assignment", + "warning_code": 70, + "fingerprint": "d241d7e5a92d236ee0b2642ccdbde49e42dbb34989232d7c0d35cdabebb4e9b8", + "check_name": "MassAssignment", + "message": "Specify exact keys allowed for mass assignment instead of using `permit!` which allows any keys", + "file": "app/controllers/workshop_logs_controller.rb", + "line": 146, + "link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/", + "code": "params[:files].permit!", + "render_path": null, + "location": { + "type": "method", + "class": "WorkshopLogsController", + "method": "files_params" + }, + "user_input": null, + "confidence": "Medium", + "cwe_id": [ + 915 + ], + "note": "" + }, + { + "warning_type": "Unmaintained Dependency", + "warning_code": 120, + "fingerprint": "d84924377155b41e094acae7404ec2e521629d86f97b0ff628e3d1b263f8101c", + "check_name": "EOLRails", + "message": "Support for Rails 6.1.7.10 ended on 2024-10-01", + "file": "Gemfile.lock", + "line": 319, + "link": "https://brakemanscanner.org/docs/warning_types/unmaintained_dependency/", + "code": null, + "render_path": null, + "location": null, + "user_input": null, + "confidence": "High", + "cwe_id": [ + 1104 + ], + "note": "" + } + ], + "brakeman_version": "7.1.0" +} From fb9ade0271437c10ffff5b9e3a63f5ba9571f384 Mon Sep 17 00:00:00 2001 From: John Paul Ashenfelter Date: Fri, 12 Sep 2025 14:05:00 -0400 Subject: [PATCH 3/8] Add workflows for security and change name to CI for workflow --- .github/dependabot.yml | 12 ++++++++++++ .github/workflows/{build-and-test.yml => ci.yml} | 0 2 files changed, 12 insertions(+) create mode 100644 .github/dependabot.yml rename .github/workflows/{build-and-test.yml => ci.yml} (100%) diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..83610cfa4 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: +- package-ecosystem: bundler + directory: "/" + schedule: + interval: weekly + open-pull-requests-limit: 10 +- package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly + open-pull-requests-limit: 10 diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/ci.yml similarity index 100% rename from .github/workflows/build-and-test.yml rename to .github/workflows/ci.yml From c7596b2878bb8d24f2c91171bcd3b4d9d80dd379 Mon Sep 17 00:00:00 2001 From: John Paul Ashenfelter Date: Fri, 12 Sep 2025 14:08:09 -0400 Subject: [PATCH 4/8] Remove circleci in favor of using GitHub --- .circleci/config.yml | 44 -------------------------------------------- 1 file changed, 44 deletions(-) delete mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index a5fd0638e..000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,44 +0,0 @@ -version: 2 - -jobs: - deploy-staging: - docker: - - image: mehradm/awbworg:v1 - auth: - username: $DOCKERHUB_USERNAME - password: $DOCKERHUB_PASSWORD # context / project UI env-var reference - resource_class: small - working_directory: ~/repo - steps: - - checkout - - - add_ssh_keys: - fingerprints: - - "15:bf:72:7f:c7:2c:1c:65:3f:83:32:7f:34:85:fc:ba" - - - run: cap staging deploy - - deploy-production: - docker: - - image: mehradm/awbworg:v1 - - steps: - - checkout - - run: ruby -v - -workflows: - version: 2 - deploy_staging_branch: # The name of our workflow is "build_and_test" - jobs: # The list of jobs we run as part of this workflow. - - deploy-staging: - context: docker-hub-creds - filters: - branches: - only: staging - deploy_production_branch: # The name of our workflow is "build_and_test" - jobs: # The list of jobs we run as part of this workflow. - - deploy-production: - context: docker-hub-creds - filters: - branches: - only: production \ No newline at end of file From 258b44ea65e5b0abb630aa1fbd9fc3eadf72445a Mon Sep 17 00:00:00 2001 From: John Paul Ashenfelter Date: Fri, 12 Sep 2025 14:11:47 -0400 Subject: [PATCH 5/8] Add the scanner task to CI --- .github/workflows/ci.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d9a3c8263..e66d8531f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,6 +8,24 @@ on: - reopened jobs: + scan_ruby: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v5 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + + - name: Scan for common Rails security vulnerabilities using static analysis + run: bin/brakeman --no-pager + + - name: Scan for known security vulnerabilities in gems used + run: bin/bundler-audit + build-and-test: runs-on: ubuntu-latest From 233fc4c9fd92c44efb8439be8556792687bc4617 Mon Sep 17 00:00:00 2001 From: John Paul Ashenfelter Date: Fri, 12 Sep 2025 14:26:51 -0400 Subject: [PATCH 6/8] Remove an extra ruby end --- app/views/resources/latest_news.html.erb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/views/resources/latest_news.html.erb b/app/views/resources/latest_news.html.erb index 92b63728e..4b3db6d9d 100644 --- a/app/views/resources/latest_news.html.erb +++ b/app/views/resources/latest_news.html.erb @@ -8,7 +8,6 @@ <% end %> -<% end %>
@@ -53,5 +52,5 @@
- +

LALALALA

From 2db88f896ff99fa54daa9bd43d1ae78fd6f39d0a Mon Sep 17 00:00:00 2001 From: John Paul Ashenfelter Date: Fri, 12 Sep 2025 14:31:39 -0400 Subject: [PATCH 7/8] Fix the name --- .bundler-audit.yml => config/bundler-audit.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .bundler-audit.yml => config/bundler-audit.yml (100%) diff --git a/.bundler-audit.yml b/config/bundler-audit.yml similarity index 100% rename from .bundler-audit.yml rename to config/bundler-audit.yml From dd9dc7da916d0f6b60d36b36e6e111504fd4e221 Mon Sep 17 00:00:00 2001 From: John Paul Ashenfelter Date: Fri, 12 Sep 2025 14:36:35 -0400 Subject: [PATCH 8/8] Add rails later, check github actions now --- .github/dependabot.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 83610cfa4..e5903e0b8 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,12 +1,6 @@ version: 2 updates: -- package-ecosystem: bundler - directory: "/" - schedule: - interval: weekly - open-pull-requests-limit: 10 - package-ecosystem: github-actions directory: "/" schedule: interval: weekly - open-pull-requests-limit: 10