2.3 KiB
sinatra-0002: CWE-407 — provides condition calls Array#include? inside route-match loop
| Field | Value |
|---|---|
| ID | sinatra-0002 |
| Project | sinatra/sinatra |
| Severity | MEDIUM |
| Status | OPEN |
| CWE | CWE-407 (Algorithmic Complexity) |
| Found | 2026-03-27 |
Location
lib/sinatra/base.rb:1765 (inside the provides method's condition block)
def provides(*types)
types.map! { |t| mime_types(t) }
types.flatten!
condition do # <-- this block runs on every route attempt
response_content_type = response['content-type']
preferred_type = request.preferred_type(types)
if response_content_type
types.include?(response_content_type) || types.include?(response_content_type[/^[^;]+/])
# ^^^ two O(n) Array#include? scans on every route evaluation
Root Cause
provides registers a condition block that runs during process_route for every route that
uses it (line 1120: conditions.each { |c| throw :pass if c.bind(self).call == false }).
Inside the condition, types is a plain Array. Array#include? is O(n). Two back-to-back
scans happen when response_content_type is already set (the common case after middleware sets
content-type early).
With R routes each using provides and T types, every request costs O(R×T) Array scans in the
worst case (all routes are attempted before match).
Fix
Freeze types as a Set at route-registration time (once), not at request time:
def provides(*types)
types.map! { |t| mime_types(t) }
types.flatten!
types_set = types.to_set # built once at route definition time
condition do
response_content_type = response['content-type']
preferred_type = request.preferred_type(types) # keep Array for ordering
if response_content_type
types_set.include?(response_content_type) ||
types_set.include?(response_content_type[/^[^;]+/])
# O(1) hash lookup instead of O(n) scan
request.preferred_type(types) needs the Array for Accept-header ordering logic, so types
must remain an Array for that call. types_set is used only for the membership tests.
Patch
defects/sinatra/patch/sinatra-0002-provides-types-set.patch
Benchmark
defects/sinatra/unit/SinatraTest.java