diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml
index 4c81c8c2..14103d40 100644
--- a/.github/workflows/run-tests.yml
+++ b/.github/workflows/run-tests.yml
@@ -41,7 +41,7 @@ jobs:
uses: ruby/setup-ruby@v1
- uses: actions/setup-node@v4
with:
- node-version: '14'
+ node-version: '16'
- name: Cache ruby gems
uses: actions/cache@v4
with:
diff --git a/.overcommit.yml b/.overcommit.yml
new file mode 100644
index 00000000..9c65d5e0
--- /dev/null
+++ b/.overcommit.yml
@@ -0,0 +1,159 @@
+# Overcommit configuration for Rails projects
+# https://github.com/sds/overcommit
+
+# Don't use Bundler context since overcommit is installed globally
+gemfile: false
+
+# Hooks that run during `git commit`
+CommitMsg:
+ # Enforce proper commit message format
+ CapitalizedSubject:
+ enabled: true
+ description: 'Check subject capitalization'
+ EmptyMessage:
+ enabled: true
+ description: 'Check for empty commit message'
+ TextWidth:
+ enabled: true
+ description: 'Check text width'
+ max_subject_width: 72
+ max_body_width: 80
+ TrailingPeriod:
+ enabled: true
+ description: 'Check for trailing periods in subject'
+ SingleLineSubject:
+ enabled: true
+ description: 'Check subject is single line'
+
+# Hooks that run before `git commit`
+PreCommit:
+ # Ruby/Rails specific hooks
+ RuboCop:
+ enabled: true
+ description: 'Analyze Ruby code with RuboCop'
+ required_executable: 'bundle'
+ command: ['bundle', 'exec', 'rubocop']
+ flags: ['--autocorrect-all', '--display-cop-names']
+ on_warn: fail # Treat warnings as failures
+ problem_on_unmodified_line: report
+ include:
+ - '**/*.rb'
+ - '**/*.rake'
+ - '**/Gemfile'
+ - '**/Rakefile'
+
+ RailsBestPractices:
+ enabled: false # Enable when ready
+ description: 'Analyze with rails_best_practices'
+ required_executable: 'bundle'
+ command: ['bundle', 'exec', 'rails_best_practices']
+
+ RailsSchemaUpToDate:
+ enabled: true
+ description: 'Check if db/schema.rb matches migrations'
+
+ BundleCheck:
+ enabled: true
+ description: 'Check Gemfile dependencies'
+
+ # JavaScript/Vue specific hooks
+ EsLint:
+ enabled: true
+ description: 'Analyze JavaScript/Vue with ESLint'
+ required_executable: 'yarn'
+ command: ['yarn', 'lint']
+ include:
+ - '**/*.js'
+ - '**/*.vue'
+
+ # Shell script hooks
+ ShellCheck:
+ enabled: false
+ description: 'Analyze shell scripts with ShellCheck'
+ include:
+ - '**/*.sh'
+ - '**/bin/*'
+ exclude:
+ - '**/bin/*.rb'
+ - '**/bin/bundle'
+ - '**/bin/rails'
+ - '**/bin/rake'
+ - '**/bin/spring'
+ - '**/bin/webpack'
+ - '**/bin/webpack-dev-server'
+ - '**/bin/yarn'
+
+ # General hooks
+ TrailingWhitespace:
+ enabled: true
+ exclude:
+ - '**/db/schema.rb'
+ - '**/db/structure.sql'
+ - '**/*.md'
+
+ MergeConflicts:
+ enabled: true
+
+ YamlSyntax:
+ enabled: true
+ include:
+ - '**/*.yml'
+ - '**/*.yaml'
+
+ JsonSyntax:
+ enabled: true
+ include:
+ - '**/*.json'
+
+ HardTabs:
+ enabled: true
+ exclude:
+ - '**/Makefile'
+ - '**/*.mk'
+
+ # Security scanning (disabled by default for speed)
+ Brakeman:
+ enabled: false
+ description: 'Security scan with Brakeman'
+ command: ['bundle', 'exec', 'brakeman', '--quiet', '--summary']
+
+ BundleAudit:
+ enabled: false
+ description: 'Check for vulnerable gem versions'
+
+# Hooks that run after `git checkout`
+PostCheckout:
+ BundleInstall:
+ enabled: true
+ description: 'Install bundle dependencies'
+
+ YarnInstall:
+ enabled: true
+ description: 'Install yarn dependencies'
+ required_executable: 'yarn'
+ command: ['yarn', 'install']
+
+ ActiveRecordMigrations:
+ enabled: true
+ description: 'Run pending migrations'
+
+# Hooks that run after `git merge`
+PostMerge:
+ BundleInstall:
+ enabled: true
+
+ YarnInstall:
+ enabled: true
+ required_executable: 'yarn'
+ command: ['yarn', 'install']
+
+ ActiveRecordMigrations:
+ enabled: true
+
+# Hooks that run after `git rewrite`
+PostRewrite:
+ BundleInstall:
+ enabled: true
+
+ YarnInstall:
+ enabled: true
\ No newline at end of file
diff --git a/.rubocop.yml b/.rubocop.yml
index 2871b11c..141c5381 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -1,4 +1,4 @@
-require:
+plugins:
- rubocop-rails
- rubocop-performance
diff --git a/.ruby-version b/.ruby-version
index a603bb50..818bd47a 100644
--- a/.ruby-version
+++ b/.ruby-version
@@ -1 +1 @@
-2.7.5
+3.0.6
diff --git a/.tool-versions b/.tool-versions
index a4023dc7..a994f29a 100644
--- a/.tool-versions
+++ b/.tool-versions
@@ -1 +1,2 @@
-ruby 2.7.5
+ruby 3.0.6
+nodejs 16.20.2
diff --git a/Dockerfile b/Dockerfile
index 1d3c2d6c..73c64b44 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM ruby:2.7
+FROM ruby:3.0.7
RUN curl -sS https://deb.nodesource.com/setup_16.x | bash -
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
@@ -10,7 +10,7 @@ ENV RAILS_ENV=production
RUN mkdir $APP_HOME
WORKDIR $APP_HOME
-RUN gem install bundler:2.2.32
+RUN gem install bundler:2.3.10
ADD Gemfile* $APP_HOME/
RUN bundle install --without development test
diff --git a/Gemfile b/Gemfile
index 9b621d23..025152b6 100644
--- a/Gemfile
+++ b/Gemfile
@@ -2,10 +2,12 @@
source 'https://rubygems.org'
-ruby '~> 2.7'
+ruby File.read('.ruby-version').strip
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
-gem 'rails', '~> 6.1.4'
+gem 'rails', '~> 7.0.0'
+# TODO: Remove this once upgrading to Rails 7.1, this is required for a bug specific to Rails 7.0
+gem 'concurrent-ruby', '1.3.4'
# Use postgresql as the database for Active Record
gem 'pg', '>= 0.18', '< 2.0'
# Use Puma as the app server
@@ -45,7 +47,7 @@ gem 'settingslogic', '~> 2.0.9'
# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.4.2', require: false
-gem 'audited', '~> 5.3.3'
+gem 'audited', '~> 5.8.0'
gem 'activerecord-import'
@@ -61,6 +63,8 @@ gem 'fast_excel'
# For writing excel files
gem 'ruh-roo', '~> 3.0.0', require: 'roo'
+gem 'rexml'
+
gem 'ox'
gem 'rubyzip'
@@ -69,37 +73,37 @@ gem 'mitre-inspec-objects'
gem 'rest-client'
group :development do
- gem 'listen', '~> 3.1.5'
+ gem 'listen'
# Access an interactive console on exception pages or by calling 'console' anywhere in the code.
gem 'web-console', '>= 3.3.0'
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
gem 'letter_opener'
gem 'spring'
- gem 'spring-watcher-listen', '~> 2.0.0'
+ gem 'spring-watcher-listen'
# Process manager for Procfile-based applications (development only)
gem 'foreman'
+ # Git hook manager for automated linting and formatting
+ gem 'overcommit', '~> 0.60', require: false
end
group :test do
# Adds support for Capybara system testing and selenium driver
- gem 'capybara', '>= 2.15'
- gem 'selenium-webdriver'
- # Easy installation and use of web drivers to run system tests with browsers
- # gem 'webdrivers'
-
+ gem 'capybara'
gem 'database_cleaner-active_record'
gem 'rubocop', require: false
gem 'rubocop-performance'
gem 'rubocop-rails'
+ gem 'selenium-webdriver'
gem 'simplecov', require: false
end
group :development, :test do
gem 'brakeman'
gem 'byebug'
- gem 'factory_bot_rails', '~> 5.2.0'
+ gem 'factory_bot_rails', '~> 6.4.0'
+ gem 'pry'
gem 'rspec-mocks'
- gem 'rspec-rails', '~> 4.0.0'
+ gem 'rspec-rails', '~> 6.0.0'
# Load environment variables from .env files in development and test
gem 'dotenv-rails'
end
diff --git a/Gemfile.lock b/Gemfile.lock
index 8cddb079..2b151ca9 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,145 +1,152 @@
GEM
remote: https://rubygems.org/
specs:
- actioncable (6.1.4.6)
- actionpack (= 6.1.4.6)
- activesupport (= 6.1.4.6)
+ actioncable (7.0.8.7)
+ actionpack (= 7.0.8.7)
+ activesupport (= 7.0.8.7)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
- actionmailbox (6.1.4.6)
- actionpack (= 6.1.4.6)
- activejob (= 6.1.4.6)
- activerecord (= 6.1.4.6)
- activestorage (= 6.1.4.6)
- activesupport (= 6.1.4.6)
+ actionmailbox (7.0.8.7)
+ actionpack (= 7.0.8.7)
+ activejob (= 7.0.8.7)
+ activerecord (= 7.0.8.7)
+ activestorage (= 7.0.8.7)
+ activesupport (= 7.0.8.7)
mail (>= 2.7.1)
- actionmailer (6.1.4.6)
- actionpack (= 6.1.4.6)
- actionview (= 6.1.4.6)
- activejob (= 6.1.4.6)
- activesupport (= 6.1.4.6)
+ net-imap
+ net-pop
+ net-smtp
+ actionmailer (7.0.8.7)
+ actionpack (= 7.0.8.7)
+ actionview (= 7.0.8.7)
+ activejob (= 7.0.8.7)
+ activesupport (= 7.0.8.7)
mail (~> 2.5, >= 2.5.4)
+ net-imap
+ net-pop
+ net-smtp
rails-dom-testing (~> 2.0)
- actionpack (6.1.4.6)
- actionview (= 6.1.4.6)
- activesupport (= 6.1.4.6)
- rack (~> 2.0, >= 2.0.9)
+ actionpack (7.0.8.7)
+ actionview (= 7.0.8.7)
+ activesupport (= 7.0.8.7)
+ rack (~> 2.0, >= 2.2.4)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
- actiontext (6.1.4.6)
- actionpack (= 6.1.4.6)
- activerecord (= 6.1.4.6)
- activestorage (= 6.1.4.6)
- activesupport (= 6.1.4.6)
+ actiontext (7.0.8.7)
+ actionpack (= 7.0.8.7)
+ activerecord (= 7.0.8.7)
+ activestorage (= 7.0.8.7)
+ activesupport (= 7.0.8.7)
+ globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
- actionview (6.1.4.6)
- activesupport (= 6.1.4.6)
+ actionview (7.0.8.7)
+ activesupport (= 7.0.8.7)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
- activejob (6.1.4.6)
- activesupport (= 6.1.4.6)
+ activejob (7.0.8.7)
+ activesupport (= 7.0.8.7)
globalid (>= 0.3.6)
- activemodel (6.1.4.6)
- activesupport (= 6.1.4.6)
- activerecord (6.1.4.6)
- activemodel (= 6.1.4.6)
- activesupport (= 6.1.4.6)
- activerecord-import (1.3.0)
+ activemodel (7.0.8.7)
+ activesupport (= 7.0.8.7)
+ activerecord (7.0.8.7)
+ activemodel (= 7.0.8.7)
+ activesupport (= 7.0.8.7)
+ activerecord-import (2.2.0)
activerecord (>= 4.2)
- activestorage (6.1.4.6)
- actionpack (= 6.1.4.6)
- activejob (= 6.1.4.6)
- activerecord (= 6.1.4.6)
- activesupport (= 6.1.4.6)
- marcel (~> 1.0.0)
+ activestorage (7.0.8.7)
+ actionpack (= 7.0.8.7)
+ activejob (= 7.0.8.7)
+ activerecord (= 7.0.8.7)
+ activesupport (= 7.0.8.7)
+ marcel (~> 1.0)
mini_mime (>= 1.1.0)
- activesupport (6.1.4.6)
+ activesupport (7.0.8.7)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
- zeitwerk (~> 2.3)
- addressable (2.8.0)
- public_suffix (>= 2.0.2, < 5.0)
+ addressable (2.8.7)
+ public_suffix (>= 2.0.2, < 7.0)
aes_key_wrap (1.1.0)
- amoeba (3.2.0)
- activerecord (>= 4.2.0)
- ast (2.4.2)
- attr_required (1.0.1)
- audited (5.3.3)
- activerecord (>= 5.0, < 7.1)
- request_store (~> 1.2)
- bcrypt (3.1.16)
- bindata (2.4.15)
+ amoeba (3.3.0)
+ activerecord (>= 5.2.0)
+ ast (2.4.3)
+ attr_required (1.0.2)
+ audited (5.8.0)
+ activerecord (>= 5.2, < 8.2)
+ activesupport (>= 5.2, < 8.2)
+ base64 (0.3.0)
+ bcrypt (3.1.20)
+ bigdecimal (3.2.2)
+ bindata (2.5.1)
bindex (0.8.1)
- bootsnap (1.10.3)
+ bootsnap (1.18.6)
msgpack (~> 1.2)
- brakeman (5.2.1)
- builder (3.2.4)
+ brakeman (6.2.2)
+ racc
+ builder (3.3.0)
byebug (11.1.3)
- capybara (3.36.0)
+ capybara (3.40.0)
addressable
matrix
mini_mime (>= 0.1.3)
- nokogiri (~> 1.8)
+ nokogiri (~> 1.11)
rack (>= 1.6.0)
rack-test (>= 0.6.3)
regexp_parser (>= 1.5, < 3.0)
xpath (~> 3.2)
- chef-config (17.9.46)
+ chef-config (13.6.4)
addressable
- chef-utils (= 17.9.46)
fuzzyurl
- mixlib-config (>= 2.2.12, < 4.0)
- mixlib-shellout (>= 2.0, < 4.0)
- tomlrb (~> 1.2)
+ mixlib-config (~> 2.0)
+ mixlib-shellout (~> 2.0)
chef-telemetry (1.1.1)
chef-config
concurrent-ruby (~> 1.0)
- chef-utils (17.9.46)
- concurrent-ruby
+ childprocess (5.1.0)
+ logger (~> 1.5)
coderay (1.1.3)
- concurrent-ruby (1.2.2)
+ concurrent-ruby (1.3.4)
crass (1.0.6)
- database_cleaner-active_record (2.0.1)
+ database_cleaner-active_record (2.2.1)
activerecord (>= 5.a)
database_cleaner-core (~> 2.0.0)
database_cleaner-core (2.0.1)
- devise (4.8.1)
+ date (3.4.1)
+ devise (4.9.4)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 4.1.0)
responders
warden (~> 1.2.3)
- diff-lcs (1.5.0)
- docile (1.4.0)
- domain_name (0.5.20190701)
- unf (>= 0.0.5, < 1.0.0)
- dotenv (2.8.1)
- dotenv-rails (2.8.1)
- dotenv (= 2.8.1)
- railties (>= 3.2)
- erubi (1.12.0)
- erubis (2.7.0)
- factory_bot (5.2.0)
- activesupport (>= 4.2.0)
- factory_bot_rails (5.2.0)
- factory_bot (~> 5.2.0)
- railties (>= 4.2.0)
+ diff-lcs (1.6.2)
+ docile (1.4.1)
+ domain_name (0.6.20240107)
+ dotenv (3.1.8)
+ dotenv-rails (3.1.8)
+ dotenv (= 3.1.8)
+ railties (>= 6.1)
+ erubi (1.13.1)
+ factory_bot (6.5.3)
+ activesupport (>= 6.1.0)
+ factory_bot_rails (6.4.4)
+ factory_bot (~> 6.5)
+ railties (>= 5.0.0)
faraday (1.3.1)
faraday-net_http (~> 1.0)
multipart-post (>= 1.2, < 3)
ruby2_keywords (>= 0.0.4)
- faraday-net_http (1.0.1)
- faraday_middleware (1.2.0)
+ faraday-net_http (1.0.2)
+ faraday_middleware (1.2.1)
faraday (~> 1.0)
- fast_excel (0.4.0)
+ fast_excel (0.5.0)
ffi (> 1.9, < 2)
- ffaker (2.20.0)
- ffi (1.15.5)
+ ffaker (2.24.0)
+ ffi (1.17.2-arm64-darwin)
+ ffi (1.17.2-x86_64-darwin)
foreman (0.88.1)
fuzzyurl (0.9.0)
gitlab_omniauth-ldap (2.2.0)
@@ -147,31 +154,29 @@ GEM
omniauth (>= 1.3, < 3)
pyu-ruby-sasl (>= 0.0.3.3, < 0.1)
rubyntlm (~> 0.5)
- gli (2.21.0)
- globalid (1.0.1)
- activesupport (>= 5.0)
- haml (5.2.2)
- temple (>= 0.8.0)
+ gli (2.22.2)
+ ostruct
+ globalid (1.2.1)
+ activesupport (>= 6.1)
+ haml (6.3.0)
+ temple (>= 0.8.2)
+ thor
tilt
- haml-rails (2.0.1)
+ haml-rails (2.1.0)
actionpack (>= 5.1)
activesupport (>= 5.1)
- haml (>= 4.0.6, < 6.0)
- html2haml (>= 1.0.1)
+ haml (>= 4.0.6)
railties (>= 5.1)
hashie (4.1.0)
- highline (2.0.3)
- html2haml (2.2.0)
- erubis (~> 2.7.0)
- haml (>= 4.0, < 6)
- nokogiri (>= 1.6.0)
- ruby_parser (~> 3.5)
+ highline (2.1.0)
http-accept (1.7.0)
- http-cookie (1.0.5)
+ http-cookie (1.0.8)
domain_name (~> 0.5)
- httpclient (2.8.3)
- i18n (1.13.0)
+ httpclient (2.9.0)
+ mutex_m
+ i18n (1.14.7)
concurrent-ruby (~> 1.0)
+ iniparse (1.5.0)
inspec-core (4.24.32)
addressable (~> 2.4)
chef-telemetry (~> 1.0)
@@ -194,90 +199,101 @@ GEM
train-core (~> 3.0)
tty-prompt (~> 0.17)
tty-table (~> 0.10)
- jbuilder (2.11.5)
+ jbuilder (2.13.0)
actionview (>= 5.0.0)
activesupport (>= 5.0.0)
- json (2.6.1)
- json-jwt (1.15.3)
+ json (2.12.2)
+ json-jwt (1.15.3.1)
activesupport (>= 4.2)
aes_key_wrap
bindata
httpclient
- jwt (2.3.0)
- launchy (2.5.0)
- addressable (~> 2.7)
- letter_opener (1.7.0)
- launchy (~> 2.2)
+ jwt (2.10.1)
+ base64
+ language_server-protocol (3.17.0.5)
+ launchy (3.1.1)
+ addressable (~> 2.8)
+ childprocess (~> 5.0)
+ logger (~> 1.6)
+ letter_opener (1.10.0)
+ launchy (>= 2.2, < 4)
license-acceptance (2.1.13)
pastel (~> 0.7)
tomlrb (>= 1.2, < 3.0)
tty-box (~> 0.6)
tty-prompt (~> 0.20)
- listen (3.1.5)
- rb-fsevent (~> 0.9, >= 0.9.4)
- rb-inotify (~> 0.9, >= 0.9.7)
- ruby_dep (~> 1.2)
- loofah (2.19.1)
+ lint_roller (1.1.0)
+ listen (3.9.0)
+ rb-fsevent (~> 0.10, >= 0.10.3)
+ rb-inotify (~> 0.9, >= 0.9.10)
+ logger (1.7.0)
+ loofah (2.24.1)
crass (~> 1.0.2)
- nokogiri (>= 1.5.9)
- mail (2.7.1)
+ nokogiri (>= 1.12.0)
+ mail (2.8.1)
mini_mime (>= 0.1.1)
- marcel (1.0.2)
+ net-imap
+ net-pop
+ net-smtp
+ marcel (1.0.4)
matrix (0.4.2)
- method_source (1.0.0)
- mime-types (3.4.1)
- mime-types-data (~> 3.2015)
- mime-types-data (3.2023.0218.1)
- mini_mime (1.1.2)
- mini_portile2 (2.8.1)
- minitest (5.18.0)
+ method_source (1.1.0)
+ mime-types (3.7.0)
+ logger
+ mime-types-data (~> 3.2025, >= 3.2025.0507)
+ mime-types-data (3.2025.0603)
+ mini_mime (1.1.5)
+ minitest (5.25.5)
mitre-inspec-objects (0.3.3)
inspec-core
- mixlib-config (3.0.9)
+ mixlib-config (2.2.18)
tomlrb
mixlib-log (3.0.9)
- mixlib-shellout (3.2.5)
- chef-utils
- msgpack (1.4.5)
- multi_json (1.15.0)
+ mixlib-shellout (2.4.4)
+ msgpack (1.8.0)
multi_xml (0.6.0)
- multipart-post (2.1.1)
- net-ldap (0.17.1)
- net-protocol (0.2.1)
+ multipart-post (2.4.1)
+ mutex_m (0.3.0)
+ net-imap (0.4.22)
+ date
+ net-protocol
+ net-ldap (0.19.0)
+ net-pop (0.1.2)
+ net-protocol
+ net-protocol (0.2.2)
timeout
- net-scp (3.0.0)
- net-ssh (>= 2.6.5, < 7.0.0)
- net-smtp (0.3.3)
+ net-scp (4.1.0)
+ net-ssh (>= 2.6.5, < 8.0.0)
+ net-smtp (0.5.1)
net-protocol
- net-ssh (6.1.0)
+ net-ssh (7.3.0)
netrc (0.11.0)
- nio4r (2.5.9)
- nokogiri (1.14.3)
- mini_portile2 (~> 2.8.0)
- racc (~> 1.4)
- nokogiri (1.14.3-x86_64-darwin)
+ nio4r (2.7.4)
+ nokogiri (1.17.2-arm64-darwin)
racc (~> 1.4)
- nokogiri (1.14.3-x86_64-linux)
+ nokogiri (1.17.2-x86_64-darwin)
racc (~> 1.4)
- nokogiri-happymapper (0.9.0)
+ nokogiri-happymapper (0.10.0)
nokogiri (~> 1.5)
- oauth2 (1.4.7)
- faraday (>= 0.8, < 2.0)
- jwt (>= 1.0, < 3.0)
- multi_json (~> 1.3)
+ oauth2 (2.0.12)
+ faraday (>= 0.17.3, < 4.0)
+ jwt (>= 1.0, < 4.0)
+ logger (~> 1.2)
multi_xml (~> 0.5)
- rack (>= 1.2, < 3)
- omniauth (2.1.1)
+ rack (>= 1.2, < 4)
+ snaky_hash (~> 2.0, >= 2.0.3)
+ version_gem (>= 1.1.8, < 3)
+ omniauth (2.1.3)
hashie (>= 3.4.6)
rack (>= 2.2.3)
rack-protection
- omniauth-github (2.0.0)
+ omniauth-github (2.0.1)
omniauth (~> 2.0)
- omniauth-oauth2 (~> 1.7.1)
- omniauth-oauth2 (1.7.2)
- oauth2 (~> 1.4)
- omniauth (>= 1.9, < 3)
- omniauth-rails_csrf_protection (1.0.1)
+ omniauth-oauth2 (~> 1.8)
+ omniauth-oauth2 (1.8.0)
+ oauth2 (>= 1.4, < 3)
+ omniauth (~> 2.0)
+ omniauth-rails_csrf_protection (1.0.2)
actionpack (>= 4.2)
omniauth (~> 2.0)
omniauth_openid_connect (0.6.1)
@@ -295,143 +311,157 @@ GEM
validate_url
webfinger (~> 1.2)
orm_adapter (0.5.0)
- ox (2.14.9)
- parallel (1.21.0)
- parser (3.1.0.0)
+ ostruct (0.6.1)
+ overcommit (0.67.1)
+ childprocess (>= 0.6.3, < 6)
+ iniparse (~> 1.4)
+ rexml (>= 3.3.9)
+ ox (2.14.23)
+ bigdecimal (>= 3.0)
+ parallel (1.27.0)
+ parser (3.3.8.0)
ast (~> 2.4.1)
+ racc
parslet (2.0.0)
pastel (0.8.0)
tty-color (~> 0.5)
- pg (1.3.2)
- pry (0.14.1)
+ pg (1.5.9)
+ prism (1.4.0)
+ pry (0.15.2)
coderay (~> 1.1)
method_source (~> 1.0)
- public_suffix (4.0.6)
- puma (5.6.7)
+ public_suffix (6.0.2)
+ puma (5.6.9)
nio4r (~> 2.0)
pyu-ruby-sasl (0.0.3.3)
- racc (1.6.2)
- rack (2.2.6.4)
+ racc (1.8.1)
+ rack (2.2.17)
rack-oauth2 (1.21.3)
activesupport
attr_required
httpclient
json-jwt (>= 1.11.0)
rack (>= 2.1.0)
- rack-protection (3.0.5)
+ rack-protection (3.2.0)
+ base64 (>= 0.1.0)
+ rack (~> 2.2, >= 2.2.4)
+ rack-proxy (0.7.7)
rack
- rack-proxy (0.7.2)
- rack
- rack-test (2.0.2)
+ rack-test (2.2.0)
rack (>= 1.3)
- rails (6.1.4.6)
- actioncable (= 6.1.4.6)
- actionmailbox (= 6.1.4.6)
- actionmailer (= 6.1.4.6)
- actionpack (= 6.1.4.6)
- actiontext (= 6.1.4.6)
- actionview (= 6.1.4.6)
- activejob (= 6.1.4.6)
- activemodel (= 6.1.4.6)
- activerecord (= 6.1.4.6)
- activestorage (= 6.1.4.6)
- activesupport (= 6.1.4.6)
+ rails (7.0.8.7)
+ actioncable (= 7.0.8.7)
+ actionmailbox (= 7.0.8.7)
+ actionmailer (= 7.0.8.7)
+ actionpack (= 7.0.8.7)
+ actiontext (= 7.0.8.7)
+ actionview (= 7.0.8.7)
+ activejob (= 7.0.8.7)
+ activemodel (= 7.0.8.7)
+ activerecord (= 7.0.8.7)
+ activestorage (= 7.0.8.7)
+ activesupport (= 7.0.8.7)
bundler (>= 1.15.0)
- railties (= 6.1.4.6)
- sprockets-rails (>= 2.0.0)
- rails-dom-testing (2.0.3)
- activesupport (>= 4.2.0)
+ railties (= 7.0.8.7)
+ rails-dom-testing (2.3.0)
+ activesupport (>= 5.0.0)
+ minitest
nokogiri (>= 1.6)
- rails-html-sanitizer (1.5.0)
- loofah (~> 2.19, >= 2.19.1)
- railties (6.1.4.6)
- actionpack (= 6.1.4.6)
- activesupport (= 6.1.4.6)
+ rails-html-sanitizer (1.6.2)
+ loofah (~> 2.21)
+ nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
+ railties (7.0.8.7)
+ actionpack (= 7.0.8.7)
+ activesupport (= 7.0.8.7)
method_source
- rake (>= 0.13)
+ rake (>= 12.2)
thor (~> 1.0)
+ zeitwerk (~> 2.5)
rainbow (3.1.1)
- rake (13.0.6)
- rb-fsevent (0.11.1)
- rb-inotify (0.10.1)
+ rake (13.3.0)
+ rb-fsevent (0.11.2)
+ rb-inotify (0.11.1)
ffi (~> 1.0)
- regexp_parser (2.2.1)
- request_store (1.5.1)
- rack (>= 1.4)
- responders (3.0.1)
- actionpack (>= 5.0)
- railties (>= 5.0)
+ regexp_parser (2.10.0)
+ responders (3.1.1)
+ actionpack (>= 5.2)
+ railties (>= 5.2)
rest-client (2.1.0)
http-accept (>= 1.7.0, < 2.0)
http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 4.0)
netrc (~> 0.8)
- rexml (3.2.6)
- rspec (3.11.0)
- rspec-core (~> 3.11.0)
- rspec-expectations (~> 3.11.0)
- rspec-mocks (~> 3.11.0)
- rspec-core (3.11.0)
- rspec-support (~> 3.11.0)
- rspec-expectations (3.11.0)
+ rexml (3.4.1)
+ rspec (3.13.1)
+ rspec-core (~> 3.13.0)
+ rspec-expectations (~> 3.13.0)
+ rspec-mocks (~> 3.13.0)
+ rspec-core (3.13.4)
+ rspec-support (~> 3.13.0)
+ rspec-expectations (3.13.5)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.11.0)
- rspec-its (1.3.0)
+ rspec-support (~> 3.13.0)
+ rspec-its (1.3.1)
rspec-core (>= 3.0.0)
rspec-expectations (>= 3.0.0)
- rspec-mocks (3.11.0)
+ rspec-mocks (3.13.5)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.11.0)
- rspec-rails (4.0.2)
- actionpack (>= 4.2)
- activesupport (>= 4.2)
- railties (>= 4.2)
- rspec-core (~> 3.10)
- rspec-expectations (~> 3.10)
- rspec-mocks (~> 3.10)
- rspec-support (~> 3.10)
- rspec-support (3.11.0)
- rubocop (1.25.1)
+ rspec-support (~> 3.13.0)
+ rspec-rails (6.0.4)
+ actionpack (>= 6.1)
+ activesupport (>= 6.1)
+ railties (>= 6.1)
+ rspec-core (~> 3.12)
+ rspec-expectations (~> 3.12)
+ rspec-mocks (~> 3.12)
+ rspec-support (~> 3.12)
+ rspec-support (3.13.4)
+ rubocop (1.76.0)
+ json (~> 2.3)
+ language_server-protocol (~> 3.17.0.2)
+ lint_roller (~> 1.1.0)
parallel (~> 1.10)
- parser (>= 3.1.0.0)
+ parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
- regexp_parser (>= 1.8, < 3.0)
- rexml
- rubocop-ast (>= 1.15.1, < 2.0)
+ regexp_parser (>= 2.9.3, < 3.0)
+ rubocop-ast (>= 1.45.0, < 2.0)
ruby-progressbar (~> 1.7)
- unicode-display_width (>= 1.4.0, < 3.0)
- rubocop-ast (1.15.2)
- parser (>= 3.0.1.1)
- rubocop-performance (1.13.2)
- rubocop (>= 1.7.0, < 2.0)
- rubocop-ast (>= 0.4.0)
- rubocop-rails (2.13.2)
+ unicode-display_width (>= 2.4.0, < 4.0)
+ rubocop-ast (1.45.1)
+ parser (>= 3.3.7.2)
+ prism (~> 1.4)
+ rubocop-performance (1.25.0)
+ lint_roller (~> 1.1)
+ rubocop (>= 1.75.0, < 2.0)
+ rubocop-ast (>= 1.38.0, < 2.0)
+ rubocop-rails (2.32.0)
activesupport (>= 4.2.0)
+ lint_roller (~> 1.1)
rack (>= 1.1)
- rubocop (>= 1.7.0, < 2.0)
- ruby-progressbar (1.11.0)
+ rubocop (>= 1.75.0, < 2.0)
+ rubocop-ast (>= 1.44.0, < 2.0)
+ ruby-progressbar (1.13.0)
ruby2_keywords (0.0.5)
- ruby_dep (1.5.0)
- ruby_parser (3.18.1)
- sexp_processor (~> 4.16)
- rubyntlm (0.6.3)
- rubyzip (2.3.2)
+ rubyntlm (0.6.5)
+ base64
+ rubyzip (2.4.1)
ruh-roo (3.0.1)
nokogiri (~> 1)
rubyzip (>= 1.3.0, < 3.0.0)
- selenium-webdriver (4.9.0)
+ selenium-webdriver (4.26.0)
+ base64 (~> 0.2)
+ logger (~> 1.4)
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0)
- semantic_range (3.0.0)
- semverse (3.0.0)
+ semantic_range (3.1.0)
+ semverse (3.0.2)
settingslogic (2.0.9)
- sexp_processor (4.16.0)
- simplecov (0.21.2)
+ simplecov (0.22.0)
docile (~> 1.1)
simplecov-html (~> 0.11)
simplecov_json_formatter (~> 0.1)
- simplecov-html (0.12.3)
+ simplecov-html (0.13.1)
simplecov_json_formatter (0.1.4)
slack-ruby-client (1.0.0)
faraday (>= 1.0)
@@ -440,17 +470,13 @@ GEM
hashie
websocket-driver
slack_block_kit (0.3.3)
- spring (2.1.1)
- spring-watcher-listen (2.0.1)
+ snaky_hash (2.0.3)
+ hashie (>= 0.1.0, < 6)
+ version_gem (>= 1.1.8, < 3)
+ spring (4.3.0)
+ spring-watcher-listen (2.1.0)
listen (>= 2.7, < 4.0)
- spring (>= 1.2, < 3.0)
- sprockets (4.0.2)
- concurrent-ruby (~> 1.0)
- rack (> 1, < 3)
- sprockets-rails (3.4.2)
- actionpack (>= 5.2)
- activesupport (>= 5.2)
- sprockets (>= 3.0.0)
+ spring (>= 4)
sslshake (1.3.1)
strings (0.2.1)
strings-ansi (~> 0.2)
@@ -461,18 +487,18 @@ GEM
activesupport (>= 3)
attr_required (>= 0.0.5)
httpclient (>= 2.4)
- temple (0.8.2)
- thor (1.2.1)
- tilt (2.0.10)
- timeout (0.3.2)
- tomlrb (1.3.0)
- train-core (3.8.7)
+ temple (0.10.3)
+ thor (1.3.2)
+ tilt (2.6.0)
+ timeout (0.4.3)
+ tomlrb (2.0.3)
+ train-core (3.12.13)
addressable (~> 2.5)
ffi (!= 1.13.0)
json (>= 1.8, < 3.0)
mixlib-shellout (>= 2.0, < 4.0)
- net-scp (>= 1.2, < 4.0)
- net-ssh (>= 2.9, < 7.0)
+ net-scp (>= 1.2, < 5.0)
+ net-ssh (>= 2.9, < 8.0)
tty-box (0.7.0)
pastel (~> 0.8)
strings (~> 0.2.0)
@@ -486,7 +512,7 @@ GEM
tty-cursor (~> 0.7)
tty-screen (~> 0.8)
wisper (~> 2.0)
- tty-screen (0.8.1)
+ tty-screen (0.8.2)
tty-table (0.12.0)
pastel (~> 0.8)
strings (~> 0.2.0)
@@ -496,12 +522,9 @@ GEM
turbolinks-source (5.2.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
- tzinfo-data (1.2021.5)
+ tzinfo-data (1.2025.2)
tzinfo (>= 1.0.0)
- unf (0.1.4)
- unf_ext
- unf_ext (0.0.8.2)
- unicode-display_width (2.1.0)
+ unicode-display_width (2.6.0)
unicode_utils (1.4.0)
validate_email (0.1.6)
activemodel (>= 3.0)
@@ -509,9 +532,10 @@ GEM
validate_url (1.0.15)
activemodel (>= 3.0.0)
public_suffix
+ version_gem (1.1.8)
warden (1.2.9)
rack (>= 2.0.9)
- web-console (4.2.0)
+ web-console (4.2.1)
actionview (>= 6.0.0)
activemodel (>= 6.0.0)
bindex (>= 0.4.0)
@@ -519,38 +543,38 @@ GEM
webfinger (1.2.0)
activesupport
httpclient (>= 2.4)
- webpacker (5.4.3)
+ webpacker (5.4.4)
activesupport (>= 5.2)
rack-proxy (>= 0.6.1)
railties (>= 5.2)
semantic_range (>= 2.3.0)
- websocket (1.2.9)
- websocket-driver (0.7.5)
+ websocket (1.2.11)
+ websocket-driver (0.8.0)
+ base64
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
wisper (2.0.1)
xpath (3.2.0)
nokogiri (~> 1.8)
- zeitwerk (2.6.8)
+ zeitwerk (2.6.18)
PLATFORMS
- ruby
- x86_64-darwin-19
- x86_64-darwin-20
- x86_64-linux
+ arm64-darwin
+ x86_64-darwin
DEPENDENCIES
activerecord-import
amoeba
- audited (~> 5.3.3)
+ audited (~> 5.8.0)
bootsnap (>= 1.4.2)
brakeman
byebug
- capybara (>= 2.15)
+ capybara
+ concurrent-ruby (= 1.3.4)
database_cleaner-active_record
devise
dotenv-rails
- factory_bot_rails (~> 5.2.0)
+ factory_bot_rails (~> 6.4.0)
fast_excel
ffaker (~> 2.10)
foreman
@@ -559,7 +583,7 @@ DEPENDENCIES
highline (~> 2.0)
jbuilder (~> 2.7)
letter_opener
- listen (~> 3.1.5)
+ listen
mitre-inspec-objects
nokogiri
nokogiri-happymapper
@@ -567,13 +591,16 @@ DEPENDENCIES
omniauth-github
omniauth-rails_csrf_protection (~> 1.0)
omniauth_openid_connect (~> 0.6.0)
+ overcommit (~> 0.60)
ox
pg (>= 0.18, < 2.0)
+ pry
puma (~> 5.6)
- rails (~> 6.1.4)
+ rails (~> 7.0.0)
rest-client
+ rexml
rspec-mocks
- rspec-rails (~> 4.0.0)
+ rspec-rails (~> 6.0.0)
rubocop
rubocop-performance
rubocop-rails
@@ -585,14 +612,14 @@ DEPENDENCIES
slack-ruby-client (= 1.0.0)
slack_block_kit (= 0.3.3)
spring
- spring-watcher-listen (~> 2.0.0)
+ spring-watcher-listen
turbolinks (~> 5)
tzinfo-data
web-console (>= 3.3.0)
webpacker (~> 5.0)
RUBY VERSION
- ruby 2.7.5p203
+ ruby 3.0.6p216
BUNDLED WITH
- 2.3.10
+ 2.5.23
diff --git a/Procfile.dev b/Procfile.dev
index c6abddb0..cd022ede 100644
--- a/Procfile.dev
+++ b/Procfile.dev
@@ -1,2 +1,2 @@
web: bundle exec rails s -p 3000
-webpacker: ./bin/webpack-dev-server
+webpacker: bash -c 'NODE_VERSION=$(node -v | cut -d. -f1 | sed "s/v//"); if [ "$NODE_VERSION" -ge 17 ]; then export NODE_OPTIONS="--openssl-legacy-provider"; fi; ./bin/webpack-dev-server'
diff --git a/README.md b/README.md
index 007c61ef..e93f630d 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,7 @@
# Vulcan
[](https://github.com/mitre/vulcan/actions/workflows/run-tests.yml) [](https://github.com/mitre/vulcan/actions/workflows/push-to-docker.yml)
+
## Description
Vulcan is a tool to help streamline the process of creating STIG-ready securiy guidance documentation and InSpec automated validation profiles.
@@ -33,38 +34,68 @@ For more details on this release and previous ones, check the [Changelog](https:
[Deploying Vulcan in Production](https://vulcan.mitre.org/docs/) [
](https://pages.github.com/)[
](https://pages.github.com/)
-## Deployment Dependencies
+## Development Requirements
+
+### Version Requirements
+* **Ruby**: 3.0.6 (see `.ruby-version`)
+* **Rails**: 7.0.8.7
+* **Node.js**: 16.x (see `.nvmrc`)
+* **PostgreSQL**: 12+
+
+### System Dependencies
-For Ruby (on Ubuntu):
+For development on macOS/Linux:
+* Ruby version manager (rbenv or rvm recommended)
+* Node.js version manager (nvm recommended)
+* Docker (for PostgreSQL) or local PostgreSQL
+* `build-essential` (Linux) or Xcode Command Line Tools (macOS)
+* `libpq-dev` (Linux) or `postgresql` (macOS via Homebrew)
-* Ruby
-* `build-essentials`
-* Bundler
-* `libq-dev`
-* nodejs
+### Quick Development Setup
+
+The easiest way to get started is using our development setup script:
+
+```bash
+# Clone the repository
+git clone https://github.com/mitre/vulcan.git
+cd vulcan
-### Run With Ruby
+# Run the setup script (handles Ruby, Node.js, database setup)
+./bin/dev-setup
-#### Setup Ruby
+# Start the application
+foreman start -f Procfile.dev
+```
-1. Install the version of Ruby specified in `.ruby-version`
-2. Install postgres and rbenv
-3. Run `gem install foreman`
-4. Run `rbenv install`
-5. Run `bin/setup`
+### Manual Setup
- >> **Note**: `bin/setup` will install the JS dependencies andprepare the database.
+If you prefer to set up manually:
-6. Run `rails db:seed` to seed the database.
+1. Install Ruby 3.0.6 (using rbenv or rvm)
+2. Install Node.js 16.x (using nvm)
+3. Install PostgreSQL 12+ (via Docker or locally)
+4. Run `gem install bundler`
+5. Run `bundle install`
+6. Run `yarn install`
+7. Run `bin/rails db:create db:schema:load db:seed`
-#### Running with Ruby
+### Running the Application
Make sure you have run the setup steps at least once before following these steps!
-1. ensure postgres is running
-2. foreman start -f Procfile.dev
+1. Ensure PostgreSQL is running
+2. Start the application: `foreman start -f Procfile.dev`
3. Navigate to `http://127.0.0.1:3000`
+#### Node.js 17+ Compatibility Note
+
+If using Node.js 17 or higher, webpack-dev-server requires a compatibility flag:
+```bash
+NODE_OPTIONS=--openssl-legacy-provider ./bin/webpack-dev-server
+```
+
+Our `Procfile.dev` and `dev-setup` script handle this automatically.
+
#### Test User
For testing purposes in the development environment, you can use the following credentials:
diff --git a/app/controllers/components_controller.rb b/app/controllers/components_controller.rb
index 9e25013d..14c3240e 100644
--- a/app/controllers/components_controller.rb
+++ b/app/controllers/components_controller.rb
@@ -13,12 +13,12 @@ class ComponentsController < ApplicationController
before_action :authorize_admin_component, only: %i[destroy]
before_action :authorize_author_component, only: %i[update]
before_action :check_permission_to_update_slackchannel, only: %i[update]
- before_action :authorize_admin_component, only: %i[update], if: (lambda {
- params
- .require(:component)
- .permit(:advanced_fields)[:advanced_fields]
- .present?
- })
+ before_action :authorize_admin_component, only: %i[update], if: lambda {
+ params
+ .require(:component)
+ .permit(:advanced_fields)[:advanced_fields]
+ .present?
+ }
before_action :authorize_viewer_component, only: %i[show], if: -> { @component.released == false }
before_action :authorize_logged_in, only: %i[search]
@@ -241,7 +241,7 @@ def find
LOWER(status_justification) LIKE ? OR
LOWER(artifact_description) LIKE ? OR
id IN (?) ", "%#{find_param}%", "%#{find_param}%", "%#{find_param}%", "%#{find_param}%",
- "%#{find_param}%", (checks.pluck(:base_rule_id) | descriptions.pluck(:base_rule_id))
+ "%#{find_param}%", checks.pluck(:base_rule_id) | descriptions.pluck(:base_rule_id)
)
.order(:rule_id)
@@ -353,7 +353,7 @@ def set_project
end
def check_permission_to_update_slackchannel
- return if component_update_params[:component_metadata_attributes]&.dig('data')&.dig('Slack Channel ID').blank?
+ return if component_update_params[:component_metadata_attributes]&.dig('data', 'Slack Channel ID').blank?
authorize_admin_component
end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index a043068c..d0483be3 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -182,8 +182,8 @@ def project_params
end
def check_permission_to_update
- condition = (project_params[:project_metadata_attributes]&.dig('data')&.dig('Slack Channel ID').present? ||
- project_params[:visibility].present?)
+ condition = project_params[:project_metadata_attributes]&.dig('data', 'Slack Channel ID').present? ||
+ project_params[:visibility].present?
authorize_admin_project if condition
end
diff --git a/app/controllers/rules_controller.rb b/app/controllers/rules_controller.rb
index 548a28e9..2e9657c9 100644
--- a/app/controllers/rules_controller.rb
+++ b/app/controllers/rules_controller.rb
@@ -130,12 +130,12 @@ def create_or_duplicate
srg_rule = srg.parsed_benchmark.rule.find { |r| r.ident.reject(&:legacy).first.ident == 'CCI-000366' }
rule = BaseRule.from_mapping(Rule, srg_rule)
- rule.audits.build(Audited.audit_class.create_initial_rule_audit_from_mapping(@component.id))
rule.component = @component
rule.srg_rule = srg.srg_rules.find_by(ident: 'CCI-000366')
rule.rule_id = (@component.rules.order(:rule_id).pluck(:rule_id).last.to_i + 1)&.to_s&.rjust(6, '0')
rule.status = 'Not Yet Determined'
rule.rule_severity = 'unknown'
+ rule.audit_comment = 'Created new rule'
rule
end
diff --git a/app/controllers/security_requirements_guides_controller.rb b/app/controllers/security_requirements_guides_controller.rb
index 9248da2d..00ffe4e3 100644
--- a/app/controllers/security_requirements_guides_controller.rb
+++ b/app/controllers/security_requirements_guides_controller.rb
@@ -6,7 +6,7 @@ class SecurityRequirementsGuidesController < ApplicationController
before_action :security_requirements_guide, only: %i[destroy]
def index
- @srgs = SecurityRequirementsGuide.all.order(:srg_id, :version).select(:id, :srg_id, :title, :version, :release_date)
+ @srgs = SecurityRequirementsGuide.order(:srg_id, :version).select(:id, :srg_id, :title, :version, :release_date)
respond_to do |format|
format.html
format.json { render json: @srgs }
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index c94afc2d..99ec2248 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -49,7 +49,7 @@ def build_oidc_logout_url(id_token)
}
# Add client_id if available (required by some providers)
- client_id = Settings.oidc.args.client_options.identifier || ENV['VULCAN_OIDC_CLIENT_ID']
+ client_id = Settings.oidc.args.client_options.identifier || ENV.fetch('VULCAN_OIDC_CLIENT_ID', nil)
params[:client_id] = client_id if client_id.present?
"#{logout_endpoint}?#{params.to_query}"
@@ -62,7 +62,7 @@ def fetch_oidc_logout_endpoint
return session[:oidc_logout_endpoint]
end
- issuer_url = Settings.oidc.args.issuer || ENV['VULCAN_OIDC_ISSUER_URL']
+ issuer_url = Settings.oidc.args.issuer || ENV.fetch('VULCAN_OIDC_ISSUER_URL', nil)
discovery_url = "#{issuer_url.to_s.chomp('/')}/.well-known/openid-configuration"
Rails.logger.info "Fetching OIDC discovery document from: #{discovery_url}"
diff --git a/app/controllers/stigs_controller.rb b/app/controllers/stigs_controller.rb
index cde52d1c..f48b9019 100644
--- a/app/controllers/stigs_controller.rb
+++ b/app/controllers/stigs_controller.rb
@@ -6,7 +6,7 @@ class StigsController < ApplicationController
before_action :set_stig, only: %i[show destroy]
def index
- @stigs = Stig.all.order(:stig_id, :version).select(:id, :stig_id, :title, :version, :benchmark_date)
+ @stigs = Stig.order(:stig_id, :version).select(:id, :stig_id, :title, :version, :benchmark_date)
respond_to do |format|
format.html
format.json { render json: @stigs }
@@ -56,7 +56,7 @@ def set_stig
@stig = Stig.find_by(id: params[:id])
return unless @stig.nil?
- flash[:alert] = 'STIG not found'
+ flash[:alert] = t('controllers.stigs.not_found')
redirect_to stigs_path
end
end
diff --git a/app/errors/not_authorized_error.rb b/app/errors/not_authorized_error.rb
index 52f898c3..ac5a60f9 100644
--- a/app/errors/not_authorized_error.rb
+++ b/app/errors/not_authorized_error.rb
@@ -3,6 +3,6 @@
# Raised when a Rule cannot be successfully rolled back to a previous state
class NotAuthorizedError < StandardError
def initialize(message = 'You are not authorized to perform this action.')
- super message
+ super
end
end
diff --git a/app/errors/rule_revert_error.rb b/app/errors/rule_revert_error.rb
index 4a81b0f5..7f65114e 100644
--- a/app/errors/rule_revert_error.rb
+++ b/app/errors/rule_revert_error.rb
@@ -3,6 +3,6 @@
# Raised when a Rule cannot be successfully rolled back to a previous state
class RuleRevertError < StandardError
def initialize(message = 'Could not revert history for rule.')
- super message
+ super
end
end
diff --git a/app/helpers/export_helper.rb b/app/helpers/export_helper.rb
index ed885d0d..36ae8025 100644
--- a/app/helpers/export_helper.rb
+++ b/app/helpers/export_helper.rb
@@ -101,10 +101,10 @@ def get_check_and_fix_text(status)
def export_xccdf_project(project)
Zip::OutputStream.write_buffer do |zio|
project.components.eager_load(rules: %i[disa_rule_descriptions checks
- satisfies satisfied_by]).each do |component|
+ satisfies satisfied_by]).find_each do |component|
version = component[:version] ? "V#{component[:version]}" : ''
release = component[:release] ? "R#{component[:release]}" : ''
- title = (component[:title] || "#{component[:name]} STIG Readiness Guide")
+ title = component[:title] || "#{component[:name]} STIG Readiness Guide"
file_name = "U_#{title.tr(' ', '_')}_#{version}#{release}-xccdf.xml"
zio.put_next_entry(file_name)
@@ -119,7 +119,7 @@ def export_xccdf_project(project)
def export_inspec_project(project)
Zip::OutputStream.write_buffer do |zio|
project.components.eager_load(rules: %i[disa_rule_descriptions checks
- satisfies satisfied_by]).each do |component|
+ satisfies satisfied_by]).find_each do |component|
version = component[:version] ? "V#{component[:version]}" : ''
release = component[:release] ? "R#{component[:release]}" : ''
dir = "#{component[:name].tr(' ', '-')}-#{version}#{release}-stig-baseline/"
@@ -190,7 +190,7 @@ def xccdf_helper(component)
benchmark['xmlns'] = 'http://checklists.nist.gov/xccdf/1.1'
ox_el_helper(benchmark, 'status', 'draft', { date: Time.zone.today.strftime('%Y-%m-%d') })
- title = (component[:title] || "#{component[:name]} STIG Readiness Guide")
+ title = component[:title] || "#{component[:name]} STIG Readiness Guide"
ox_el_helper(benchmark, 'title', title)
ox_el_helper(benchmark, 'description', component[:description] || title)
ox_el_helper(benchmark, 'notice', nil, { id: 'terms-of-use', 'xml:lang': 'en' })
diff --git a/app/lib/cci_map/constants.rb b/app/lib/cci_map/constants.rb
index 9d26757d..84885843 100644
--- a/app/lib/cci_map/constants.rb
+++ b/app/lib/cci_map/constants.rb
@@ -3,6 +3,7 @@
# rubocop:disable Metrics/ModuleLength
module CciMap
module Constants
+ # rubocop:disable Metrics/CollectionLiteralLength
CCI_TO_NIST_CONSTANT = {
'CCI-000001': 'AC-1 a 1',
'CCI-000002': 'AC-1 a 1 (a)',
@@ -5105,6 +5106,7 @@ module Constants
'CCI-005149': 'AU-16 (3)',
'CCI-005150': 'PM-30 (1)'
}.freeze
+ # rubocop:enable Metrics/CollectionLiteralLength
end
end
# rubocop:enable Metrics/ModuleLength
diff --git a/app/lib/vulcan_audit.rb b/app/lib/vulcan_audit.rb
index 971ce72e..b463d607 100644
--- a/app/lib/vulcan_audit.rb
+++ b/app/lib/vulcan_audit.rb
@@ -1,21 +1,51 @@
# frozen_string_literal: true
+require 'audited/audit'
+
# Custom Audited class for Vulcan-specific methods for interacting with audits.
-class VulcanAudit < ::Audited::Audit
+class VulcanAudit < Audited::Audit
belongs_to :audited_user, class_name: 'User', optional: true
- before_create :set_username, :find_and_save_audited_user, :find_and_save_associated_rule
- def self.create_initial_rule_audit_from_mapping(project_id)
- {
- auditable_type: 'Rule',
- action: 'create',
- user_type: 'System',
- audited_changes: {
- project_id: project_id
- }
- }
+ # In Rails 5+, belongs_to associations are required by default.
+ # The parent Audited::Audit class defines:
+ # - `belongs_to :user, polymorphic: true`
+ # - `belongs_to :associated, polymorphic: true`
+ # Both become required. We need to allow nil for system-generated audits.
+
+ # Override the associations to make them optional
+ belongs_to :user, polymorphic: true, optional: true
+ belongs_to :associated, polymorphic: true, optional: true
+
+ # Force removal of presence validations that Rails adds automatically
+ # This needs to be done with a more aggressive approach since Rails keeps re-adding them
+ def self.remove_presence_validations!
+ # Remove all presence validators for user and associated
+ %i[user associated].each do |attr|
+ _validators[attr] = _validators[attr]&.reject { |v| v.is_a?(ActiveRecord::Validations::PresenceValidator) } || []
+
+ # Remove from callback chain (Rails 7 compatible)
+ callbacks_to_remove = _validate_callbacks.select do |callback|
+ callback.filter.is_a?(ActiveRecord::Validations::PresenceValidator) &&
+ callback.filter.attributes.include?(attr)
+ end
+
+ callbacks_to_remove.each do |callback|
+ _validate_callbacks.delete(callback)
+ end
+ end
+ end
+
+ # Call it immediately after class definition
+ remove_presence_validations!
+
+ # Also remove them in inherited classes
+ def self.inherited(subclass)
+ super
+ subclass.remove_presence_validations!
end
+ before_create :set_username, :find_and_save_audited_user, :find_and_save_associated_rule
+
def set_username
self.username = user&.name
end
diff --git a/app/lib/xccdf/idref/overrideable_idref.rb b/app/lib/xccdf/idref/overrideable_idref.rb
index 61f58c81..23ddb6a3 100644
--- a/app/lib/xccdf/idref/overrideable_idref.rb
+++ b/app/lib/xccdf/idref/overrideable_idref.rb
@@ -5,6 +5,8 @@ module Xccdf
# just a mandatory URI reference, but also have
# an override attribute for controlling inheritance.
class Idref
+ ##
+ # Handles IDREF elements that support override attributes
class OverrideableIdref < Idref
include HappyMapper
diff --git a/app/lib/xccdf/item/selectable_item.rb b/app/lib/xccdf/item/selectable_item.rb
index 0f15d65a..fae2957d 100644
--- a/app/lib/xccdf/item/selectable_item.rb
+++ b/app/lib/xccdf/item/selectable_item.rb
@@ -4,6 +4,7 @@ module Xccdf
# This abstract item type represents the basic data shared by all
# Groups and Rules.
class Item
+ # Abstract base class for selectable XCCDF items (Groups and Rules)
class SelectableItem < Item
include HappyMapper
diff --git a/app/lib/xccdf/item/selectable_item/group.rb b/app/lib/xccdf/item/selectable_item/group.rb
index 6f03b86c..7bcdff7c 100644
--- a/app/lib/xccdf/item/selectable_item/group.rb
+++ b/app/lib/xccdf/item/selectable_item/group.rb
@@ -5,6 +5,7 @@ module Xccdf
# Groups, Rules and Values.
class Item
class SelectableItem
+ # Represents a grouping of Groups, Rules and Values in XCCDF
class Group < SelectableItem
include HappyMapper
diff --git a/app/lib/xccdf/item/selectable_item/rule.rb b/app/lib/xccdf/item/selectable_item/rule.rb
index 021b8470..b8483bc8 100644
--- a/app/lib/xccdf/item/selectable_item/rule.rb
+++ b/app/lib/xccdf/item/selectable_item/rule.rb
@@ -5,6 +5,7 @@ module Xccdf
# specific benchmark test.
class Item
class SelectableItem
+ # Represents a specific benchmark test in XCCDF
class Rule < SelectableItem
include HappyMapper
diff --git a/app/lib/xccdf/item/value.rb b/app/lib/xccdf/item/value.rb
index 475cddf0..97ebbd52 100644
--- a/app/lib/xccdf/item/value.rb
+++ b/app/lib/xccdf/item/value.rb
@@ -4,6 +4,7 @@ module Xccdf
# Data type for the Value element, which represents a
# tailorable string, boolean, or number in the Benchmark.
class Item
+ # Represents a tailorable value in an XCCDF benchmark
class Value < Item
include HappyMapper
diff --git a/app/lib/xccdf/warning.rb b/app/lib/xccdf/warning.rb
index b1080adc..d7f2857f 100644
--- a/app/lib/xccdf/warning.rb
+++ b/app/lib/xccdf/warning.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
module Xccdf
+ # Represents warnings with various categories in XCCDF
class Warning
include HappyMapper
diff --git a/app/models/additional_answer.rb b/app/models/additional_answer.rb
index 85113770..45ad2535 100644
--- a/app/models/additional_answer.rb
+++ b/app/models/additional_answer.rb
@@ -9,8 +9,8 @@ class AdditionalAnswer < ApplicationRecord
belongs_to :additional_question
belongs_to :rule
- URL_REGEXP = %r{\A(http|https)://[a-z0-9@:%._+~#=]{2,256}\.[a-z]{2,16}\b([-a-z0-9@:%_+.~#?&/=]*)\z}ix.freeze
- validates :answer, format: { with: URL_REGEXP, message: 'URL must be valid and begin with http or https' },
+ URL_REGEXP = %r{\A(http|https)://[a-z0-9@:%._+~#=]{2,256}\.[a-z]{2,16}\b([-a-z0-9@:%_+.~#?&/=]*)\z}ix
+ validates :answer, format: { with: URL_REGEXP },
if: :present_and_type_is_url?
def present_and_type_is_url?
diff --git a/app/models/additional_question.rb b/app/models/additional_question.rb
index e007c903..34400cc2 100644
--- a/app/models/additional_question.rb
+++ b/app/models/additional_question.rb
@@ -11,7 +11,7 @@ class AdditionalQuestion < ApplicationRecord
FIELD_TYPES = %w[dropdown freeform url].freeze
- enum question_type: FIELD_TYPES.zip(FIELD_TYPES).to_h
+ enum :question_type, FIELD_TYPES.zip(FIELD_TYPES).to_h
validates :name, :question_type, presence: true
diff --git a/app/models/base_rule.rb b/app/models/base_rule.rb
index 36e5d1ae..387fa50d 100644
--- a/app/models/base_rule.rb
+++ b/app/models/base_rule.rb
@@ -49,7 +49,7 @@ def self.from_mapping(rule_class, rule_mapping)
title: rule_mapping.title.first || nil,
ident: rule_mapping.ident.reject(&:legacy).map(&:ident).sort.join(', '),
legacy_ids: rule_mapping.ident.select(&:legacy).map(&:ident).join(', '),
- ident_system: rule_mapping.ident&.reject(&:legacy)&.first&.system,
+ ident_system: rule_mapping.ident&.reject(&:legacy)&.first.try(:system),
fixtext: rule_mapping.fixtext.first&.fixtext,
fixtext_fixref: rule_mapping.fixtext.first&.fixref,
fix_id: rule_mapping.fix.first&.id
@@ -81,8 +81,7 @@ def as_json(options = {})
def nist_control_family
ccis = ident.to_s.split(/, */)
- ia_controls = []
- ccis.each { |cci| ia_controls << CCI_TO_NIST_CONSTANT[cci.to_sym] }
+ ia_controls = ccis.map { |cci| CCI_TO_NIST_CONSTANT[cci.to_sym] }
ia_controls.uniq.join(', ')
end
diff --git a/app/models/check.rb b/app/models/check.rb
index da3bbfe2..143577d2 100644
--- a/app/models/check.rb
+++ b/app/models/check.rb
@@ -11,8 +11,8 @@ class Check < ApplicationRecord
def self.from_mapping(check_mapping)
{
system: check_mapping&.system,
- content_ref_name: check_mapping&.check_content_ref&.first&.name,
- content_ref_href: check_mapping&.check_content_ref&.first&.href,
+ content_ref_name: check_mapping&.check_content_ref&.first.try(:name),
+ content_ref_href: check_mapping&.check_content_ref&.first.try(:href),
content: check_mapping&.check_content&.content
}
end
diff --git a/app/models/component.rb b/app/models/component.rb
index ed4e3ce5..31a2ad86 100644
--- a/app/models/component.rb
+++ b/app/models/component.rb
@@ -97,7 +97,7 @@ def from_spreadsheet(spreadsheet)
return
end
- spreadsheet_srg_ids = parsed.map { |row| row[IMPORT_MAPPING[:srg_id]] }
+ spreadsheet_srg_ids = parsed.pluck(IMPORT_MAPPING[:srg_id])
database_srg_ids = srg_rules.map(&:version)
missing_from_srg = spreadsheet_srg_ids - database_srg_ids
@@ -118,7 +118,7 @@ def from_spreadsheet(spreadsheet)
end
# Calculate the prefix (which will need to be removed from each row)
- possible_prefixes = parsed.collect { |row| row[IMPORT_MAPPING[:stig_id]] }.compact_blank
+ possible_prefixes = parsed.pluck(IMPORT_MAPPING[:stig_id]).compact_blank
if possible_prefixes.empty?
errors.add(:base, 'No STIG prefixes were detected in the file. Please set any STIGID ' \
'in the file and try again.')
@@ -230,7 +230,7 @@ def releasable
return false if released_was
# If all rules are locked, then component may be released
- rules.where(locked: false).size.zero?
+ rules.where(locked: false).empty?
end
def duplicate(new_name: nil, new_prefix: nil, new_version: nil, new_release: nil,
@@ -391,6 +391,10 @@ def from_mapping(srg, new_rule_versions = nil, starting_idx = 0)
end
success
rescue StandardError => e
+ # Log the full error for debugging
+ Rails.logger.error "Import error: #{e.class}: #{e.message}"
+ Rails.logger.error e.backtrace.join("\n")
+
message = e.message[0, 50]
message += '...' if e.message.size >= 50
errors.add(:base, "Encountered an error when importing rules from the SRG: #{message}")
@@ -402,8 +406,9 @@ def largest_rule_id
if id.nil?
rules.collect { |rule| rule.rule_id.to_i }.max
else
- Rule.connection.execute("SELECT MAX(TO_NUMBER(rule_id, '999999')) FROM base_rules
- WHERE component_id = #{id}")&.values&.flatten&.first&.to_i || 0
+ result = Rule.connection.execute("SELECT MAX(TO_NUMBER(rule_id, '999999')) FROM base_rules
+ WHERE component_id = #{id}")
+ result&.values&.flatten.try(:first).to_i
end
end
diff --git a/app/models/component_metadata.rb b/app/models/component_metadata.rb
index 0d16818b..326aee81 100644
--- a/app/models/component_metadata.rb
+++ b/app/models/component_metadata.rb
@@ -1,7 +1,9 @@
# frozen_string_literal: true
+##
+# ComponentMetadata stores additional metadata for components in a flexible JSON structure
class ComponentMetadata < ApplicationRecord
belongs_to :component
- validates :component, uniqueness: { message: 'already has associated metadata' }
+ validates :component, uniqueness: true
end
diff --git a/app/models/concerns/prefix_validator.rb b/app/models/concerns/prefix_validator.rb
index ed5435ef..c0c77436 100644
--- a/app/models/concerns/prefix_validator.rb
+++ b/app/models/concerns/prefix_validator.rb
@@ -3,7 +3,7 @@
# Validates that a project prefix is in the correct format
class PrefixValidator < ActiveModel::Validator
def validate(record)
- return if record.prefix.respond_to?(:match?) && validate_prefix(record.prefix)
+ return if record.prefix.respond_to?(:match?) && validate_prefix?(record.prefix)
record.errors.add(:base, 'Prefix must be of the form AAAA-00')
end
@@ -12,7 +12,7 @@ def validate(record)
# Prefixes are 4 alphanumeric characters, followed by a dash, followed by 2 alphanumeric characters.
# Ex. abcd-01
- def validate_prefix(prefix)
+ def validate_prefix?(prefix)
return true if prefix.match?(/^\w{4}-\w{2}$/)
false
diff --git a/app/models/membership.rb b/app/models/membership.rb
index ebd25ac5..02bc6326 100644
--- a/app/models/membership.rb
+++ b/app/models/membership.rb
@@ -22,12 +22,14 @@ class Membership < ApplicationRecord
validates :role, inclusion: {
in: PROJECT_MEMBER_ROLES,
- message: "is not an acceptable value. Acceptable values are: #{PROJECT_MEMBER_ROLES.join(', ')}"
+ message: lambda do |_object, _data|
+ I18n.t('activerecord.errors.models.membership.attributes.role.inclusion',
+ acceptable_values: PROJECT_MEMBER_ROLES.join(', '))
+ end
}
validates :user, uniqueness: {
- scope: %i[membership_type membership_id],
- message: 'is already a member of this project.'
+ scope: %i[membership_type membership_id]
}
##
@@ -35,7 +37,7 @@ class Membership < ApplicationRecord
# This is useful for the ProjectMember.vue component.
#
def as_json(options = {})
- super options.merge(methods: %i[name email])
+ super(options.merge(methods: %i[name email]))
end
private
@@ -89,8 +91,8 @@ def cannot_have_equal_or_lesser_component_permissions
errors.add(
:role,
- "provides equal or lesser permissions compared to the role the user's current project level role"\
- " (#{project_membership_role}). This permission would have no effect on the user's abilities."
+ I18n.t('activerecord.errors.models.membership.attributes.role.equal_or_lesser_permissions',
+ project_role: project_membership_role)
)
end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 16a5643d..b2f01c48 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -4,7 +4,7 @@
class Project < ApplicationRecord
attr_accessor :current_user
- enum visibility: { discoverable: 0, hidden: 1 }
+ enum :visibility, { discoverable: 0, hidden: 1 }
audited except: %i[id admin_name admin_email memberships_count created_at updated_at], max_audits: 1000
@@ -48,7 +48,7 @@ def update_admin_contact_info
# Get a list of Users that are not yet members of this project
#
def available_members
- (User.all.select(:id, :name, :email) - users.select(:id, :name, :email))
+ (User.select(:id, :name, :email) - users.select(:id, :name, :email))
end
def details
diff --git a/app/models/project_access_request.rb b/app/models/project_access_request.rb
index 62312d39..2685512d 100644
--- a/app/models/project_access_request.rb
+++ b/app/models/project_access_request.rb
@@ -1,8 +1,10 @@
# frozen_string_literal: true
+##
+# ProjectAccessRequest represents a user's request to access a specific project
class ProjectAccessRequest < ApplicationRecord
belongs_to :user
belongs_to :project
- validates :user_id, uniqueness: { scope: :project_id, message: 'has already requested access to this project' }
+ validates :user_id, uniqueness: { scope: :project_id }
end
diff --git a/app/models/project_metadata.rb b/app/models/project_metadata.rb
index 4ce886af..46a1353c 100644
--- a/app/models/project_metadata.rb
+++ b/app/models/project_metadata.rb
@@ -1,7 +1,9 @@
# frozen_string_literal: true
+##
+# ProjectMetadata stores additional metadata for projects in a flexible JSON structure
class ProjectMetadata < ApplicationRecord
belongs_to :project
- validates :project, uniqueness: { message: 'already has associated metadata' }
+ validates :project, uniqueness: true
end
diff --git a/app/models/review.rb b/app/models/review.rb
index a5a78b8d..915da0e8 100644
--- a/app/models/review.rb
+++ b/app/models/review.rb
@@ -23,7 +23,7 @@ class Review < ApplicationRecord
# Override `as_json` to include delegated attributes
#
def as_json(options = {})
- super options.merge(methods: %i[name])
+ super(options.merge(methods: %i[name]))
end
private
diff --git a/app/models/rule.rb b/app/models/rule.rb
index 7a0ac107..76d1e83f 100644
--- a/app/models/rule.rb
+++ b/app/models/rule.rb
@@ -62,12 +62,14 @@ def update_single_rule_clone(rule_clone)
def self.from_mapping(rule_mapping, component_id, idx, srg_rules)
rule = super(self, rule_mapping)
- rule.audits.build(Audited.audit_class.create_initial_rule_audit_from_mapping(component_id))
rule.component_id = component_id
rule.srg_rule_id = srg_rules[rule.rule_id]
# This is what is appended to the component prefix in the UI
rule.rule_id = idx&.to_s&.rjust(6, '0')
+ # Set audit comment to indicate this was created from SRG mapping
+ rule.audit_comment = 'Created from SRG mapping'
+
rule
end
@@ -77,14 +79,14 @@ def status
end
def status=(value)
- super(value) unless satisfied_by.size.positive?
+ super unless satisfied_by.size.positive?
end
##
# Override `as_json` to include parent SRG information
#
def as_json(options = {})
- result = super(options)
+ result = super
unless options[:skip_merge].eql?(true)
result = result.merge(
{
diff --git a/app/models/security_requirements_guide.rb b/app/models/security_requirements_guide.rb
index bc399cb1..d25b0d4d 100644
--- a/app/models/security_requirements_guide.rb
+++ b/app/models/security_requirements_guide.rb
@@ -10,8 +10,7 @@ class SecurityRequirementsGuide < ApplicationRecord
validates :srg_id, :title, :version, :xml, presence: true
validates :srg_id, uniqueness: {
- scope: :version,
- message: ' ID has already been taken'
+ scope: :version
}
# Since an SRG is top-level, the parameter is the entire parsed benchmark
diff --git a/app/models/stig.rb b/app/models/stig.rb
index 3e6669dc..1ccc950e 100644
--- a/app/models/stig.rb
+++ b/app/models/stig.rb
@@ -6,8 +6,7 @@ class Stig < ApplicationRecord
validates :stig_id, :title, :name, :version, :xml, presence: true
validates :stig_id, uniqueness: {
- scope: :version,
- message: 'ID has already been taken'
+ scope: :version
}
after_create :import_stig_rules
diff --git a/app/views/components/show.html.haml b/app/views/components/show.html.haml
index d14f45de..e319cf01 100644
--- a/app/views/components/show.html.haml
+++ b/app/views/components/show.html.haml
@@ -4,7 +4,7 @@
#projectcomponent
%projectcomponent{ |
- 'v-bind:queried-rule': @rule_json, |
+ 'v-bind:queried-rule': (@rule_json || 'null'), |
'v-bind:effective_permissions': @effective_permissions.to_json, |
'v-bind:initial-component-state': @component_json, |
'v-bind:project': @project_json, |
diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml
index 579ff86b..9ed233ca 100644
--- a/app/views/devise/sessions/new.html.haml
+++ b/app/views/devise/sessions/new.html.haml
@@ -25,11 +25,11 @@
%b-card-text
= render 'devise/sessions/ldap'
- if local_login_enabled?
- %b-tab{:active => params[:active_tab].eql?('local') ? true : false, :title => "Local Login"}
+ %b-tab{'v-bind:active' => params[:active_tab].eql?('local').to_s, :title => "Local Login"}
%b-card-text
= render 'devise/sessions/local'
- if user_registration_enabled?
- %b-tab{:active => params[:active_tab].eql?('registration') ? true : false, :title => "Register"}
+ %b-tab{'v-bind:active' => params[:active_tab].eql?('registration').to_s, :title => "Register"}
%b-card-text
= render 'devise/registrations/form'
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index 0021533a..86e1b583 100644
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -18,7 +18,7 @@
%navbar{ |
'v-bind:navigation': @navigation.to_json, |
'v-bind:signed_in': user_signed_in?.to_s, |
- 'v-bind:users_path': current_user&.admin ? users_path.to_json : nil, |
+ 'v-bind:users_path': (current_user&.admin ? users_path : '').to_json, |
'v-bind:profile_path': edit_user_registration_path.to_json, |
'v-bind:sign_out_path': destroy_user_session_path.to_json, |
'v-bind:access_requests': @access_requests.to_json |
diff --git a/app/views/stigs/show.html.haml b/app/views/stigs/show.html.haml
index 4cfe9aa7..d3a0d20e 100644
--- a/app/views/stigs/show.html.haml
+++ b/app/views/stigs/show.html.haml
@@ -4,7 +4,7 @@
#stig
%stig{ |
- 'v-bind:queried-rule': @rule_json, |
+ 'v-bind:queried-rule': (@rule_json || 'null'), |
'v-bind:stig': @stig_json, |
'v-bind:severities': RuleConstants::SEVERITIES.to_json, |
'v-bind:severities_map': RuleConstants::SEVERITIES_MAP.to_json, |
diff --git a/bin/dev-setup b/bin/dev-setup
new file mode 100755
index 00000000..16133928
--- /dev/null
+++ b/bin/dev-setup
@@ -0,0 +1,317 @@
+#!/bin/bash
+#
+# Vulcan Development Environment Setup Script
+#
+# This script sets up the complete Vulcan development environment including:
+# - Ruby version management (rbenv/rvm)
+# - Node.js version management (nvm)
+# - PostgreSQL database via Docker
+# - Authentication configuration (Local/LDAP/Okta)
+# - Database initialization with seed data
+#
+# For a simpler Rails-only setup, use: bin/setup
+#
+
+# Script usage
+usage() {
+ echo "Usage: $0 [OPTIONS]"
+ echo ""
+ echo "Options:"
+ echo " --clean Stop and remove existing containers before starting"
+ echo " --refresh Reset database (drops and recreates)"
+ echo " --okta Configure environment for Okta authentication"
+ echo " --ldap Configure environment for LDAP authentication"
+ echo " --local Configure environment for local authentication (default)"
+ echo " --help Show this help message"
+ echo ""
+ echo "Examples:"
+ echo " $0 # Start with local auth"
+ echo " $0 --okta # Start with Okta auth"
+ echo " $0 --ldap # Start with LDAP auth"
+ echo " $0 --clean --okta # Clean start with Okta"
+ exit 1
+}
+
+# Parse command line arguments
+CLEAN=false
+REFRESH=false
+AUTH_MODE="local"
+
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --clean)
+ CLEAN=true
+ shift
+ ;;
+ --refresh)
+ REFRESH=true
+ shift
+ ;;
+ --okta)
+ AUTH_MODE="okta"
+ shift
+ ;;
+ --ldap)
+ AUTH_MODE="ldap"
+ shift
+ ;;
+ --local)
+ AUTH_MODE="local"
+ shift
+ ;;
+ --help)
+ usage
+ ;;
+ *)
+ echo "Unknown option: $1"
+ usage
+ ;;
+ esac
+done
+
+echo "Starting Vulcan Development Environment (Auth: $AUTH_MODE)..."
+
+# Clean up if requested or if containers are already running
+if [ "$CLEAN" = true ] || docker-compose -f docker-compose.dev.yml ps -q | grep -q .; then
+ if [ "$CLEAN" = true ]; then
+ echo "Cleaning up existing containers (--clean flag)..."
+ else
+ echo "Found existing containers, cleaning up..."
+ fi
+ docker-compose -f docker-compose.dev.yml down
+fi
+
+# Check Ruby version
+REQUIRED_RUBY=$(cat .ruby-version 2>/dev/null || echo "2.7.5")
+CURRENT_RUBY=$(ruby -v 2>/dev/null | awk '{print $2}' | cut -d'p' -f1)
+
+echo "Required Ruby version: $REQUIRED_RUBY"
+echo "Current Ruby version: $CURRENT_RUBY"
+
+# Check for rbenv or rvm
+if command -v rbenv &> /dev/null; then
+ echo "Using rbenv..."
+ # Ensure rbenv loads the correct version
+ eval "$(rbenv init -)"
+ if ! rbenv versions | grep -q "$REQUIRED_RUBY"; then
+ echo "Installing Ruby $REQUIRED_RUBY with rbenv..."
+ rbenv install "$REQUIRED_RUBY"
+ fi
+ rbenv local "$REQUIRED_RUBY"
+elif command -v rvm &> /dev/null; then
+ echo "Using rvm..."
+ # Load RVM if not already loaded
+ if ! type rvm | grep -q 'function'; then
+ [[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"
+ fi
+
+ # Use rvm use . to respect .ruby-version and .ruby-gemset files
+ echo "Loading RVM settings from .ruby-version and .ruby-gemset..."
+ rvm use .
+
+ # If the required Ruby isn't installed, install it
+ if ! rvm list | grep -q "$REQUIRED_RUBY"; then
+ echo "Installing Ruby $REQUIRED_RUBY..."
+ rvm install "$REQUIRED_RUBY"
+ rvm use .
+ fi
+else
+ echo "WARNING: Neither rbenv nor rvm detected!"
+ echo "Please ensure Ruby $REQUIRED_RUBY is installed and active"
+ if [ "$CURRENT_RUBY" != "$REQUIRED_RUBY" ]; then
+ echo "ERROR: Wrong Ruby version. Expected $REQUIRED_RUBY but got $CURRENT_RUBY"
+ exit 1
+ fi
+fi
+
+# Verify Ruby version
+CURRENT_RUBY=$(ruby -v | awk '{print $2}' | cut -d'p' -f1)
+if [ "$CURRENT_RUBY" != "$REQUIRED_RUBY" ]; then
+ echo "ERROR: Failed to switch to Ruby $REQUIRED_RUBY"
+ exit 1
+fi
+
+echo "✓ Using Ruby $CURRENT_RUBY with gemset: $(rvm current | cut -d'@' -f2)"
+
+# Check Node.js version
+if command -v nvm &> /dev/null; then
+ echo "Checking Node.js version..."
+ # Load NVM if not already loaded
+ if ! type nvm | grep -q 'function'; then
+ export NVM_DIR="$HOME/.nvm"
+ [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
+ fi
+
+ # Use .nvmrc or default to Node 16
+ if [ -f .nvmrc ]; then
+ nvm use
+ else
+ echo "No .nvmrc found, using Node 16 to match production..."
+ nvm use 16
+ fi
+
+ echo "✓ Using Node.js $(node -v)"
+else
+ echo "WARNING: NVM not found. Please ensure Node.js 16.x is installed"
+ echo "Current Node.js: $(node -v 2>/dev/null || echo 'Not installed')"
+fi
+
+# Check and install Ruby dependencies
+echo "Checking Ruby dependencies..."
+bundle check || {
+ echo "Installing Ruby dependencies..."
+ bundle install
+}
+
+# Check and install JavaScript dependencies
+echo "Checking JavaScript dependencies..."
+if [ ! -d "node_modules" ] || [ "package.json" -nt "node_modules" ]; then
+ echo "Installing JavaScript dependencies..."
+ yarn install
+fi
+
+# Configure environment based on auth mode
+case $AUTH_MODE in
+ okta)
+ # Check if .env.development exists, if not copy from .env.okta.dev
+ if [ ! -f .env.development ]; then
+ echo "Creating .env.development from .env.okta.dev template..."
+ cp .env.okta.dev .env.development
+ fi
+
+ # Check for Okta credentials
+ if [ -f .env.development.local ]; then
+ echo "Loading Okta credentials from .env.development.local"
+ # shellcheck disable=SC2046
+ export $(grep -v '^#' .env.development.local | xargs)
+ else
+ echo "WARNING: .env.development.local not found!"
+ echo "Please add your Okta credentials to .env.development.local:"
+ echo " VULCAN_OIDC_CLIENT_ID=your-client-id"
+ echo " VULCAN_OIDC_CLIENT_SECRET=your-client-secret"
+ fi
+ ;;
+
+ ldap)
+ # Set up LDAP environment
+ export VULCAN_ENABLE_LDAP=true
+
+ # Check if .env.ldap exists
+ if [ -f .env.ldap ]; then
+ echo "Loading LDAP configuration from .env.ldap"
+ # shellcheck disable=SC2046
+ export $(grep -v '^#' .env.ldap | xargs)
+ else
+ echo "Using default LDAP configuration for development"
+ export VULCAN_LDAP_HOST=localhost
+ export VULCAN_LDAP_PORT=10389
+ export VULCAN_LDAP_ATTRIBUTE=mail
+ export VULCAN_LDAP_BIND_DN="cn=admin,dc=planetexpress,dc=com"
+ export VULCAN_LDAP_ADMIN_PASS="GoodNewsEveryone"
+ export VULCAN_LDAP_BASE="ou=people,dc=planetexpress,dc=com"
+
+ echo "LDAP will be available with test users from docker-compose.dev.yml"
+ fi
+ ;;
+
+ local)
+ # Local authentication is the default
+ echo "Using local authentication (default)"
+
+ # Load general .env.development if it exists
+ if [ -f .env.development ]; then
+ # shellcheck disable=SC2046
+ export $(grep -v '^#' .env.development | xargs)
+ fi
+ ;;
+esac
+
+# Load any general .env file
+if [ -f .env ]; then
+ echo "Loading general environment from .env"
+ # shellcheck disable=SC2046
+ export $(grep -v '^#' .env | xargs)
+fi
+
+# Start PostgreSQL in Docker
+echo "Starting PostgreSQL database..."
+docker-compose -f docker-compose.dev.yml up -d
+
+# Export DATABASE_URL for Rails
+export DATABASE_URL="postgres://postgres:postgres@localhost:5432/vulcan_vue_development"
+
+# Wait for PostgreSQL to be ready
+echo "Waiting for PostgreSQL to be ready..."
+until docker-compose -f docker-compose.dev.yml exec -T db pg_isready -U postgres > /dev/null 2>&1; do
+ echo -n "."
+ sleep 1
+done
+echo " PostgreSQL is ready!"
+
+# Check database setup
+echo "Checking database..."
+
+if [ "$REFRESH" = true ]; then
+ echo "Refreshing database (--refresh flag)..."
+ DISABLE_SPRING=1 DATABASE_URL="postgres://postgres:postgres@localhost:5432/vulcan_vue_development" rails db:drop db:create db:schema:load db:seed
+else
+ DISABLE_SPRING=1 DATABASE_URL="postgres://postgres:postgres@localhost:5432/vulcan_vue_development" rails db:version 2>/dev/null || {
+ echo "Setting up database..."
+ DISABLE_SPRING=1 DATABASE_URL="postgres://postgres:postgres@localhost:5432/vulcan_vue_development" rails db:create db:schema:load db:seed
+ }
+fi
+
+echo ""
+echo "✅ Vulcan Development Environment Ready!"
+echo ""
+echo "Authentication mode: $AUTH_MODE"
+echo "Database is running at: postgres://localhost:5432/vulcan_vue_development"
+echo ""
+echo "To start the Rails app, run:"
+echo " foreman start -f Procfile.dev"
+echo ""
+echo "Or in separate terminals:"
+echo " Terminal 1: bundle exec rails server"
+
+# Check if Node.js 17+ is being used and provide appropriate webpack-dev-server command
+NODE_VERSION=$(node -v | cut -d. -f1 | sed 's/v//')
+if [ "$NODE_VERSION" -ge 17 ]; then
+ echo " Terminal 2: NODE_OPTIONS=--openssl-legacy-provider ./bin/webpack-dev-server"
+ echo ""
+ echo " Note: Node.js $NODE_VERSION requires the --openssl-legacy-provider flag for webpack 4"
+else
+ echo " Terminal 2: ./bin/webpack-dev-server"
+fi
+echo ""
+echo "Useful commands:"
+echo " Stop database: docker-compose -f docker-compose.dev.yml down"
+echo " View logs: docker-compose -f docker-compose.dev.yml logs -f"
+echo " Reset everything: $0 --clean --refresh"
+echo ""
+
+# Show auth-specific information
+case $AUTH_MODE in
+ okta)
+ echo "Okta authentication configured."
+ echo "Make sure your Okta app is configured with:"
+ echo " Redirect URI: http://localhost:3000/users/auth/openid_connect/callback"
+ ;;
+ ldap)
+ echo "LDAP authentication configured."
+ if [ ! -f .env.ldap ]; then
+ echo ""
+ echo "Test LDAP credentials (from docker-compose.dev.yml):"
+ echo " Username: fry@planetexpress.com"
+ echo " Password: fry"
+ echo " Username: zoidberg@planetexpress.com"
+ echo " Password: zoidberg"
+ fi
+ ;;
+ local)
+ echo "Local authentication configured."
+ echo ""
+ echo "Test credentials:"
+ echo " Email: admin@example.com"
+ echo " Password: 1234567ab!"
+ ;;
+esac
\ No newline at end of file
diff --git a/bin/rails b/bin/rails
index 5badb2fd..efc03774 100755
--- a/bin/rails
+++ b/bin/rails
@@ -1,9 +1,4 @@
#!/usr/bin/env ruby
-begin
- load File.expand_path('../spring', __FILE__)
-rescue LoadError => e
- raise unless e.message.include?('spring')
-end
-APP_PATH = File.expand_path('../config/application', __dir__)
-require_relative '../config/boot'
-require 'rails/commands'
+APP_PATH = File.expand_path("../config/application", __dir__)
+require_relative "../config/boot"
+require "rails/commands"
diff --git a/bin/rake b/bin/rake
index d87d5f57..4fbf10b9 100755
--- a/bin/rake
+++ b/bin/rake
@@ -1,9 +1,4 @@
#!/usr/bin/env ruby
-begin
- load File.expand_path('../spring', __FILE__)
-rescue LoadError => e
- raise unless e.message.include?('spring')
-end
-require_relative '../config/boot'
-require 'rake'
+require_relative "../config/boot"
+require "rake"
Rake.application.run
diff --git a/bin/setup b/bin/setup
index 3b392888..ec47b79b 100755
--- a/bin/setup
+++ b/bin/setup
@@ -1,36 +1,33 @@
#!/usr/bin/env ruby
-require 'fileutils'
+require "fileutils"
# path to your application root.
-APP_ROOT = File.expand_path('..', __dir__)
+APP_ROOT = File.expand_path("..", __dir__)
def system!(*args)
system(*args) || abort("\n== Command #{args} failed ==")
end
FileUtils.chdir APP_ROOT do
- # This script is a way to setup or update your development environment automatically.
- # This script is idempotent, so that you can run it at anytime and get an expectable outcome.
+ # This script is a way to set up or update your development environment automatically.
+ # This script is idempotent, so that you can run it at any time and get an expectable outcome.
# Add necessary setup steps to this file.
- puts '== Installing dependencies =='
- system! 'gem install bundler --conservative'
- system('bundle check') || system!('bundle install')
-
- # Install JavaScript dependencies
- system('bin/yarn')
+ puts "== Installing dependencies =="
+ system! "gem install bundler --conservative"
+ system("bundle check") || system!("bundle install")
# puts "\n== Copying sample files =="
- # unless File.exist?('config/database.yml')
- # FileUtils.cp 'config/database.yml.sample', 'config/database.yml'
+ # unless File.exist?("config/database.yml")
+ # FileUtils.cp "config/database.yml.sample", "config/database.yml"
# end
puts "\n== Preparing database =="
- system! 'bin/rails db:create db:schema:load'
+ system! "bin/rails db:prepare"
puts "\n== Removing old logs and tempfiles =="
- system! 'bin/rails log:clear tmp:clear'
+ system! "bin/rails log:clear tmp:clear"
puts "\n== Restarting application server =="
- system! 'bin/rails restart'
+ system! "bin/rails restart"
end
diff --git a/config/application.rb b/config/application.rb
index 25835994..f4017cc9 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -31,17 +31,19 @@
Dotenv.load('.env', ".env.#{Rails.env}", ".env.#{Rails.env}.local") if defined?(Dotenv)
module VulcanVue
- # This application was originally generated using Rails 6.0. Any subsequent updates
- # will require testing to verify that the defaults for that new version do not break
- # any functionality.
+ ##
+ # Main application configuration for Vulcan
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
- config.load_defaults 6.0
+ config.load_defaults 7.0
config.time_zone = 'UTC'
- # Settings in config/environments/* take precedence over those specified here.
- # Application configuration can go into files in config/initializers
- # -- all .rb files in that directory are automatically loaded after loading
- # the framework and any gems in your application.
+ # Configuration for the application, engines, and railties goes here.
+ #
+ # These settings can be overridden in specific environments using the files
+ # in config/environments, which are processed later.
+ #
+ # config.time_zone = "Central Time (US & Canada)"
+ # config.eager_load_paths << Rails.root.join("extras")
end
end
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 079778f0..60ebdbc1 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -1,10 +1,12 @@
# frozen_string_literal: true
+require 'active_support/core_ext/integer/time'
+
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
- # In the development environment your application's code is reloaded on
- # every request. This slows down response time but is perfect for development
+ # In the development environment your application's code is reloaded any time
+ # it changes. This slows down response time but is perfect for development
# since you don't have to restart the web server when you make code changes.
config.cache_classes = false
@@ -14,9 +16,12 @@
# Show full error reports.
config.consider_all_requests_local = true
+ # Enable server timing
+ config.server_timing = true
+
# Enable/disable caching. By default caching is disabled.
# Run rails dev:cache to toggle caching.
- if Rails.root.join('tmp', 'caching-dev.txt').exist?
+ if Rails.root.join('tmp', 'caching-dev.txt', 'caching-dev.txt').exist?
config.action_controller.perform_caching = true
config.action_controller.enable_fragment_cache_logging = true
@@ -41,6 +46,12 @@
# Print deprecation notices to the Rails logger.
config.active_support.deprecation = :log
+ # Raise exceptions for disallowed deprecations.
+ config.active_support.disallowed_deprecation = :raise
+
+ # Tell Active Support which deprecation messages to disallow.
+ config.active_support.disallowed_deprecation_warnings = []
+
# Raise an error on page load if there are pending migrations.
config.active_record.migration_error = :page_load
@@ -48,7 +59,13 @@
config.active_record.verbose_query_logs = true
# Raises error for missing translations.
- # config.action_view.raise_on_missing_translations = true
+ # config.i18n.raise_on_missing_translations = true
+
+ # Annotate rendered view with file names.
+ # config.action_view.annotate_rendered_view_with_filenames = true
+
+ # Uncomment if you wish to allow Action Cable access from any origin.
+ # config.action_cable.disable_request_forgery_protection = true
# Use the letter opener gem for email delivery in development
config.action_mailer.delivery_method = :letter_opener
diff --git a/config/environments/production.rb b/config/environments/production.rb
index aff76889..af90f4d9 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require 'active_support/core_ext/integer/time'
+
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
@@ -25,26 +27,26 @@
config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
# Enable serving of images, stylesheets, and JavaScripts from an asset server.
- # config.action_controller.asset_host = 'http://assets.example.com'
+ # config.asset_host = "http://assets.example.com"
# Specifies the header that your server uses for sending files.
- # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
- # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
+ # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache
+ # config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX
# Store uploaded files on the local file system (see config/storage.yml for options).
config.active_storage.service = :local
# Mount Action Cable outside main process or domain.
# config.action_cable.mount_path = nil
- # config.action_cable.url = 'wss://example.com/cable'
- # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
+ # config.action_cable.url = "wss://example.com/cable"
+ # config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ ]
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
config.force_ssl = ENV['FORCE_SSL'].is_a? String
- # Use the lowest log level to ensure availability of diagnostic information
- # when problems arise.
- config.log_level = :debug
+ # Include generic and useful information about system operation, but avoid logging too much
+ # information to avoid inadvertent exposure of personally identifiable information (PII).
+ config.log_level = :info
# Prepend all log lines with the following tags.
config.log_tags = [:request_id]
@@ -69,15 +71,15 @@
# the I18n.default_locale when a translation cannot be found).
config.i18n.fallbacks = true
- # Send deprecation notices to registered listeners.
- config.active_support.deprecation = :notify
+ # Don't log any deprecations.
+ config.active_support.report_deprecations = false
# Use default logging formatter so that PID and timestamp are not suppressed.
- config.log_formatter = ::Logger::Formatter.new
+ config.log_formatter = Logger::Formatter.new
# Use a different logger for distributed setups.
- # require 'syslog/logger'
- # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
+ # require "syslog/logger"
+ # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new "app-name")
if ENV['RAILS_LOG_TO_STDOUT'].present?
logger = ActiveSupport::Logger.new($stdout)
@@ -87,25 +89,4 @@
# Do not dump schema after migrations.
config.active_record.dump_schema_after_migration = false
-
- # Inserts middleware to perform automatic connection switching.
- # The `database_selector` hash is used to pass options to the DatabaseSelector
- # middleware. The `delay` is used to determine how long to wait after a write
- # to send a subsequent read to the primary.
- #
- # The `database_resolver` class is used by the middleware to determine which
- # database is appropriate to use based on the time delay.
- #
- # The `database_resolver_context` class is used by the middleware to set
- # timestamps for the last write to the primary. The resolver uses the context
- # class timestamps to determine how long to wait before reading from the
- # replica.
- #
- # By default Rails will store a last write timestamp in the session. The
- # DatabaseSelector middleware is designed as such you can define your own
- # strategy for connection switching and pass that into the middleware through
- # these configuration options.
- # config.active_record.database_selector = { delay: 2.seconds }
- # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
- # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
end
diff --git a/config/environments/test.rb b/config/environments/test.rb
index 3b35cdef..e5df61fd 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require 'active_support/core_ext/integer/time'
+
# The test environment is used exclusively to run your application's
# test suite. You never need to work with it otherwise. Remember that
# your test database is "scratch space" for the test suite and is wiped
@@ -8,12 +10,13 @@
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
- config.cache_classes = false
+ # Turn false under Spring and add config.action_view.cache_template_loading = true.
+ config.cache_classes = true
- # Do not eager load code on boot. This avoids loading your whole application
- # just for the purpose of running a single test. If you are using a tool that
- # preloads Rails for running tests, you may have to set it to true.
- config.eager_load = false
+ # Eager loading loads your whole application. When running a single test locally,
+ # this probably isn't necessary. It's a good idea to do in a continuous integration
+ # system, or in some way before deploying your code.
+ config.eager_load = ENV['CI'].present?
# Configure public file server for tests with Cache-Control for performance.
config.public_file_server.enabled = true
@@ -47,6 +50,15 @@
# Print deprecation notices to the stderr.
config.active_support.deprecation = :stderr
+ # Raise exceptions for disallowed deprecations.
+ config.active_support.disallowed_deprecation = :raise
+
+ # Tell Active Support which deprecation messages to disallow.
+ config.active_support.disallowed_deprecation_warnings = []
+
# Raises error for missing translations.
- # config.action_view.raise_on_missing_translations = true
+ # config.i18n.raise_on_missing_translations = true
+
+ # Annotate rendered view with file names.
+ # config.action_view.annotate_rendered_view_with_filenames = true
end
diff --git a/config/initializers/application_controller_renderer.rb b/config/initializers/application_controller_renderer.rb
index f4556db3..6d56e439 100644
--- a/config/initializers/application_controller_renderer.rb
+++ b/config/initializers/application_controller_renderer.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
# Be sure to restart your server when you modify this file.
# ActiveSupport::Reloader.to_prepare do
diff --git a/config/initializers/audited.rb b/config/initializers/audited.rb
index 1f180d82..2a2639e2 100644
--- a/config/initializers/audited.rb
+++ b/config/initializers/audited.rb
@@ -4,5 +4,23 @@
Rails.application.reloader.to_prepare do
Audited.config do |config|
config.audit_class = VulcanAudit
+ config.current_user_method = :current_user
+ end
+
+ # Force removal of presence validations using our custom method
+ # This handles cases where Rails re-adds validations after class loading
+ VulcanAudit.remove_presence_validations!
+end
+
+# Configure Warden to set audit user on authentication
+# This ensures audits are properly associated with the authenticated user
+if defined?(Warden)
+ Warden::Manager.after_set_user do |user, _auth, opts|
+ # Only set for non-fetch operations (actual login)
+ Audited.store[:current_user] = user if opts[:event] != :fetch && user
+ end
+
+ Warden::Manager.before_logout do |_user, _auth, _opts|
+ Audited.store[:current_user] = nil
end
end
diff --git a/config/initializers/backtrace_silencers.rb b/config/initializers/backtrace_silencers.rb
index d0f0d3b5..4b63f289 100644
--- a/config/initializers/backtrace_silencers.rb
+++ b/config/initializers/backtrace_silencers.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
# Be sure to restart your server when you modify this file.
# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb
index 598474b1..5346075a 100644
--- a/config/initializers/content_security_policy.rb
+++ b/config/initializers/content_security_policy.rb
@@ -2,21 +2,26 @@
# Be sure to restart your server when you modify this file.
-# Define an application-wide content security policy
-# For further information see the following documentation
-# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
+# Define an application-wide content security policy.
+# See the Securing Rails Applications Guide for more information:
+# https://guides.rubyonrails.org/security.html#content-security-policy-header
-Rails.application.config.content_security_policy do |policy|
- policy.script_src :self, :unsafe_eval
-end
-
-# If you are using UJS then enable automatic nonce generation
-# Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
+Rails.application.configure do
+ config.content_security_policy do |policy|
+ # policy.default_src :self, :https
+ # policy.font_src :self, :https, :data
+ # policy.img_src :self, :https, :data
+ # policy.object_src :none
+ policy.script_src :self, :unsafe_eval
+ # policy.style_src :self, :https
+ # Specify URI for violation reports
+ # policy.report_uri "/csp-violation-report-endpoint"
+ end
-# Set the nonce only to specific directives
-# Rails.application.config.content_security_policy_nonce_directives = %w(script-src)
+ # Generate session nonces for permitted importmap and inline scripts
+ # config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
+ # config.content_security_policy_nonce_directives = %w(script-src)
-# Report CSP violations to a specified URI
-# For further information see the following documentation:
-# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
-# Rails.application.config.content_security_policy_report_only = true
+ # Report violations without enforcing the policy.
+ # config.content_security_policy_report_only = true
+end
diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb
index 7a4f47b4..3df77c5b 100644
--- a/config/initializers/filter_parameter_logging.rb
+++ b/config/initializers/filter_parameter_logging.rb
@@ -2,5 +2,9 @@
# Be sure to restart your server when you modify this file.
-# Configure sensitive parameters which will be filtered from the log file.
-Rails.application.config.filter_parameters += [:password]
+# Configure parameters to be filtered from the log file. Use this to limit dissemination of
+# sensitive information. See the ActiveSupport::ParameterFilter documentation for supported
+# notations and behaviors.
+Rails.application.config.filter_parameters += %i[
+ passw secret token _key crypt salt certificate otp ssn
+]
diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb
index aa7435fb..9e049dcc 100644
--- a/config/initializers/inflections.rb
+++ b/config/initializers/inflections.rb
@@ -1,17 +1,18 @@
# frozen_string_literal: true
+
# Be sure to restart your server when you modify this file.
# Add new inflection rules using the following format. Inflections
# are locale specific, and you may define rules for as many different
# locales as you wish. All of these examples are active by default:
# ActiveSupport::Inflector.inflections(:en) do |inflect|
-# inflect.plural /^(ox)$/i, '\1en'
-# inflect.singular /^(ox)en/i, '\1'
-# inflect.irregular 'person', 'people'
+# inflect.plural /^(ox)$/i, "\\1en"
+# inflect.singular /^(ox)en/i, "\\1"
+# inflect.irregular "person", "people"
# inflect.uncountable %w( fish sheep )
# end
# These inflection rules are supported but not enabled by default:
# ActiveSupport::Inflector.inflections(:en) do |inflect|
-# inflect.acronym 'RESTful'
+# inflect.acronym "RESTful"
# end
diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb
index 6e1d16f0..be6fedc5 100644
--- a/config/initializers/mime_types.rb
+++ b/config/initializers/mime_types.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
# Be sure to restart your server when you modify this file.
# Add new mime types for use in respond_to blocks:
diff --git a/config/initializers/new_framework_defaults_7_0.rb b/config/initializers/new_framework_defaults_7_0.rb
new file mode 100644
index 00000000..8598c655
--- /dev/null
+++ b/config/initializers/new_framework_defaults_7_0.rb
@@ -0,0 +1,146 @@
+# frozen_string_literal: true
+
+# rubocop:disable Layout/LineLength
+
+# Be sure to restart your server when you modify this file.
+#
+# This file eases your Rails 7.0 framework defaults upgrade.
+#
+# Uncomment each configuration one by one to switch to the new default.
+# Once your application is ready to run with all new defaults, you can remove
+# this file and set the `config.load_defaults` to `7.0`.
+#
+# Read the Guide for Upgrading Ruby on Rails for more info on each option.
+# https://guides.rubyonrails.org/upgrading_ruby_on_rails.html
+
+# `button_to` view helper will render `