Repository navigation
Add session attribution and command audit log (inspired by console1984) #5
Description
Context
We currently provide a remote Rails console at /rails/console with a single shared HTTP Basic credential in production and no auth in development. The README is upfront that this is "toy apps only." After comparing the gem to console1984 (Basecamp's IRB-based audit/protection layer), there are two changes that would meaningfully reduce the recklessness of using slash_console without abandoning its "drop-in convenience" positioning.
Why not just depend on console1984?
We considered it. It's a bad fit:
console1984installs its protections inside Rails'console do ... endblock, which fires whenbin/rails consoleboots IRB.slash_consoledoesn't use IRB —web-consoleevaluates input byeval-ing strings inside a storedBinding. The IRB::Context prepend, thedecrypt!/encrypt!commands, and the TracePoint-based forbidden-method shield all have nothing to attach to in a/rails/consolerequest.- It carries real weight: hard Rails 7 + Active Record Encryption requirement, four migration tables, eager loading on session start, a
parserdep, monkeypatches intoTCPSocket/SSLSocket/all three AR adapters, andRefrigerator#freeze_allwhich freezes classes process-wide. That conflicts with our "toy apps only" framing — and if you're willing to take all that on, you should be SSHing in and runningrails consolethe normal way.
The ideas transfer cleanly, though. The two below are the high-value, low-cost subset.
Proposal
1. A real user concept (prerequisite for #2)
Today there is no notion of "who is using the console" — ADMIN_USERNAME is a shared label and dev mode has no auth at all. Before audit logging can mean anything, we need attribution.
Add a configurable resolver with a sensible fallback:
SlashConsole.configure do |c|
c.user_resolver = -> { Current.user&.email } # host app injects identity
end- If configured, the result is stamped on every logged command.
- If not configured, fall back to the Basic auth username (production) or a one-time per-session "your name" prompt stored in the session cookie (development). Unverified, but at least an auditable string — same posture as
console1984's unverifiedCONSOLE_USER.
This is small, composes with whatever auth the host app already has, and avoids slash_console pretending to be an auth system.
2. Session reason prompt + per-command audit log
Borrowing the most valuable part of console1984:
- On first access in a session, ask the user why they're opening the console. Store with
created_at+ resolved user. - Persist every submitted command and its rendered output to a DB table. Encrypt at rest with Active Record Encryption if the host app has it configured; plain text otherwise (with a README warning).
- Suggested schema (mirrors
console1984but flatter — no separate users table, no sensitive-access concept):slash_console_sessions:id,user,reason,ip,user_agent,created_atslash_console_commands:id,session_id,statements(encrypted),output(encrypted),created_at
web-console already hands us the input string in the controller, so this is straightforward — no IRB plumbing needed.
Explicitly out of scope
Refrigerator-style class freezing. Aweb-consoleattacker canObject.send(:remove_const, ...)or otherwise bypass; selling a defense we can't deliver is worse than not selling it.decrypt!/encrypt!two-mode model. Awkward in a web form; if we ever want it, do it as a per-request flag, not a stateful mode toggle.- Socket-level egress blocking to
protected_urls. Too invasive for this gem's positioning.
README update
Add a section pointing readers to console1984 + audits1984 + SSH for serious use cases, and reframe slash_console as: convenience for low-stakes apps, with attribution and an audit trail so misuse is at least traceable.
Open question
Should the audit log be opt-in via a generator (rails generate slash_console:install) or auto-create on first boot? Leaning opt-in to keep the "just add the gem, it works" path unchanged for toy apps that don't want a DB migration.
Activity