Skip to content

Instantly share code, notes, and snippets.

@st0012
Last active August 29, 2022 07:35
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save st0012/c281c141035b2fa64d2f3abb1ccafc74 to your computer and use it in GitHub Desktop.
Save st0012/c281c141035b2fa64d2f3abb1ccafc74 to your computer and use it in GitHub Desktop.
Debugging Challenge

Introduction

This challenge tests if you can locate the cause of a Rails issue in 5 iterations (script execution). Although it's a Rails issue, prior knowledge on Rails is not required.

The 5 iterations limit may feel constraining, but it is intentional. If you can't locate the cause within the limitation, that's totally fine.

After the challenge, there's a simple questionnaire. If you can answer the questions, it'll help me prepare my talk for RubyKaigi. Any feedback will be appreciated 🙏

I recommend allocating 1.5 hr for the challenge and the questions

Rules

  • Take as much time as you need. But you only have 5 iterations.
  • You can look up any documentation or source code. But please don't try to find the related Rails issue.
  • You can use any gems/tools (or not).

Steps

  1. Make sure you have the Ruby version that'll work with the tools you want to use. And also remember to activate it like chruby 3.1.2

  2. Copy this Ruby script to a file (e.g. test.rb)

    # frozen_string_literal: true
    
    require "rubygems/source"
    require "bundler/inline"
    
    gemfile(true) do
      source "https://rubygems.org"
    
      gem "activesupport", "7.0.3"
    end
    
    require "active_support"
    require "active_support/core_ext/object/blank"
    require "minitest/autorun"
    
    class BugTest < Minitest::Test
      def test_stuff
        old_secret = SecureRandom.base64(24)
        old_encryptor = ActiveSupport::MessageEncryptor.new old_secret
    
        new_secret = SecureRandom.base64(24)
        new_encryptor = ActiveSupport::MessageEncryptor.new new_secret
        new_encryptor.rotate old_secret
    
        # ========= Tests to help you understand the expected behavior ==========
    
        # Old encryptor roundtrips a 'nil' value correctly
        assert_nil old_encryptor.decrypt_and_verify(old_encryptor.encrypt_and_sign(nil))
    
        # New encryptor roundtrips a 'nil' value correctly
        assert_nil new_encryptor.decrypt_and_verify(new_encryptor.encrypt_and_sign(nil))
    
        # Old encryptor cannot decrypt data encrypted with the new secret
        assert_raises(ActiveSupport::MessageVerifier::InvalidSignature) do
          old_encryptor.decrypt_and_verify(new_encryptor.encrypt_and_sign("test"))
        end
    
        # New encryptor correctly decrypts data encrypted with the old secret (being rotated)
        assert_equal "test", new_encryptor.decrypt_and_verify(old_encryptor.encrypt_and_sign("test"))
    
        # ========= Find out why this fails ==========
        #
        # New encryptor should be able to decrypt a 'nil' value encrypted with the old secret
        assert_nil new_encryptor.decrypt_and_verify(old_encryptor.encrypt_and_sign(nil))
        
        # Once you finished the challenge, please fill in this questionnaire as well. Thank you :-)
        # https://forms.gle/EZK6qRhJeFEkQ6kW6
      end
    end
  3. Run it once to make sure it can execute on your machine - ruby test.rb

    • You will see the last assertion failing, which indicates the unexpected behavior.
  4. Add your debugging tool to the gemfile block, and require it. I'll use pry as an example:

    # frozen_string_literal: true
    
    require "rubygems/source"
    require "bundler/inline"
    
    gemfile(true) do
      source "https://rubygems.org"
    
      gem "activesupport", "7.0.3"
      gem "pry"
    end
    
    require "active_support"
    require "active_support/core_ext/object/blank"
    require "minitest/autorun"
    require "pry"
    
    # ....
  5. Run it again to make sure it's not broken by the tool - ruby test.rb

  6. Locate the cause in ActiveSupport in 5 iterations. In other words, you can only run ruby test.rb for 5 times from now.

  7. After you've finished the challenge (whether within/outside 5 iterations, or you decided not to continue), please answer this simple questionnaire.

  8. Thank you for your participation 🙌 I hope it's fun and also helpful 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment