Today I Learned

rack difference after upgrade from 2.0.7 to 2.2.2

I had to update rack because of the security issues with the previously used version. Unfortunately specs for this controller:

module Internal
  module Dashboard
    class StatisticsController < Sinatra::Base
      include Serial::RailsHelpers

      use Rack::Auth::Basic, 'Protected Area' do |username, password|
        username == App.config.internal_basic_auth_name &&
          password == App.config.internal_basic_auth_password
      end

      get '/', provides: :json do
        statistics = InternalStatistics.new(year: params[:year], month: params[:month])

        serialize(statistics).to_json
      end

      private

      def view_context
        Rails.application.routes.url_helpers
      end
    end
  end
end

started to fail in one specific case:

require 'rails_helper'

RSpec.describe Internal::Dashboard::StatisticsController do
  def post(params: {}, token:)
    env = {
      'REQUEST_METHOD' => 'GET',
      'CONTENT_TYPE' => 'application/json',
      'QUERY_STRING' => "&#{params.to_param}",
      'HTTP_AUTHORIZATION' => "Basic #{token}",
      'rack.input' => StringIO.new('')
    }

    status, _headers, response_body = Internal::Dashboard::StatisticsController.new.call(env)
    { status: status, body: response_body.first }
  end

  it 'rejects unauthorized requests' do
    response = post(token: nil)
    
    expect(response[:status]).to eq 401
  end
end

I started to get 400 instead of 401. Because I was not familiar with the code I started to digging in if this is a real problem and what causing it.

After comparing the code in rack gem I have noticed that basic? method has changed.

From (rack-2.0.7/lib/rack/auth/basic.rb)

def basic?
  "basic" == scheme
end

to (rack-2.2.2/lib/rack/auth/basic.rb)

def basic?
  "basic" == scheme && credentials.length == 2
end

So it is a correct behaviour now but if we want to get 401 status in specs we have to pass wrong (not empty) HTTP_AUTHORIZATION which we can do like this:

it 'rejects unauthorized requests' do
  response = post(token: Base64.strict_encode64(":"))
  
  expect(response[:status]).to eq 401
end