---
description: Review Sinatra code for security issues, performance problems, route conflicts, and framework best practices
---
# Sinatra Review Command
Performs comprehensive code review of Sinatra applications, identifying security vulnerabilities, performance issues, routing conflicts, and deviations from best practices.
## Arguments
- **$1: path** (optional) - Path to review (defaults to current directory)
## Usage Examples
```bash
# Review current directory
/sinatra-review
# Review specific directory
/sinatra-review /path/to/sinatra-app
# Review specific file
/sinatra-review app/controllers/users_controller.rb
```
## Workflow
### Step 1: Scan and Identify Application Files
**Discovery Phase:**
1. Locate `config.ru` to identify Rack application
2. Find Sinatra application files (controllers, routes)
3. Identify application structure (classic vs modular)
4. Scan for middleware configuration
5. Locate view templates and helpers
6. Find configuration files
7. Identify database and model files
**File Patterns to Search:**
```bash
# Application files
*.rb files inheriting from Sinatra::Base
config.ru
app.rb (classic style)
app/controllers/*.rb
lib/**/*.rb
# View templates
views/**/*.erb
views/**/*.haml
views/**/*.slim
# Configuration
config/*.rb
Gemfile
.env files
```
### Step 2: Analyze Route Definitions
**Route Conflict Detection:**
Check for:
1. **Duplicate routes** with same path and HTTP method
2. **Overlapping routes** where order matters (specific before generic)
3. **Missing route constraints** leading to ambiguous matching
4. **Wildcard route conflicts**
**Examples of Issues:**
```ruby
# ISSUE: Route order conflict
get '/users/new' do
# Never reached because of wildcard below
end
get '/users/:id' do
# This catches /users/new
end
# FIX: Specific routes before wildcards
get '/users/new' do
# Now reached first
end
get '/users/:id' do
# Only catches other IDs
end
# ISSUE: Duplicate routes
get '/api/users' do
# First definition
end
get '/api/users' do
# Overwrites first - only this runs
end
# ISSUE: Missing validation
get '/users/:id' do
user = User.find(params[:id]) # What if id is not numeric?
end
# FIX: Add validation
get '/users/:id', id: /\d+/ do
user = User.find(params[:id])
end
```
**Route Analysis Report:**
```
Route Analysis:
Total routes: 25
GET: 15, POST: 5, PUT: 3, DELETE: 2
⚠ Warnings:
- Route order issue in app/controllers/users_controller.rb:15
GET /users/:id should be after GET /users/new
- Missing parameter validation in app/controllers/posts_controller.rb:32
Route GET /posts/:id should validate :id is numeric
```
### Step 3: Security Analysis
**Security Checklist:**
**1. CSRF Protection:**
```ruby
# CHECK: Is CSRF protection enabled?
use Rack::Protection
# or
use Rack::Protection::AuthenticityToken
# ISSUE: Missing CSRF for POST/PUT/DELETE
post '/users' do
User.create(params[:user]) # Vulnerable to CSRF
end
# FIX: Ensure Rack::Protection is enabled
```
**2. XSS Prevention:**
```ruby
# CHECK: Are templates auto-escaping HTML?
# ERB: Use <%= %> (escapes) not <%== %> (raw)
# ISSUE: Raw user input in template
<%== @user.bio %>
# FIX: Escape user input
<%= @user.bio %>
# CHECK: JSON responses properly encoded
# ISSUE: Manual JSON creation
get '/api/users' do
"{ \"name\": \"#{user.name}\" }" # XSS if name contains quotes
end
# FIX: Use JSON library
get '/api/users' do
json({ name: user.name })
end
```
**3. SQL Injection:**
```ruby
# ISSUE: String interpolation in queries
DB["SELECT * FROM users WHERE email = '#{params[:email]}'"]
# FIX: Use parameterized queries
DB["SELECT * FROM users WHERE email = ?", params[:email]]
# ISSUE: Unsafe ActiveRecord
User.where("email = '#{params[:email]}'")
# FIX: Use hash conditions
User.where(email: params[:email])
```
**4. Authentication & Authorization:**
```ruby
# CHECK: Protected routes have authentication
# ISSUE: Admin route without auth check
delete '/users/:id' do
User.find(params[:id]).destroy # No auth check!
end
# FIX: Add authentication
before '/admin/*' do
halt 401 unless current_user&.admin?
end
# CHECK: Session security
# ISSUE: Weak session configuration
use Rack::Session::Cookie, secret: 'easy'
# FIX: Strong secret and secure flags
use Rack::Session::Cookie,
secret: ENV['SESSION_SECRET'], # Long random string
same_site: :strict,
httponly: true,
secure: production?
```
**5. Mass Assignment:**
```ruby
# ISSUE: Accepting all params
User.create(params)
# FIX: Whitelist allowed attributes
def user_params
params.slice(:name, :email, :bio)
end
User.create(user_params)
```
**6. File Upload Security:**
```ruby
# ISSUE: Unrestricted file uploads
post '/upload' do
File.write("uploads/#{params[:file][:filename]}", params[:file][:tempfile].read)
end
# FIX: Validate file type and sanitize filename
post '/upload' do
file = params[:file]
# Validate content type
halt 400 unless ['image/jpeg', 'image/png'].include?(file[:type])
# Sanitize filename
filename = File.basename(file[:filename]).gsub(/[^a-zA-Z0-9\._-]/, '')
# Save with random name
secure_name = "#{SecureRandom.hex}-#{filename}"
File.write("uploads/#{secure_name}", file[:tempfile].read)
end
```
**7. Information Disclosure:**
```ruby
# ISSUE: Detailed error messages in production
configure :production do
set :show_exceptions, true # Exposes stack traces
end
# FIX: Hide errors in production
configure :production do
set :show_exceptions, false
set :dump_errors, false
end
error do
log_error(env['sinatra.error'])
json({ error: 'Internal server error' }, 500)
end
```
**Security Report:**
```
Security Analysis:
✓ CSRF protection enabled (Rack::Protection)
✓ Session configured securely
⚠ Potential Issues:
- SQL injection risk in app/models/user.rb:45
- Raw HTML output in views/profile.erb:12
- Missing authentication check in app/controllers/admin_controller.rb:23
- Weak session secret detected
Critical: 1
High: 2
Medium: 3
Low: 2
```
### Step 4: Review Middleware Configuration
**Middleware Analysis:**
Check for:
1. **Missing essential middleware** (Protection, CommonLogger)
2. **Incorrect ordering** (e.g., session after auth)
3. **Performance issues** (e.g., no compression)
4. **Security middleware** properly configured
**Common Issues:**
```ruby
# ISSUE: Missing compression
# FIX: Add Rack::Deflater
use Rack::Deflater
# ISSUE: Session middleware after authentication
use TokenAuth
use Rack::Session::Cookie # Session needed by auth!
# FIX: Session before authentication
use Rack::Session::Cookie
use TokenAuth
# ISSUE: No security headers
# FIX: Add Rack::Protection
use Rack::Protection, except: [:session_hijacking]
# ISSUE: Static file serving after application
run MyApp
use Rack::Static # Never reached!
# FIX: Static before application
use Rack::Static, urls: ['/css', '/js'], root: 'public'
run MyApp
```
**Middleware Report:**
```
Middleware Configuration:
✓ Rack::CommonLogger (logging)
✓ Rack::Session::Cookie (sessions)
✓ Rack::Protection (security)
⚠ Warnings:
- Missing Rack::Deflater (compression)
- Middleware order issue: Session should be before CustomAuth
- Consider adding Rack::Attack for rate limiting
```
### Step 5: Performance Assessment
**Performance Patterns to Check:**
**1. Database Query Optimization:**
```ruby
# ISSUE: N+1 queries
get '/users' do
users = User.all
users.map { |u| { name: u.name, posts: u.posts.count } }
# Queries DB for each user's posts
end
# FIX: Eager load or use counter cache
get '/users' do
users = User.eager(:posts).all
users.map { |u| { name: u.name, posts: u.posts.count } }
end
# ISSUE: Loading entire collection
get '/users' do
json User.all.map(&:to_hash) # Load all users in memory
end
# FIX: Paginate
get '/users' do
page = params[:page]&.to_i || 1
per_page = 50
users = User.limit(per_page).offset((page - 1) * per_page)
json users.map(&:to_hash)
end
```
**2. Caching Opportunities:**
```ruby
# ISSUE: Expensive operation on every request
get '/stats' do
json calculate_expensive_stats # Takes 2 seconds
end
# FIX: Add caching
get '/stats' do
stats = cache.fetch('stats', expires_in: 300) do
calculate_expensive_stats
end
json stats
end
# ISSUE: No HTTP caching headers
get '/public/data' do
json PublicData.all
end
# FIX: Add cache control
get '/public/data' do
cache_control :public, max_age: 3600
json PublicData.all
end
```
**3. Response Optimization:**
```ruby
# ISSUE: Rendering large response synchronously
get '/large-export' do
csv = generate_large_csv # Blocks for 30 seconds
send_file csv
end
# FIX: Stream or queue as background job
get '/large-export' do
stream do |out|
CSV.generate(out) do |csv|
User.find_each do |user|
csv << user.to_csv_row
end
end
end
end
```
**Performance Report:**
```
Performance Analysis:
⚠ Issues Detected:
- Potential N+1 query in app/controllers/users_controller.rb:42
- Missing pagination in GET /api/posts (returns all records)
- No caching headers on GET /api/public/data
- Expensive operation in GET /stats without caching
Recommendations:
- Add database query optimization (eager loading)
- Implement pagination for collection endpoints
- Add HTTP caching headers for static content
- Consider Redis caching for expensive operations
```
### Step 6: Error Handling Review
**Error Handling Patterns:**
```ruby
# ISSUE: No error handlers defined
get '/users/:id' do
User.find(params[:id]) # Raises if not found, shows stack trace
end
# FIX: Add error handlers
error ActiveRecord::RecordNotFound do
json({ error: 'Not found' }, 404)
end
error 404 do
json({ error: 'Endpoint not found' }, 404)
end
error 500 do
json({ error: 'Internal server error' }, 500)
end
# ISSUE: Not handling exceptions in routes
post '/users' do
User.create!(params) # Raises on validation error
end
# FIX: Handle exceptions
post '/users' do
user = User.create(params)
if user.persisted?
json(user.to_hash, 201)
else
json({ errors: user.errors }, 422)
end
end
```
### Step 7: Testing Coverage
**Test Analysis:**
Check for:
1. Test files exist
2. Route coverage
3. Error case testing
4. Integration vs unit tests
5. Test quality and patterns
**Report:**
```
Testing Analysis:
Framework: RSpec
Total specs: 45
Coverage: 78%
⚠ Missing Tests:
- No tests for POST /api/users
- Error cases not tested in app/controllers/posts_controller.rb
- Missing integration tests for authentication flow
Recommendations:
- Add tests for all POST/PUT/DELETE routes
- Test error scenarios (404, 422, 500)
- Increase coverage to 90%+
```
### Step 8: Generate Comprehensive Report
**Final Report Structure:**
```
================================================================================
SINATRA CODE REVIEW REPORT
================================================================================
Project: my-sinatra-app
Path: /path/to/app
Date: 2024-01-15
Reviewer: Sinatra Review Tool
--------------------------------------------------------------------------------
SUMMARY
--------------------------------------------------------------------------------
Total Issues: 15
Critical: 2
High: 4
Medium: 6
Low: 3
Categories:
Security: 5 issues
Performance: 4 issues
Best Practices: 6 issues
--------------------------------------------------------------------------------
CRITICAL ISSUES
--------------------------------------------------------------------------------
1. SQL Injection Vulnerability
Location: app/models/user.rb:45
Severity: Critical
Issue:
DB["SELECT * FROM users WHERE email = '#{email}'"]
Fix:
DB["SELECT * FROM users WHERE email = ?", email]
Impact: Attacker can execute arbitrary SQL queries
2. Missing Authentication on Admin Route
Location: app/controllers/admin_controller.rb:23
Severity: Critical
Issue:
delete '/users/:id' do
User.find(params[:id]).destroy
end
Fix:
before '/admin/*' do
authenticate_admin!
end
Impact: Unauthorized users can delete records
--------------------------------------------------------------------------------
HIGH PRIORITY ISSUES
--------------------------------------------------------------------------------
[List high priority issues...]
--------------------------------------------------------------------------------
RECOMMENDATIONS
--------------------------------------------------------------------------------
Security:
- Enable Rack::Protection::AuthenticityToken for CSRF
- Rotate session secret to strong random value
- Implement rate limiting with Rack::Attack
- Add Content-Security-Policy headers
Performance:
- Add Rack::Deflater for response compression
- Implement caching strategy (Redis or Memcached)
- Add pagination to collection endpoints
- Optimize database queries (N+1 issues)
Testing:
- Increase test coverage to 90%+
- Add integration tests for critical flows
- Test error scenarios
- Add security-focused tests
Best Practices:
- Extract business logic to service objects
- Use helpers for repeated code
- Implement proper error handling
- Add API documentation
--------------------------------------------------------------------------------
DETAILED FINDINGS
--------------------------------------------------------------------------------
[Full list of all issues with locations, descriptions, and fixes]
================================================================================
END REPORT
================================================================================
```
## Review Categories
### Security
- CSRF protection
- XSS prevention
- SQL injection
- Authentication/Authorization
- Session security
- Mass assignment
- File upload security
- Information disclosure
- Secure headers
### Performance
- Database query optimization
- N+1 queries
- Caching opportunities
- Response optimization
- Static asset handling
- Connection pooling
### Best Practices
- Route organization
- Error handling
- Code organization
- Helper usage
- Configuration management
- Logging
- Documentation
### Testing
- Test coverage
- Test quality
- Missing tests
- Test organization
## Output Format
- Console output with colored severity indicators
- Detailed report with file locations and line numbers
- Suggested fixes with code examples
- Priority-sorted issue list
- Summary statistics
## Error Handling
- Handle non-Sinatra Ruby applications gracefully
- Report when application structure cannot be determined
- Skip non-readable files
- Handle parse errors in Ruby files