test: cover MiddlewareRateLimitingTest and teardown

This commit is contained in:
dualfroz committed 2026-09-11 16:14:16 +02:00
1 parent d706a14659
commit 0d777aaaca
1 file changed
+101
+101
View File
@@ -0,0 +1,101 @@
require 'minitest/autorun'
require 'prism'
require 'support'
class MiddlewareRateLimitingTest < Minitest::Test
include TestSupport
def setup
Prism::RateLimiter.reset!
@downstream_calls = 0
downstream = lambda do |_env|
@downstream_calls += 1
[200, {}, ['ok']]
end
@app = Prism::Middleware::RateLimiting.new(downstream)
end
def teardown
Prism::RateLimiter.reset!
end
def request(ip: '1.1.1.1', path: '/login')
env = env_for('POST', path)
env['REMOTE_ADDR'] = ip
@app.call(env)
end
def test_requests_under_the_limit_pass_through
Prism::RateLimiter.throttle('logins/ip', limit: 2, period: 60) { |req| req.env['REMOTE_ADDR'] }
2.times do
status, = request
assert_equal 200, status
end
assert_equal 2, @downstream_calls
end
def test_request_over_the_limit_is_throttled_with_retry_after
Prism::RateLimiter.throttle('logins/ip', limit: 2, period: 60) { |req| req.env['REMOTE_ADDR'] }
2.times { request }
status, headers, body = request
assert_equal 429, status
assert_equal '60', headers['Retry-After']
assert_includes body.join, 'Too Many Requests'
assert_equal 2, @downstream_calls, 'the throttled request must not reach the app'
end
def test_throttle_keys_are_independent_per_discriminator_value
Prism::RateLimiter.throttle('logins/ip', limit: 1, period: 60) { |req| req.env['REMOTE_ADDR'] }
assert_equal 200, request(ip: '1.1.1.1').first
assert_equal 200, request(ip: '2.2.2.2').first
assert_equal 429, request(ip: '1.1.1.1').first
end
def test_discriminator_returning_nil_skips_the_rule
Prism::RateLimiter.throttle('logins/ip', limit: 1, period: 60) { |_req| nil }
5.times { assert_equal 200, request.first }
end
def test_blocklisted_request_is_rejected_with_403_before_reaching_app
Prism::RateLimiter.blocklist('scanners') { |req| req.path == '/etc/passwd' }
status, = request(path: '/etc/passwd')
assert_equal 403, status
assert_equal 0, @downstream_calls
end
def test_safelist_bypasses_an_otherwise_matching_blocklist
Prism::RateLimiter.blocklist('scanners') { |_req| true }
Prism::RateLimiter.safelist('trusted') { |req| req.env['REMOTE_ADDR'] == '9.9.9.9' }
assert_equal 200, request(ip: '9.9.9.9').first
assert_equal 403, request(ip: '1.1.1.1').first
end
def test_track_rule_observes_but_never_blocks
tracked = []
Prism::RateLimiter.track('all-logins') { |req| req.env['REMOTE_ADDR'] }
Prism::RateLimiter.on_notify { |type, name, _req, payload| tracked << [type, name, payload] }
assert_equal 200, request(ip: '3.3.3.3').first
assert_equal [[:track, 'all-logins', '3.3.3.3']], tracked
end
def test_fail2ban_style_blocklist_bans_after_repeated_offenses
Prism::RateLimiter.blocklist('fail2ban pentesters') do |req|
Prism::RateLimiter.fail2ban_filter(req.env['REMOTE_ADDR'], maxretry: 2, findtime: 60, bantime: 300) do
req.path.include?('/etc/passwd')
end
end
assert_equal 200, request(ip: '5.5.5.5', path: '/etc/passwd').first
assert_equal 403, request(ip: '5.5.5.5', path: '/etc/passwd').first
assert_equal 403, request(ip: '5.5.5.5', path: '/harmless').first, 'stays banned without a new offense'
assert_equal 200, request(ip: '6.6.6.6', path: '/etc/passwd').first, 'other IPs are unaffected'
end
end