diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e9a3961..8efb9c0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up Ruby uses: ruby/setup-ruby@v1 @@ -38,7 +38,7 @@ jobs: run: sudo apt-get update && sudo apt-get install --no-install-recommends -y build-essential git libpq-dev libyaml-dev pkg-config google-chrome-stable - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up Ruby uses: ruby/setup-ruby@v1 @@ -52,7 +52,7 @@ jobs: run: bundle exec rake app:db:test:prepare test test:system - name: Keep screenshots from failed system tests - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 if: failure() with: name: screenshots diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c1df9d..5376fac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.1.7] - 2026-07-10 + +### Security +- Authentication is now required in every environment except `development` + and `test`, instead of only `production`. The engine force-enables + web-console and allows all IPs everywhere, so a `staging`/`preview` + deployment previously served an unauthenticated console. +- Require web-console >= 4.2.1: versions before 4.1.0 do not put CSP + nonces on injected assets (silently breaking under a strict CSP), and + versions before 4.2.1 lack Rack 3 / Rails 7.1 support. +- Enabled `rubygems_mfa_required`, so future gem pushes and yanks require + a multi-factor-authenticated RubyGems session. + +### Fixed +- A malformed Basic Authorization header (credentials without a colon) + now gets a 401 on Rack 2 instead of raising. +- Stored console sessions are capped at 50 per process. web-console's + session store is never evicted otherwise, so each console page load + pinned a binding in memory until process restart. + +### Removed +- Deleted the engine's unused application layout, which referenced a + stylesheet that does not ship with the gem, and a no-op CSRF skip in + the console controller (the console page is GET-only). + ## [0.1.6] - 2026-07-10 ### Security @@ -78,6 +103,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Development mode with no authentication required - Standard Ruby style guide compliance -[Unreleased]: https://github.com/firstdraft/slash_console/compare/v0.1.6...HEAD +[Unreleased]: https://github.com/firstdraft/slash_console/compare/v0.1.7...HEAD +[0.1.7]: https://github.com/firstdraft/slash_console/compare/v0.1.6...v0.1.7 [0.1.6]: https://github.com/firstdraft/slash_console/compare/v0.1.0...v0.1.6 [0.1.0]: https://github.com/firstdraft/slash_console/releases/tag/v0.1.0 diff --git a/Gemfile.lock b/Gemfile.lock index e26a22c..646b377 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,9 +1,9 @@ PATH remote: . specs: - slash_console (0.1.6) + slash_console (0.1.7) rails (>= 7.0) - web-console (>= 4.0) + web-console (>= 4.2.1) GEM remote: https://rubygems.org/ diff --git a/README.md b/README.md index 58f169a..b0c584c 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A Rails engine that provides a web-based console interface at `/rails/console`, - Only use this gem in applications where the security trade-offs are acceptable. Basically, only for toy apps/proofs-of-concept/portfolio projects that contain only sample data. Never use this gem when real user data is at risk. - For serious apps, SSH into the server and run `rails console` at the command-line. This may require upgrading your hosting from free to paid, but you should be doing that anyway if you have real users. -- Make up a strong, unique `ADMIN_PASSWORD` for each app. +- Make up a strong, unique `ADMIN_PASSWORD` for each app. There is no rate limiting on the password prompt, so a short or guessable password can be brute-forced. ## Installation @@ -30,9 +30,9 @@ That's it! Navigate to `/rails/console` in your browser. In development, no authentication is required. Simply visit `/rails/console`. -### Production +### Deployed environments -For production use, authentication is required. Set these environment variables: +In every environment other than `development` and `test` — production, staging, previews, anything — authentication is required. Set these environment variables: ```bash ADMIN_USERNAME="choose_your_own_username" @@ -41,15 +41,19 @@ ADMIN_PASSWORD="choose_your_own_strong_password" Without these environment variables, you'll see an error message explaining what needs to be configured. +If your server runs multiple processes (e.g. Puma workers), console sessions live in the memory of whichever process rendered the page, so evaluating code may intermittently report that your session is no longer available. Run a single process (e.g. `WEB_CONCURRENCY=1`) for a reliable console. + ## How It Works SlashConsole is a lightweight wrapper around [the excellent `web-console` gem](https://github.com/rails/web-console). It: 1. Provides a dedicated route for console access (instead of only on error pages). 2. Renders a full-page console interface, including in apps that enforce a strict nonce-based Content Security Policy. -3. In production, requires basic authentication via a Rack middleware that protects both the console page and web-console's code-evaluation endpoints (`/__web_console/repl_sessions/:id`). +3. In every environment except development and test, requires basic authentication via a Rack middleware that protects both the console page and web-console's code-evaluation endpoints (`/__web_console/repl_sessions/:id`). 4. Evaluates console input at the top level, so constants resolve the same way as in `bin/rails console`. +Note that SlashConsole configures web-console itself: it activates it in all environments and clears its IP allowlist (authentication replaces it). Any `config.web_console` settings in your app will be overridden. + ## Development After checking out the repo, run `bundle install` to install dependencies. diff --git a/app/controllers/slash_console/console_controller.rb b/app/controllers/slash_console/console_controller.rb index d2f1ea4..604342c 100644 --- a/app/controllers/slash_console/console_controller.rb +++ b/app/controllers/slash_console/console_controller.rb @@ -2,17 +2,23 @@ module SlashConsole class ConsoleController < ApplicationController layout false - skip_before_action :verify_authenticity_token, if: -> { defined?(verify_authenticity_token) } + # web-console stores every console session (and the binding it holds) + # in an in-memory hash that is never evicted, so each page load would + # otherwise pin memory until the process restarts. Old sessions are + # dropped once this many accumulate; an evicted session's tab shows + # web-console's normal "session is no longer available" message. + MAX_STORED_SESSIONS = 50 # BasicAuthMiddleware is the primary guard: it protects both this page # and web-console's evaluator endpoints, but only at the engine's # standard /rails mount point. These filters remain as a second layer # so the page stays protected if an application mounts the engine at a # custom path. - before_action :ensure_credentials_configured, if: -> { Rails.env.production? } - before_action :authenticate_user, if: -> { Rails.env.production? } + before_action :ensure_credentials_configured, if: -> { BasicAuthMiddleware.authentication_required? } + before_action :authenticate_user, if: -> { BasicAuthMiddleware.authentication_required? } def index + prune_console_sessions console(SlashConsole.console_binding) render :index end @@ -30,5 +36,10 @@ def authenticate_user BasicAuthMiddleware.authorized?(username, password) end end + + def prune_console_sessions + storage = WebConsole::Session.inmemory_storage + storage.shift while storage.size >= MAX_STORED_SESSIONS + end end end diff --git a/app/views/layouts/slash_console/application.html.erb b/app/views/layouts/slash_console/application.html.erb deleted file mode 100644 index 72beba1..0000000 --- a/app/views/layouts/slash_console/application.html.erb +++ /dev/null @@ -1,17 +0,0 @@ - - -
-