Upgrade Rails to 8.1.3.1 and refresh dependency security posture - #745
Merged
Conversation
Rails 8.0 support ends 2026-10-07; Brakeman's 60-day EOL window opened 2026-08-08 and failed CI on every branch. Move to the current stable line and take the dependency posture with it: - rails 8.0.5.1 -> 8.1.3.1 (all framework gems in lockstep; action_text-trix extracted, benchmark dependency dropped) - rack 2.2.23 -> 3.2.6 (+ rack-session 2, rackup 2, rack-protection 4) converging master with the feature branch's rack line - omniauth-rails_csrf_protection 1.0.2 -> 2.0.1: root-cause fix for the ActiveSupport::Configurable deprecation (removed in Rails 8.2) - brakeman 8, rspec-rails 8, shoulda-matchers 8, simplecov 1.0, byebug 13, bindata 3, rubocop-capybara 3, rdoc 8 - ~110 further within-major updates via bundle update --minor --strict bundler-audit clean after all movements. Riskier majors (OIDC stack, omniauth-ldap, rubyzip, haml, puma, slack/faraday, the InSpec/chef line) deliberately held for dedicated passes with their own live verification. Authored by: Aaron Lippold<lippold@gmail.com>
- config.load_defaults 8.1: all six new defaults active (JSON escaping relaxed, finder-order raise with PK fallback, path-relative redirect raise, Ruby render tracker, hidden-field autocomplete removal) - Delete the stale 7.0 framework-defaults initializer: every line was commented out and those defaults have been active since load_defaults reached 8.0 - Development gains verbose redirect logging (new in 8.1) - Refresh the static error pages and icon from the 8.1 template: they gain dark-mode support and dvh sizing app:update was reviewed hunk-by-hunk; the stock-template overwrites that would have removed real configuration (force_ssl env contract, the active Content-Security-Policy, the audited YAML allowlist, bin/dev port detection, bin/setup flow, test.rb cache settings) were rejected. Authored by: Aaron Lippold<lippold@gmail.com>
Active Record 8.1 sorts table columns alphabetically in schema dumps so they stop flip-flopping with migration order. One-time re-dump, proven a pure reorder: 23 tables and 75 indexes identical on both sides, with only the schema-version header changing. Authored by: Aaron Lippold<lippold@gmail.com>
The rubocop-rails update ships cops for the Rails 8 parameter API and the Rack 3 status names. The application-code half, each site hand-reviewed: - Model.find(params[:id]) -> Model.find(params.expect(:id)) at 15 sites: array/hash query-string tampering now yields a clean 400 instead of surprising find([]) behavior; well-formed requests are unchanged - redirect_back fallback_location: -> redirect_back_or_to - Rack 3 status names (:unprocessable_content) in application code, matching what the feature branch already uses - Project comment-count builders move from each_with_object to to_h - Two now-redundant cop disables removed Authored by: Aaron Lippold<lippold@gmail.com>
Mechanical sweep from the rubocop family updates, dominated by the new HttpStatusNameConsistency cops moving specs to the Rack 3 status names the feature branch already uses. Also: benchmark output leaves the performance spec (the measured time reports through the failure message, keeping the suite silent on success), the pending-migration notice goes to stderr, an intentional literal interpolation grep is escaped instead of single-quoted, and a kind-check select becomes grep(Symbol). Authored by: Aaron Lippold<lippold@gmail.com>
Two task spec files each called Rails.application.load_tasks in before(:all). Rake accumulates actions on re-load, so when both files landed in the same parallel worker every task body ran twice per invoke — the admin bootstrap specs failed with logger expectations receiving two calls instead of one, an order-dependent failure that never fired when the files ran in separate workers. Port the load-once helper from the feature branch byte-identical and convert both callers. Both files together in a single process: green. Authored by: Aaron Lippold<lippold@gmail.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Upgrades the application to Rails 8.1.3.1 (and aligned dependency set like rack 3.2.x) while modernizing request parameter handling (params.expect) and normalizing 422 handling (:unprocessable_content), plus refreshing several test/support utilities and stock error pages.
Changes:
- Upgrade Rails to 8.1 defaults (
config.load_defaults 8.1) and update Gem dependencies (notably rack 3.2.6, rspec-rails 8, shoulda-matchers 8, brakeman 8). - Adopt
params.expectin multiple controllers and switch many JSON/API 422 responses from:unprocessable_entityto:unprocessable_content(with matching spec updates). - Improve test stability by loading Rake tasks once per spec process and refresh public error pages (dark-mode + CSS tweaks).
Reviewed changes
Copilot reviewed 55 out of 58 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| spec/support/helpers/rake_task_helpers.rb | Adds helper to load Rails Rake tasks once for task specs. |
| spec/services/export/modes/backup_spec.rb | Minor Ruby cleanup using grep(Symbol) for association symbol filtering. |
| spec/requests/users/unlink_identity_spec.rb | Updates expected 422 status symbol to :unprocessable_content. |
| spec/requests/users/omniauth_callbacks_spec.rb | Adjusts string matching for OAuth error flash interpolation check. |
| spec/requests/users_spec.rb | Updates multiple 422 expectations to :unprocessable_content. |
| spec/requests/user_lock_spec.rb | Updates expected 422 status symbol to :unprocessable_content. |
| spec/requests/upload_validation_spec.rb | Updates expected 422 status symbol to :unprocessable_content across upload validations. |
| spec/requests/stigs_spec.rb | Updates expected 422 status symbol to :unprocessable_content. |
| spec/requests/rules_spec.rb | Updates expected 422 status symbol to :unprocessable_content. |
| spec/requests/rule_section_locks_spec.rb | Updates expected 422 status symbol to :unprocessable_content. |
| spec/requests/rule_satisfactions_spec.rb | Updates expected 422 status symbol to :unprocessable_content. |
| spec/requests/reviews_spec.rb | Updates many expected 422 status symbols to :unprocessable_content. |
| spec/requests/reviews_lock_sections_spec.rb | Updates expected 422 status symbol to :unprocessable_content. |
| spec/requests/reviews_lock_controls_spec.rb | Updates expected 422 status symbol to :unprocessable_content. |
| spec/requests/registrations_spec.rb | Updates expectation to allow :unprocessable_content for wrong-password update. |
| spec/requests/reactions_spec.rb | Updates expected 422 status symbol to :unprocessable_content. |
| spec/requests/projects_import_backup_spec.rb | Updates expected 422 status symbol to :unprocessable_content. |
| spec/requests/projects_create_from_backup_spec.rb | Updates expected 422 status symbol to :unprocessable_content. |
| spec/requests/memberships_spec.rb | Updates expected 422 status symbol to :unprocessable_content. |
| spec/requests/format_handling_spec.rb | Updates expected 422 status symbol to :unprocessable_content. |
| spec/requests/components_update_spreadsheet_spec.rb | Updates expected 422 status symbol to :unprocessable_content. |
| spec/requests/components_spec.rb | Updates expected 422 status symbol to :unprocessable_content. |
| spec/requests/components_detect_srg_spec.rb | Updates expected 422 status symbol to :unprocessable_content. |
| spec/rails_helper.rb | Uses warn for pending migration messaging (stderr) before exiting. |
| spec/performance/stig_viewer_performance_spec.rb | Removes noisy puts; keeps perf threshold asserted via failure message. |
| spec/models/concerns/severity_counts_spec.rb | Formatting-only indentation adjustment in expected columns computation. |
| spec/migrations/strip_satisfaction_text_spec.rb | Converts heredocs to squiggly heredocs (<<~) in migration specs. |
| spec/lib/tasks/stig_and_srg_puller_spec.rb | Switches to load_rake_tasks helper. |
| spec/lib/tasks/admin_bootstrap_spec.rb | Switches to load_rake_tasks helper. |
| public/500.html | Updates stock error page (dark mode, dvh, typography, straight apostrophes). |
| public/422.html | Updates stock error page (dark mode, dvh). |
| public/406-unsupported-browser.html | Updates stock error page (dark mode, dvh). |
| public/404.html | Updates stock error page (dark mode, dvh, straight apostrophes). |
| public/400.html | Updates stock error page (dark mode, dvh). |
| Gemfile.lock | Bumps Rails and broad dependency set, including rack 3.x and test/security tooling. |
| Gemfile | Updates Rails constraint ( |
| db/schema.rb | Updates schema header to Rails 8.1 and reorders schema output under new dumper. |
| config/initializers/new_framework_defaults_7_0.rb | Removes obsolete Rails 7.0 defaults initializer. |
| config/environments/development.rb | Enables verbose redirect logs in development. |
| config/application.rb | Updates config.load_defaults from 8.0 to 8.1. |
| app/models/security_requirements_guide.rb | Switches SQL heredoc to squiggly form (<<~). |
| app/models/project.rb | Uses rows.to_h transformations instead of manual hash building. |
| app/controllers/users/registrations_controller.rb | Uses redirect_back_or_to and returns :unprocessable_content for JSON errors. |
| app/controllers/users_controller.rb | Switches multiple JSON error responses to :unprocessable_content; adopts params.expect for set_user. |
| app/controllers/stigs_controller.rb | Adopts params.expect(:id) for show; uses :unprocessable_content in destroy JSON error response. |
| app/controllers/security_requirements_guides_controller.rb | Adopts params.expect(:id) for show/private finder; uses :unprocessable_content in destroy JSON error response. |
| app/controllers/rules_controller.rb | Switches multiple JSON error responses to :unprocessable_content; adopts params.expect for set_rule. |
| app/controllers/rule_satisfactions_controller.rb | Switches failure responses to :unprocessable_content; adopts params.expect for IDs. |
| app/controllers/reviews_controller.rb | Switches invalid section handling to :unprocessable_content; adopts params.expect for IDs. |
| app/controllers/projects_controller.rb | Switches many JSON error responses to :unprocessable_content; adopts params.expect(:id) for set_project. |
| app/controllers/project_access_requests_controller.rb | Uses redirect_back_or_to, switches JSON failure to :unprocessable_content, adopts params.expect for IDs. |
| app/controllers/memberships_controller.rb | Uses redirect_back_or_to, switches JSON failure to :unprocessable_content, adopts params.expect(:id). |
| app/controllers/concerns/upload_validatable.rb | Switches upload validation failures to :unprocessable_content. |
| app/controllers/concerns/oidc_discovery_helper.rb | Removes unnecessary RuboCop predicate-method disable/enable comments. |
| app/controllers/components_controller.rb | Switches multiple JSON failures to :unprocessable_content; uses redirect_back_or_to in several places. |
| app/controllers/application_controller.rb | Uses redirect_back_or_to for error handlers. |
| app/controllers/api/user_search_controller.rb | Uses params.expect(:membership_id) when resolving membership target. |
Suppressed comments (1)
app/controllers/components_controller.rb:370
- Inside the
.or(...)clause, also useparams.expect(:id)so the exclusion is validated the same way as the outer scope.
.or(Component.where(based_on: SecurityRequirementsGuide.where(title: srg_title))
.where.not(id: params[:id])
.where(released: true))
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Puma 8 changelog and the 8.0-Upgrade guide reviewed in full first: none of the renamed lifecycle hooks, before_thread_start arity, SIGPWR, supported_http_methods, http_content_length_limit, fork_worker, or prune_bundler surfaces are in use — config/puma.rb needs no changes. The one behavioral change that touches us is the IPv6-preferring default bind. Accepted as shipped: dual-stack hosts serve both families, and on IPv4-only networks (the Docker bridge) puma falls back to 0.0.0.0 with a boot warning — observed live in the production container smoke. 8.0.2 also carries PROXY-protocol parsing security fixes. Verified: full suite green, Docker image built and serving, real browser login round-trip through the new server. Authored by: Aaron Lippold<lippold@gmail.com>
The Excel reader blocked the zip library: ruh-roo (dormant since 2021) caps rubyzip below 3, while the revived roo 3.0 requires and was co-designed with rubyzip 3. Swap ruh-roo for roo ~> 3.0 — the Roo::Spreadsheet surface we use is unchanged — and take rubyzip 3.4.1 with its path-traversal and central-directory hardening. Re-proving the zip-bomb defense found it half-imaginary: rubyzip validates entry sizes only in Entry#extract, so streaming reads — every read the import paths perform — inflated without limit on 2.4 and 3.x alike. An archive whose central directory under-declares sizes passed the aggregate budget and expanded unbounded in memory. - ZipEntryReader.read_capped: chunked streaming read hard-capped at the entry's declared size, raising Zip::EntrySizeError on the lie; applied at every zip-read site — the archive importer, the restore modal's pre-budget defaults probe, and the STIG puller - The importer reports a clean error for a manifest without a components list instead of crashing - Fixture tests binary-patch real archives' central directories to prove the lying-CD rejection at both the importer and the real upload endpoint, alongside the existing honest-big budget tests Verified: full suite green, live UI export-and-restore round trip through the real modals with the restored project's contents DB-confirmed. The re-lock also normalized bundler platform entries (mechanical Bundler 2.7.2 behavior; all deploy platforms retained). Authored by: Aaron Lippold<lippold@gmail.com>
haml 7's breaking surface was reviewed against every template before the bump: the attr_quote default flips from single to double quotes (byte-level only — entity decoding keeps semantics identical), the Ruby floor rises to 3.2, and the 7.3 Prism parser interpolates literal instance/global variable references in strings. Zero of the 42 templates use any sensitive pattern and none needed changes — an independent compile sweep of all 42 confirms. haml-rails 3.1 is the Rails 8.1 support release and fixes a real Rails 8.1 regression: the tightened ERB dependency tracker stopped Haml partial changes from busting fragment caches; 3.1 switches Haml templates to the :ruby render tracker. The quote flip's one casualty was a consent spec helper whose regex demanded single-quoted attributes — now quote-agnostic with a backreference, and still failing honestly when the attribute is absent. Verified: full suite green including system specs, visual pass of login, project, component view, and component edit pages in a real browser. Authored by: Aaron Lippold<lippold@gmail.com>
slack-ruby-client 1.0.0 was the bundle's last faraday-1 holder (via faraday_middleware, which 3.2 replaces with faraday-mashify and faraday-multipart). The UPGRADING guide was reviewed end to end: our surface is Web API only, so the RTM removals don't touch us; the 3.2 error-type refinement keeps our ChannelNotFound-then-SlackError rescue order correct; the 2.0 faraday SSL-default removal was the one live risk and is proven by a real delivery over TLS to slack.com. - faraday 1.10 -> 2.14 (direct pin loosened to ~> 2.0); the entire faraday-1 adapter constellation drops from the lock - slack_block_kit stays: latest release, dependency-free, independent of the client - climate_control joins the test group for parallel-safe ENV mutation in specs Verified live end to end against a real workspace: direct chat.postMessage delivery (ok=true, name resolved to channel id) and a UI-triggered membership notification rendering its full Block Kit payload in-channel. Authored by: Aaron Lippold<lippold@gmail.com>
The settings template interpolated env vars into YAML unquoted, so any value whose first character is '#' — Slack channels, hex colors, passwords, client secrets — turned the rest of the line into a YAML comment and the setting silently became nil. The banner colors only appeared to work because an initializer re-defaults blank values, which means a deployer's custom '#'-color silently reverted to the default; a Slack channel like #vulcan-test could never be configured at all, and SMTP/LDAP/OIDC credentials containing YAML-special characters vanished without a trace. Every free-form string interpolation now emits its value with to_json: JSON strings are valid YAML scalars, every special character is escaped, and unset variables still render as YAML null. Typed values (booleans, numerics, symbols) are untouched. New template-rendering specs load the file exactly as Settingslogic does and pin that hash-leading channels, colors, secrets, and punctuation-heavy passwords all survive — each proven failing against the unquoted template first. Authored by: Aaron Lippold<lippold@gmail.com>
omniauth_openid_connect 0.6.1 held the whole nov stack at its 1.x/ httpclient era. The 0.8 bump brings openid_connect 2.5, rack-oauth2 2.3, swd 2.0, webfinger 2.1, and json-jwt 1.17 — the entire line now rides the faraday 2 already in the bundle, and httpclient leaves the lock. json-jwt 1.16.1's padding-oracle removal comes with it. Every changelog was read end to end before the bump: the strategy's option surface is unchanged for our config, rack-oauth2 2.x keeps the jwt_bearer/private_key client auth the login.gov plan needs, and the 2.x error classes preserve their contracts. No application code needed to change; the full suite runs green untouched. Proven live against a real Okta org in the browser: discovery, code exchange, id_token session storage, and RP-initiated provider logout through the discovered end_session_endpoint. Authored by: Aaron Lippold<lippold@gmail.com>
The 3.0 line's only removals — the deprecated top-level ca_file and ssl_version options and their adaptor translation — are options this app never passes; the whole configured surface (host, port, uid, encryption, bind_dn, password, base) carries over unchanged, so no env var or settings-template change is needed. 3.0.1's hardened inspection redaction (bind passwords and TLS keys filtered from nested output) comes along as a security win, and the trusted-header SSO tightening landed already via 2.3.4. Proven live in the browser against the same test-openldap fixture image the system specs document: form sign-in, user provisioning from the directory's mail attribute, and sign-out. Authored by: Aaron Lippold<lippold@gmail.com>
Checking Remember me on an LDAP or OIDC login has thrown NoMethodError since v2.3.1: the callback controller calls remember_me(user) but Devise::Controllers::Rememberable is opt-in and was never included — the module exists precisely for controllers that sign users in from a callback. The catch-all rescue converted every occurrence into a generic error toast, and the covering spec only grepped the controller source for the word remember_me, so it never noticed. That spec is replaced with request specs that drive the real callback route across both delivery paths (form params at the callback; a query param stashed through the provider round-trip — OmniAuth only carries request.GET into omniauth.params, which the old comment got wrong) plus the negative case. An unreachable directory also surfaced to users as the raw humanized error type — 'Ldap error'. The failure message now translates known types through devise.omniauth_callbacks.reasons, telling the user the server was unreachable and their credentials were never checked; unknown types keep Devise's humanized fallback. Unused failure-mock helpers that overrode OmniAuth's global on_failure without restoring it are deleted, and the rescue handlers are no longer public methods. Authored by: Aaron Lippold<lippold@gmail.com>
Vulcan uses inspec-core purely as a library — one code-generation site building InSpec control source through mitre-inspec-objects, which needs inspec-core for a single class. The pin moves that dependency to the Cinc Project's community gem server, which builds current InSpec releases from source, bringing the 5.23 line up to 7.1.7. The resolution is narrow: only inspec-core comes from the cinc remote; chef-config and chef-utils step to the 19.x line from rubygems.org (kept alive by the chef-licensing and chef-telemetry dependencies), and the handful of new arrivals are small. Control generation is proven in-process and live through the rule editor: an edited test script round-trips through save and comes back embedded in the regenerated control. Authored by: Aaron Lippold<lippold@gmail.com>
Yarn audit stood at 168 findings (2 critical, 96 high). This sweep takes it to 4, all accepted with reasons: image-size has no patch in existence (dev-only, esbuild-vue's build chain processing our own assets), and vue/vue-template-compiler advisories are inherent to the Vue 2 line whose migration is already the project's plan. - axios 1.12.2 -> 1.19.0, clearing eleven highs; the supply-chain ceiling resolution stays, raised to the new version - dompurify -> 3.4.13, clearing fourteen sanitizer advisories - vitest/coverage -> 4.1.10 (critical) and vite -> 7.3.6, worked around yarn 1's nested-linking bug with a vite resolution; concurrently and its shell-quote (critical) refreshed - @rails/actioncable and @rails/activestorage -> 8.1.301, matching the app's Rails 8.1.3.1 - lodash, marked, sass, esbuild, esbuild-sass-plugin, easymde, shiki, @eslint/js refreshed within their ranges - scoped resolutions pin patched transitives under toolchains whose own pins lag: minimatch (3.x and 9.x lines separately), picomatch, js-yaml, ajv, flatted, ws, rollup, piscina, js-cookie Verified: yarn test:unit 2317/2317, eslint --max-warnings 0 clean, esbuild build clean. Authored by: Aaron Lippold<lippold@gmail.com>
The documentation site's own yarn tree stood at 68 audit findings. This sweep takes it to zero: vitepress 2.0.0-alpha.11 -> alpha.19 (carrying the patched vite line), mermaid -> 10.9.8 with its dompurify, uuid, and lodash-es transitives refreshed, svgo/vue/image-optimizer in-range refreshes, and one resolution pinning the patched mdast-util-to-hast under vitepress's shiki chain. Verified: vitepress build renders every page clean. Authored by: Aaron Lippold<lippold@gmail.com>
The same-SRG lookup already has the component loaded by its before_action; excluding by the record's own id drops the redundant params read and states the intent. Behavior identical — the routed path id and the loaded record are the same value by construction. Authored by: Aaron Lippold<lippold@gmail.com>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
Rails 8.0 support ends 2026-10-07, and Brakeman's 60-day EOL warning opened on 2026-08-08 — failing CI on every branch. This PR moves master to the current stable line and takes the full dependency security posture with it: Rails, every held gem major, and both yarn trees.
Rails 8.1
config.load_defaults 8.1(all six new defaults active; the stale 7.0 defaults initializer deleted)ActiveSupport::Configurabledeprecation (removed in Rails 8.2)bundler-auditcleanparams.expectadoption at 15 controller sites (query-string shape tampering → clean 400) plus the new-cop modernization sweepThe gem-major chain (each upgraded guides-first, live-proven, independently AC-reviewed)
Bugs found and fixed along the way (each with regression tests)
#-leading env value (Slack channels, hex colors, passwords, secrets) as a YAML comment — silent nil, masked by initializer defaultsNoMethodErrorsince v2.3.1 — the DeviseRememberablecontroller module was never included; the covering spec only grepped source text and was replaced with real callback-driving specsJS dependency posture (both yarn trees)
Changelog
Notable version moves
Full Ruby dependency delta — 118 changed, 11 added, 25 left the tree
Changed:
actioncable, actionmailbox, actionmailer, actionpack, actiontext, actionview, activejob, activemodel, activerecord, activestorage, activesupport, railties, rails 8.0.5.1→8.1.3.1 · activerecord-session_store 2.2.0→2.3.0 · anonymous_loader 0.1.1→0.1.3 · auth-sanitizer 0.2.2→0.2.3 · bindata 2.5.1→3.0.0 · blueprinter 1.2.1→1.3.0 · bootsnap 1.24.6→1.25.0 · brakeman 7.1.0→8.0.5 · byebug 12.0.0→13.0.0 · caxlsx 4.4.1→4.5.0 · cgi 0.5.1→0.5.2 · chef-config 18.8.11→19.3.15 · chef-utils 18.8.11→19.3.15 · commonmarker 2.7.0→2.9.0 · cookstyle 8.4.0→8.7.6 · crack 1.0.0→1.0.1 · csv 3.3.5→3.3.6 · database_cleaner-core 2.0.1→2.1.0 · devise-encryptable 0.2.0→0.3.0 · erb 6.0.4→6.0.7 · factory_bot 6.5.6→6.6.0 · faraday 1.10.6→2.14.3 · faraday-follow_redirects 0.3.0→0.5.0 · faraday-net_http 1.0.2→3.4.4 · ffaker 2.24.0→2.25.0 · ffi 1.17.2→1.17.4 · globalid 1.3.0→1.4.0 · haml 6.3.0→7.3.0 · haml-rails 2.1.0→3.1.0 · hashdiff 1.2.0→1.2.1 · hashie 5.0.0→5.1.0 · http-cookie 1.0.8→1.1.6 · i18n 1.14.8→1.15.2 · inspec-core 5.22.95→7.1.7 · io-console 0.8.2→0.9.0 · irb 1.17.0→1.18.0 · jbuilder 2.14.1→2.15.1 · json 2.21.1→2.21.2 · json-jwt 1.15.3.1→1.17.2 · language_server-protocol 3.17.0.5→3.17.0.6 · listen 3.9.0→3.10.0 · mail 2.9.0→2.9.1 · marcel 1.1.0→1.2.1 · mime-types-data 3.2025.0812→3.2026.0701 · minitest 6.0.3→6.0.6 · mixlib-log 3.0.9→3.2.3 · mixlib-shellout 3.3.9→3.4.10 · msgpack 1.8.3→1.8.4 · multi_xml 0.7.2→0.9.1 · net-imap 0.6.4.1→0.6.6 · net-ssh 7.3.0→7.3.3 · nokogiri-happymapper 0.10.0→0.10.1 · oauth2 2.0.24→2.0.25 · oj 3.17.4→3.17.5 · omniauth 2.1.3→2.1.4 · omniauth-ldap 2.3.3→3.0.2 · omniauth-rails_csrf_protection 1.0.2→2.0.1 · omniauth_openid_connect 0.6.1→0.8.0 · openid_connect 1.4.2→2.5.0 · ox 2.14.23→2.14.28 · parallel 1.27.0→1.28.0 · parallel_tests 5.6.0→5.7.0 · parser 3.3.9.0→3.3.12.0 · pp 0.6.3→0.6.4 · propshaft 1.2.1→1.3.2 · pry 0.15.2→0.16.0 · public_suffix 7.0.2→7.0.5 · puma 7.2.1→8.0.2 · rack 2.2.23→3.2.6 · rack-oauth2 1.21.3→2.3.0 · rack-protection 3.2.0→4.2.1 · rack-session 1.0.2→2.1.2 · rackup 1.0.1→2.3.1 · rake 13.3.1→13.4.2 · rdoc 7.2.0→8.0.0 · regexp_parser 2.11.2→2.12.0 · reline 0.6.3→0.7.0 · rspec 3.13.1→3.13.2 · rspec-core 3.13.5→3.13.6 · rspec-mocks 3.13.5→3.13.8 · rspec-rails 6.1.5→8.0.4 · rspec-support 3.13.4→3.13.7 · rubocop 1.79.2→1.86.1 · rubocop-ast 1.46.0→1.50.0 · rubocop-capybara 2.22.1→3.0.0 · rubocop-performance 1.25.0→1.26.1 · rubocop-rails 2.33.3→2.36.0 · rubocop-rspec 3.6.0→3.9.0 · rubyzip 2.4.1→3.4.1 · selenium-webdriver 4.35.0→4.46.0 · shoulda-matchers 7.0.1→8.0.1 · simplecov 0.22.0→1.0.3 · slack-ruby-client 1.0.0→3.2.0 · snaky_hash 2.0.6→2.0.7 · swd 1.3.0→2.0.3 · temple 0.10.4→0.10.6 · test-prof 1.5.2→1.6.3 · tilt 2.6.1→2.8.0 · tomlrb 1.3.0→2.0.4 · train-core 3.12.13→3.16.5 · tzinfo-data 1.2025.2→1.2026.3 · version_gem 1.1.12→1.1.15 · web-console 4.2.1→4.3.0 · webfinger 1.2.0→2.1.3 · webmock 3.25.1→3.26.2 · zeitwerk 2.7.5→2.8.3
Added: action_text-trix 2.1.19 · chef-licensing 1.4.1 · climate_control 1.2.0 · faraday-http-cache 2.7.0 · faraday-mashify 1.0.2 · net-http 0.9.1 · pstore 0.1.4 · rbs 4.1.2 · roo 3.0.0 · syslog 0.4.0 · tty-spinner 0.9.3
Left the tree — deliberate replacements: ruh-roo (→ roo) · httpclient (→ faraday 2 across the OIDC stack) · faraday_middleware + the faraday-1 adapter constellation (em_http, em_synchrony, excon, httpclient, net_http_persistent, patron, rack, retry — replaced by faraday 2's model) · validate_email (dropped by openid_connect 2.3)
Left the tree — dependency-graph shifts (nothing lost): psych, stringio, benchmark, webrick, mutex_m, ruby2_keywords (stdlib gems no longer declared by updated dependents) · mini_portile2, rake-compiler-dock, rb_sys (build tooling no longer pulled) · simplecov-html, simplecov_json_formatter, docile (absorbed by simplecov 1.0) · nokogiri's generic lock entry (platform-specific entries remain — nokogiri itself unchanged at 1.19.4)
JS dependency delta (app tree)
Direct: axios 1.12.2→1.19.0 (exact pin; supply-chain ceiling resolution raised to match) · dompurify 3.3.1→3.4.13 · lodash 4.17.23→4.18.1 · marked 17.0.3→17.0.6 · shiki 3.22.0→3.23.0 · easymde 2.20.0→2.21.0 · sass 1.97.3→1.102.0 · esbuild 0.25.9→0.25.12 · esbuild-sass-plugin 3.3.1→3.7.0 · vite 7.3.1→7.3.6 · vitest + @vitest/coverage-v8 4.0.18→4.1.10 · concurrently 9.2.1→9.2.4 · @eslint/js 9.33.0→9.39.5 · @rails/actioncable + @rails/activestorage 8.0.201→8.1.301
New scoped resolutions (patched transitives under toolchains whose own pins lag): minimatch (3.x and 9.x lines separately) · picomatch · js-yaml · ajv · flatted · ws · rollup · piscina · js-cookie · vite (single-copy hoist for yarn 1)
Docs tree: vitepress 2.0.0-alpha.11→alpha.19 · mermaid 10.9.3→10.9.8 (+ its dompurify/uuid/lodash-es transitives) · svgo 4.0.0→4.0.2 · vue 3.5.18→3.5.41 · vite-plugin-image-optimizer 2.0.2→2.0.3 · one resolution (mdast-util-to-hast)
Verification
Merge note
The commits are structured as a reviewable story (Rails upgrade → gem majors one per commit → found fixes → JS posture). Recommend a merge commit rather than squash to preserve it.