hanami-controller/spec/unit/hanami/action/csrf_protection_spec.rb

112 lines
3.3 KiB
Ruby

# frozen_string_literal: true
RSpec.describe Hanami::Action::CSRFProtection do
subject(:action) {
Class.new(Hanami::Action) {
include Hanami::Action::CSRFProtection
}.new
}
let(:response) { action.(request) }
describe "Requests requiring protection (Non-idempotent request methods)" do
let(:request) { {"REQUEST_METHOD" => "POST"} }
context "No existing session" do
let(:request) { super().merge("rack.session" => {}) }
context "non-matching CSRF token in request" do
let(:request) { super().merge(_csrf_token: "non-matching") }
it "rejects the request" do
expect { response }.to raise_error Hanami::Action::InvalidCSRFTokenError
end
end
context "missing CSRF token in request" do
it "rejects the request" do
expect { response }.to raise_error Hanami::Action::InvalidCSRFTokenError
end
end
end
context "Existing session" do
let(:request) { super().merge("rack.session" => session) }
let(:session) { {_csrf_token: session_token} }
let(:session_token) { "abc123" }
context "matching CSRF token in request" do
let(:request) { super().merge(_csrf_token: session_token) }
it "accepts the request" do
expect(response.status).to eq 200
end
end
context "non-matching CSRF token in request" do
let(:request) { super().merge(_csrf_token: "non-matching") }
it "rejects the request" do
expect { response }.to raise_error Hanami::Action::InvalidCSRFTokenError
end
end
context "missing CSRF token in request" do
it "rejects the request" do
expect { response }.to raise_error Hanami::Action::InvalidCSRFTokenError
end
end
context "CSRF checks skipped" do
subject(:action) {
Class.new(Hanami::Action) {
include Hanami::Action::CSRFProtection
def verify_csrf_token?(_req, _res)
false
end
}.new
}
context "missing CSRF token in request" do
it "accepts the request" do
expect(response.status).to eq 200
end
end
context "non-matching CSRF token in request" do
let(:request) { super().merge(_csrf_token: "non-matching") }
it "accepts the request" do
expect(response.status).to eq 200
end
end
end
end
end
context "Requests not requiring protection (Idempotent request methods)" do
%w[GET HEAD TRACE OPTIONS].each do |request_method|
describe request_method do
let(:request) { {"REQUEST_METHOD" => request_method} }
context "No existing session" do
it "sets a CSRF token in the response session" do
expect(response.session[:_csrf_token]).to be_a_kind_of String
end
end
context "Existing session" do
let(:request) { super().merge("rack.session" => session) }
let(:session) { {_csrf_token: session_token} }
let(:session_token) { "abc123" }
it "includes the existing CSRF token in the response session" do
expect(response.session[:_csrf_token]).to eq session_token
end
end
end
end
end
end