Overview
Brakeman analyzes Ruby on Rails applications to identify security vulnerabilities without executing the code. The scanner examines Rails models, views, controllers, and configuration files to detect common security flaws including SQL injection, cross-site scripting, command injection, and mass assignment vulnerabilities.
The tool operates as a command-line utility that parses Rails application code and generates reports in multiple formats. Brakeman maintains an internal representation of the application structure, tracking data flow through methods and identifying potentially dangerous code patterns.
# Basic Brakeman scan
require 'brakeman'
# Scan current directory
results = Brakeman.run('.')
puts results.warnings.length
# => 5
Brakeman categorizes findings by confidence levels: High, Medium, and Weak. High confidence warnings typically indicate genuine security issues, while Medium and Weak confidence warnings may include false positives that require manual review.
# Programmatic usage with options
tracker = Brakeman.run(
app_path: '/path/to/rails/app',
output_formats: [:json, :html],
confidence_levels: [1, 2] # High and Medium only
)
tracker.warnings.each do |warning|
puts "#{warning.warning_type}: #{warning.message}"
end
The scanner supports Rails applications from version 2.3 through current versions, automatically detecting the Rails version and adjusting its analysis accordingly. Brakeman recognizes Rails conventions and patterns, understanding how data flows through typical Rails architectural components.
# Filtering warnings by type
scanner_results = Brakeman.run('/app')
sql_warnings = scanner_results.warnings.select do |w|
w.warning_type == 'SQL Injection'
end
sql_warnings.each do |warning|
puts "File: #{warning.file}"
puts "Line: #{warning.line}"
puts "Code: #{warning.format_code}"
end
Basic Usage
Install Brakeman as a gem and run it against Rails applications from the command line or integrate it programmatically into Ruby scripts. The simplest usage involves running the scanner against the current directory containing a Rails application.
gem install brakeman
cd /path/to/rails/app
brakeman
The command-line interface provides numerous options for customizing scans. Common options include specifying output formats, confidence levels, and specific warning types to include or exclude.
# Generate JSON and HTML reports
brakeman -o results.json -f json
brakeman -o report.html -f html
# Only show high confidence warnings
brakeman -w1
# Skip certain warning types
brakeman -x "Mass Assignment,Cross Site Scripting"
Programmatic usage through the Ruby API allows integration into custom tools, test suites, and continuous integration pipelines. The Brakeman.run
method accepts a hash of options corresponding to command-line flags.
require 'brakeman'
options = {
app_path: Rails.root,
output_formats: [:json],
min_confidence: 2,
skip_checks: ['MassAssignment']
}
results = Brakeman.run(options)
# Process warnings
results.warnings.each do |warning|
next if warning.confidence > 1
puts "Security Issue: #{warning.warning_type}"
puts "Location: #{warning.file}:#{warning.line}"
puts "Message: #{warning.message}"
puts "Code: #{warning.format_code}"
puts "---"
end
Configuration files allow persistent settings across scans. Brakeman reads configuration from .brakeman.yml
files in the application root directory, supporting YAML format for specifying scan options.
# .brakeman.yml
:skip_checks:
- MassAssignment
- LinkTo
:min_confidence: 2
:output_formats:
- json
- html
:output_files:
- brakeman-report.json
- brakeman-report.html
The scanner processes various file types within Rails applications, including Ruby source files, ERB templates, Haml templates, and YAML configuration files. Brakeman automatically excludes vendor directories, test directories, and other non-application code by default.
# Custom file inclusion/exclusion
options = {
app_path: '.',
skip_files: ['app/controllers/admin_controller.rb'],
only_files: ['app/models/', 'app/controllers/'],
rails_version: '6.1'
}
tracker = Brakeman.run(options)
puts "Scanned #{tracker.processed_files.length} files"
Error Handling & Debugging
Brakeman warnings require careful interpretation to distinguish genuine security vulnerabilities from false positives. Each warning includes context information including file location, line numbers, affected code, and confidence levels that aid in evaluation.
def analyze_warning_severity(warning)
case warning.confidence
when 0
"High - Likely genuine vulnerability"
when 1
"Medium - Review required"
when 2
"Weak - May be false positive"
else
"Unknown confidence level"
end
end
results = Brakeman.run('.')
results.warnings.each do |warning|
severity = analyze_warning_severity(warning)
puts "#{warning.warning_type} (#{severity})"
puts " File: #{warning.relative_path}"
puts " Method: #{warning.method}" if warning.method
puts " User input: #{warning.user_input}" if warning.user_input
end
False positives occur when Brakeman identifies potentially dangerous patterns that are actually safe in context. Common scenarios include sanitized user input, whitelisted parameters, and protected method calls that appear unsafe to static analysis.
# Handling false positives programmatically
def filter_false_positives(warnings)
warnings.reject do |warning|
# Skip warnings in test files
next true if warning.file.include?('/spec/') || warning.file.include?('/test/')
# Skip known safe patterns
if warning.warning_type == 'SQL Injection'
# Allow sanitized queries
next true if warning.format_code.include?('sanitize_sql')
end
false
end
end
filtered_warnings = filter_false_positives(results.warnings)
Missing dependencies or unsupported Rails versions can cause scan failures. Brakeman requires access to Rails models and their associations to perform accurate analysis, failing when essential files are missing or unreadable.
begin
results = Brakeman.run(
app_path: suspect_app_path,
ensure_latest: false,
quiet: true
)
rescue Brakeman::NoApplication
puts "No Rails application found at #{suspect_app_path}"
exit 1
rescue StandardError => e
puts "Scan failed: #{e.message}"
puts "Backtrace: #{e.backtrace.first(5).join("\n")}"
exit 1
end
Large codebases may encounter memory constraints or performance issues during scanning. Brakeman loads the entire application into memory for analysis, requiring sufficient RAM for complex applications with many models and controllers.
# Monitor scan performance
start_time = Time.now
memory_before = `ps -o rss= -p #{Process.pid}`.to_i
results = Brakeman.run(
app_path: large_app_path,
parallel_checks: true
)
memory_after = `ps -o rss= -p #{Process.pid}`.to_i
duration = Time.now - start_time
puts "Scan completed in #{duration.round(2)} seconds"
puts "Memory usage: #{(memory_after - memory_before) / 1024} MB"
puts "Warnings found: #{results.warnings.length}"
Template parsing errors occur when Brakeman encounters unsupported template syntax or malformed ERB code. These failures typically result in incomplete scans rather than total failures, with error details available in the scan results.
results = Brakeman.run('.')
if results.errors.any?
puts "Scan completed with errors:"
results.errors.each do |error|
puts " #{error[:error]}: #{error[:file]}"
puts " #{error[:exception]}" if error[:exception]
end
end
if results.ignored_files.any?
puts "Ignored files:"
results.ignored_files.each { |file| puts " #{file}" }
end
Production Patterns
Continuous integration environments commonly integrate Brakeman scans into automated testing pipelines, failing builds when high-confidence security vulnerabilities are detected. Exit codes indicate scan results, with non-zero codes signaling discovered vulnerabilities.
# CI/CD integration script
#!/usr/bin/env ruby
require 'brakeman'
require 'json'
def ci_security_scan
results = Brakeman.run(
app_path: ENV['RAILS_ROOT'] || '.',
output_formats: [:json],
min_confidence: 1, # High and Medium confidence only
exit_on_warn: true
)
high_confidence_warnings = results.warnings.select { |w| w.confidence == 0 }
if high_confidence_warnings.any?
puts "SECURITY SCAN FAILED"
puts "High confidence vulnerabilities found:"
high_confidence_warnings.each do |warning|
puts " #{warning.warning_type}: #{warning.file}:#{warning.line}"
end
exit 1
else
puts "Security scan passed"
exit 0
end
end
ci_security_scan if __FILE__ == $0
Docker containers and deployment environments require careful configuration to ensure Brakeman can access application code and dependencies. Scanning during the build process catches vulnerabilities before production deployment.
# Dockerfile security scanning stage
FROM ruby:3.0 AS security_scanner
WORKDIR /app
COPY Gemfile Gemfile.lock ./
RUN bundle install --without development test
COPY . .
RUN gem install brakeman
RUN brakeman --exit-on-warn --confidence-level 1 --format json --output /tmp/security-report.json
# Copy security report to final image
FROM ruby:3.0-alpine AS production
COPY --from=security_scanner /tmp/security-report.json /app/security-report.json
Team workflows often establish baseline security reports to track improvements and regressions over time. Comparing current scans against baseline reports identifies newly introduced vulnerabilities.
class SecurityBaseline
def initialize(baseline_file)
@baseline = JSON.parse(File.read(baseline_file))
end
def compare_with_current_scan
current_results = Brakeman.run('.')
current_warnings = current_results.warnings.map(&:fingerprint)
baseline_warnings = @baseline['warnings'].map { |w| w['fingerprint'] }
new_warnings = current_warnings - baseline_warnings
fixed_warnings = baseline_warnings - current_warnings
{
new_vulnerabilities: new_warnings.length,
fixed_vulnerabilities: fixed_warnings.length,
regression: new_warnings.any?
}
end
end
# Usage in CI pipeline
baseline = SecurityBaseline.new('security-baseline.json')
comparison = baseline.compare_with_current_scan
if comparison[:regression]
puts "Security regression detected: #{comparison[:new_vulnerabilities]} new issues"
exit 1
end
Monitoring systems can integrate Brakeman scan results to track security posture across multiple applications and deployments. Aggregating scan data provides organizational security metrics and trend analysis.
require 'net/http'
require 'json'
class SecurityMetrics
def initialize(metrics_endpoint)
@endpoint = URI(metrics_endpoint)
end
def report_scan_results(app_name, results)
metrics = {
application: app_name,
timestamp: Time.now.iso8601,
total_warnings: results.warnings.length,
high_confidence: results.warnings.count { |w| w.confidence == 0 },
medium_confidence: results.warnings.count { |w| w.confidence == 1 },
warning_types: results.warnings.group_by(&:warning_type).transform_values(&:length)
}
http = Net::HTTP.new(@endpoint.host, @endpoint.port)
request = Net::HTTP::Post.new(@endpoint.path)
request['Content-Type'] = 'application/json'
request.body = metrics.to_json
response = http.request(request)
puts "Metrics reported: #{response.code}"
end
end
# Automated reporting
results = Brakeman.run('.')
metrics = SecurityMetrics.new(ENV['SECURITY_METRICS_URL'])
metrics.report_scan_results(ENV['APP_NAME'], results)
Common Pitfalls
Mass assignment warnings frequently trigger false positives in Rails applications that properly configure strong parameters. Brakeman may not recognize complex parameter filtering patterns, particularly in controller inheritance hierarchies or shared parameter handling modules.
# False positive scenario - Brakeman may not detect strong parameters
class ArticlesController < ApplicationController
before_action :set_article_params, only: [:create, :update]
def create
# Brakeman might flag this as mass assignment vulnerability
@article = Article.new(@article_params)
end
private
def set_article_params
# Complex parameter filtering that Brakeman might miss
@article_params = params.require(:article).permit(
permitted_article_attributes
)
end
def permitted_article_attributes
base_attrs = [:title, :content, :published]
base_attrs += [:featured] if current_user.admin?
base_attrs
end
end
SQL injection warnings often appear for parameterized queries that are actually safe. Dynamic query building using ActiveRecord methods may trigger warnings even when properly escaping user input through Rails built-in sanitization.
# Safe code that may trigger SQL injection warnings
class ReportGenerator
def self.generate_report(filters)
query = Article.all
# Brakeman might flag these as SQL injection risks
query = query.where("published_at > ?", filters[:start_date]) if filters[:start_date]
query = query.where("category_id IN (?)", filters[:categories]) if filters[:categories]
# This is safe - Rails sanitizes the interpolated values
# But Brakeman static analysis might not recognize the safety
query
end
end
Cross-site scripting warnings may flag legitimate HTML output in administrative interfaces or areas where HTML content is intentionally rendered. The raw
and html_safe
methods trigger warnings even when the content source is trusted.
# Administrative interface that may trigger XSS warnings
class AdminDashboard
def system_status_html
status_message = SystemHealthCheck.current_status
# Brakeman flags this as potential XSS
# Even though admin content is trusted
content_tag :div, status_message.html_safe, class: 'system-status'
end
def render_user_content(content)
# This legitimately needs review - user content should be sanitized
# Brakeman correctly identifies this as risky
content.html_safe
end
end
Command injection warnings appear for legitimate system calls that don't involve user input. File system operations, backup scripts, and system integrations may trigger warnings when commands are constructed using safe, internal data sources.
class BackupManager
def perform_backup(database_name)
# Brakeman may flag this as command injection
# Even though database_name comes from configuration
timestamp = Time.now.strftime('%Y%m%d_%H%M%S')
backup_file = "/backups/#{database_name}_#{timestamp}.sql"
# This is safe - no user input involved
# But static analysis cannot always determine data source safety
system("pg_dump #{database_name} > #{backup_file}")
end
end
Version-specific warnings may not apply to the actual Rails version in use. Brakeman sometimes fails to detect the correct Rails version or applies warnings for vulnerability patterns that were fixed in newer versions.
# Check Rails version compatibility
def validate_brakeman_warnings(warnings)
rails_version = Rails::VERSION::STRING
warnings.reject do |warning|
# Skip warnings for older Rails versions
case warning.warning_code
when 'CVE-2019-5418' # File Content Disclosure
Gem::Version.new(rails_version) >= Gem::Version.new('5.2.2.1')
when 'CVE-2020-8164' # Deserialization of Untrusted Data
Gem::Version.new(rails_version) >= Gem::Version.new('6.0.3.1')
else
false
end
end
end
Configuration inheritance across multiple Rails engines or mounted applications can confuse Brakeman's analysis. The scanner may not properly understand security configurations that apply across engine boundaries or shared authentication systems.
# Complex engine setup that may confuse Brakeman
class MainApp::Application < Rails::Application
# Security configurations that apply to mounted engines
config.force_ssl = true
config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
end
# Mounted engine that inherits security settings
module AdminEngine
class Engine < ::Rails::Engine
# Brakeman might not recognize inherited security configurations
# and flag admin controllers as lacking CSRF protection
end
end
Reference
Command Line Options
Option | Description | Example |
---|---|---|
-o, --output FILE |
Write output to specified file | brakeman -o report.html |
-f, --format FORMAT |
Output format (text, html, json, csv, tabs, sarif) | brakeman -f json |
-w, --confidence-level N |
Minimum confidence level (1=high, 2=medium, 3=weak) | brakeman -w 2 |
-x, --skip-checks CHECK1,CHECK2 |
Skip specified checks | brakeman -x "MassAssignment,SSL" |
-t, --test CHECK1,CHECK2 |
Run only specified checks | brakeman -t "SQL,XSS" |
--skip-files FILE1,FILE2 |
Skip scanning specified files | brakeman --skip-files "admin.rb,*.erb" |
--rails-version VERSION |
Specify Rails version | brakeman --rails-version 6.1 |
-q, --quiet |
Suppress informational warnings | brakeman -q |
--no-exit-on-warn |
Return exit code 0 even with warnings | brakeman --no-exit-on-warn |
--compare FILE |
Compare results against baseline | brakeman --compare baseline.json |
Programmatic API Methods
Method | Parameters | Returns | Description |
---|---|---|---|
Brakeman.run(options) |
options (Hash) |
Tracker |
Execute security scan with specified options |
#warnings |
None | Array<Warning> |
Array of all detected warnings |
#errors |
None | Array<Hash> |
Parsing errors encountered during scan |
#ignored_files |
None | Array<String> |
Files skipped during analysis |
#filtered_warnings |
None | Array<Warning> |
Warnings after applying filters |
Warning Object Properties
Property | Type | Description |
---|---|---|
warning_type |
String |
Type of security vulnerability detected |
confidence |
Integer |
Confidence level (0=High, 1=Medium, 2=Weak) |
file |
String |
Absolute path to file containing vulnerability |
line |
Integer |
Line number where vulnerability occurs |
message |
String |
Descriptive message about the vulnerability |
code |
Sexp |
Abstract syntax tree of problematic code |
method |
String |
Method name containing vulnerability |
user_input |
Sexp |
Source of potentially malicious user input |
fingerprint |
String |
Unique identifier for tracking warnings across scans |
Common Warning Types
Warning Type | Description | Common Causes |
---|---|---|
SQL Injection |
Unsafe database queries | String interpolation in queries, unsafe where clauses |
Cross Site Scripting |
Unescaped output in views | Using raw , html_safe , or bypassing escaping |
Mass Assignment |
Unrestricted parameter assignment | Missing strong parameters, bulk assignment |
Command Injection |
Unsafe system command execution | User input in system , backticks, or exec calls |
File Access |
Unauthorized file system access | User-controlled file paths |
Session Setting |
Insecure session configuration | Missing secure flags, weak session keys |
Cross-Site Request Forgery |
Missing CSRF protection | Controllers without protect_from_forgery |
Redirect |
Open redirect vulnerabilities | User-controlled redirect targets |
Link To |
Dangerous link generation | User input in link helpers |
Render |
Unsafe template rendering | Dynamic template selection |
Configuration File Format
# .brakeman.yml
:skip_checks:
- MassAssignment
- LinkTo
:skip_files:
- "app/controllers/admin_controller.rb"
- "lib/legacy/*.rb"
:min_confidence: 2
:output_formats:
- json
- html
:output_files:
- brakeman-report.json
- brakeman-report.html
:print_report: true
:rails_version: "6.1"
:exit_on_warn: true
Exit Codes
Exit Code | Meaning |
---|---|
0 |
No warnings found or --no-exit-on-warn specified |
3 |
Warnings found (when --exit-on-warn is default behavior) |
4 |
Brakeman execution error |
5 |
No Rails application found |