Hi. I am travis and I work in a place long ago abandoned by the gods: New York City. By day, I am a Software Engineer at Patreon. By night, i am a normal person.

Rails Jail

The Rails console is one of my favorite things as a developer in that I can cowboy my way to a solution in seconds flat. Whether I'm debugging in development or fixing an actual production issue, I cannot resist the temptation and danger of that beautiful Rails console because it is just so handy. It is truly the WD-40 of ruby development.

But if you've spent enough time around WD-40, you know that 1) it does not get you high when you huff it, so don't even bother, and 2) it can destroy the things you own when used incorrectly. The same thing goes for the Rails console: as human beings, sometimes we forget where and even who we are. Inevitably, you will User.destroy_all on production thinking you were on development, and this will most certainly lead to copious bodily discomfort.

As an organization, it's probably best to just not give production console access to all developers. Notoriously eager and over-confident, they will eventually bring your business to a screeching halt (at least I will). If they do have this privilege, its prudent to come up with a consistent strategy for safer production access, such as blaring terminal colors. One simple strategy that doesn't depend on developers opting in is a "jailed" rails console with restricted privileges. It's hacking time!

The first thing you're going to need are a few additional gems. One is to add a little pizazz to your console (colorize) and the other is to disable web requests (webmock). It is very important that you do not require the webmock gem. Doing so will disable all web requests from your app, which you obviously do not want to do in general.

gem 'colorize', require: false
gem 'webmock', require: false

The next step in protecting your production environment from your own employees is a nice JailedConsole module, which can be placed in an initializer or in app/models/concerns, whichever you so desire.

The most important part of this code is the sandbox_console method, which will handle disabling all dangerous functionality in the console. In this method, I finally require the webmock gem, which will stub out all network requests, as well as the sidekiq/testing gem, which mocks the Sidekiq interface and prevents any jobs from being enqueued unintentionally. You can add any additional safeguards here, e.g. including the resque_spec gem if you use Resque in your app.

module JailedConsole
  extend ActiveSupport::Concern

  included do
    require 'colorize'

    def sandbox_console
      require 'webmock'
      require 'sidekiq/testing' if defined?(Sidekiq)
    end

    def user_input
      prompt("Defaulting the console into sandbox mode. "\
        "Type 'production' to disable this")
      gets.strip != 'production'
    end

    def warn(message)
      puts message.black.on_red.blink
    end

    def prompt(message)
      puts message.black.on_yellow
    end
  end
end

The last step is to hook this module into your application. This block should be included in application.rb in the class declaration for your Application class, i.e. under class Application < Rails::Application. You can examine the railties source for a better understanding of how the console is bootstrapped, but it is important to understand that this code runs after the Console class has been loaded but before the console actually starts. Hence, this spot is a reliable place to monkeypatch the console.

def require_environment!
  if defined?(Rails::Console) && Rails.env.production?
    Rails::Console.class_eval do
      class << self
        include JailedConsole

        def start(*args)
          warn('WARNING: YOU ARE ENTERING PRODUCTION. PROCEED WITH CAUTION.')

          options = args.last
          options[:sandbox] = user_input if options[:sandbox].blank?

          sandbox_console if options[:sandbox]
          new(*args).start
        end
      end
    end
  end
end

This code will prompt the console user for confirmation in the form of text input. The user can simply press ENTER and proceed in sandbox mode, or type "production" to disable the sandbox functionality. Assigning this state to options[:sandbox] is crucial because Rails::Console uses it for the built-in sandbox behavior, i.e. rolling all database changes back when the user exits the console.

Eureka! No longer must we suffer the devastation of developer hubris. Now, when the newish engineer inexorably feels ballsy enough to open a production console, he or she won't take down your entire site convinced they're running something on development. On the other hand, this is definitely still vulnerable to malevolence, so don't fire someone or make fun of their shoes before you completely revoke their production access.

RSpec Search & Destroy

Counting is Hard