Overview
Vulnerability assessment identifies security weaknesses in software systems before attackers exploit them. The process examines applications, infrastructure, configurations, and dependencies to catalog potential security issues. Organizations run vulnerability assessments during development, before deployment, and continuously in production environments.
The assessment process differs from penetration testing. Vulnerability assessment catalogs potential weaknesses across the entire system surface. Penetration testing attempts to exploit specific vulnerabilities to measure actual risk. Both practices complement each other in a security program.
Vulnerability assessments generate prioritized lists of security issues. Each identified vulnerability receives a severity rating based on potential impact and exploitability. Teams address critical vulnerabilities immediately while scheduling lower-priority issues for future releases.
# Basic vulnerability scan result structure
vulnerability = {
id: 'CVE-2024-1234',
severity: 'HIGH',
package: 'rails',
version: '6.0.0',
fixed_in: '6.0.6.1',
description: 'SQL injection in ActiveRecord'
}
Modern vulnerability assessment integrates with continuous integration pipelines. Automated scans run on every commit or pull request. Failed scans block deployment when critical vulnerabilities appear. This shift-left approach catches security issues earlier in the development cycle.
Key Principles
Vulnerability assessment operates on several core principles. The first principle states that all software contains vulnerabilities. No system achieves perfect security. Assessment identifies known vulnerabilities and estimates unknown risk based on code complexity and external dependencies.
The second principle addresses comprehensive coverage. Effective assessment examines multiple layers: application code, dependencies, infrastructure, configurations, and deployment environments. Missing any layer creates blind spots where vulnerabilities hide.
The third principle prioritizes vulnerabilities by actual risk rather than theoretical severity. A critical vulnerability in an internal admin tool accessed only by authenticated users presents less immediate risk than a medium vulnerability in a public API endpoint. Risk calculation combines severity, exploitability, and exposure.
# Vulnerability risk calculation
class VulnerabilityRisk
def calculate(vulnerability, context)
base_score = cvss_score(vulnerability)
exposure_factor = calculate_exposure(context)
exploitability = check_exploitability(vulnerability)
(base_score * exposure_factor * exploitability).round(1)
end
private
def calculate_exposure(context)
return 1.5 if context.public_facing?
return 1.2 if context.authenticated_users?
return 0.8 if context.internal_only?
1.0
end
end
The fourth principle emphasizes continuous assessment. New vulnerabilities appear daily in dependencies and frameworks. A system secure today may contain critical vulnerabilities tomorrow when researchers publish exploits for libraries the system uses.
The fifth principle requires actionable reporting. Assessment tools produce thousands of findings. Effective assessment filters noise, deduplicates issues, and provides clear remediation paths. Reports must answer: what vulnerability exists, where it appears, how severe the risk, and how to fix it.
Vulnerability databases form the foundation of assessment. The National Vulnerability Database (NVD) maintains Common Vulnerabilities and Exposures (CVE) identifiers. Each CVE receives a Common Vulnerability Scoring System (CVSS) score indicating severity. Scores range from 0.0 to 10.0, with 9.0-10.0 classified as critical.
Assessment distinguishes between vulnerability types. Dependency vulnerabilities affect third-party libraries. Configuration vulnerabilities stem from insecure settings. Code vulnerabilities originate in application logic. Infrastructure vulnerabilities exist in servers, containers, and orchestration platforms. Each type requires different detection and remediation approaches.
Ruby Implementation
Ruby applications face vulnerabilities across multiple layers. RubyGems packages may contain security flaws. Rails applications inherit vulnerabilities from the framework. Application code introduces logic errors. Ruby provides several tools and patterns for vulnerability assessment.
Bundler Audit scans Gemfile.lock against a database of known vulnerabilities. The tool compares installed gem versions with vulnerability reports. Running bundler-audit after dependency updates catches known security issues.
# Gemfile additions for security scanning
group :development, :test do
gem 'bundler-audit', require: false
gem 'brakeman', require: false
end
# Running scans
# bundle exec bundler-audit check --update
# bundle exec brakeman -A -q
Brakeman performs static analysis on Rails applications. The tool examines routes, controllers, models, and views for common vulnerability patterns. Brakeman detects SQL injection, cross-site scripting, mass assignment, and other Rails-specific issues without executing code.
# Brakeman configuration in config/brakeman.yml
application_path: "."
quiet: true
min_confidence: 2
ignore_file: config/brakeman.ignore
# Suppress specific warnings
{
"ignored_warnings": [
{
"warning_type": "SQL Injection",
"fingerprint": "abc123...",
"note": "False positive - parameter sanitized upstream"
}
]
}
Custom vulnerability checks integrate into Ruby applications through Rake tasks. These tasks scan configurations, check file permissions, verify SSL certificates, and validate security headers.
# lib/tasks/security_check.rake
namespace :security do
desc "Run comprehensive security checks"
task check: :environment do
SecurityChecker.new.run_all_checks
end
end
class SecurityChecker
def run_all_checks
check_ssl_certificates
check_security_headers
check_database_encryption
check_file_permissions
check_secret_exposure
end
private
def check_ssl_certificates
expiring_soon = Certificate.where('expires_at < ?', 30.days.from_now)
if expiring_soon.any?
warn "SSL certificates expiring: #{expiring_soon.map(&:domain)}"
end
end
def check_security_headers
required_headers = [
'X-Frame-Options',
'X-Content-Type-Options',
'Strict-Transport-Security'
]
missing = required_headers - Rails.application.config.action_dispatch.default_headers.keys
warn "Missing security headers: #{missing}" if missing.any?
end
end
Ruby's dynamic nature creates unique vulnerability patterns. Method calls resolved at runtime enable reflection attacks. Unsafe deserialization of YAML or Marshal data leads to remote code execution. Dynamic SQL construction causes injection vulnerabilities.
# Vulnerable: Unsafe YAML deserialization
def load_config(yaml_string)
YAML.load(yaml_string) # Dangerous - executes arbitrary code
end
# Secure: Safe YAML loading
def load_config(yaml_string)
YAML.safe_load(yaml_string, permitted_classes: [Date, Time])
end
# Vulnerable: Dynamic SQL with string interpolation
def find_users(name)
User.where("name = '#{name}'") # SQL injection risk
end
# Secure: Parameterized queries
def find_users(name)
User.where("name = ?", name)
end
Rails provides security mechanisms requiring proper configuration. Content Security Policy headers prevent XSS attacks. Strong parameters prevent mass assignment vulnerabilities. Encrypted credentials protect secrets. Assessment verifies these mechanisms activate correctly.
# config/initializers/content_security_policy.rb
Rails.application.config.content_security_policy do |policy|
policy.default_src :self, :https
policy.script_src :self, :https
policy.style_src :self, :https, :unsafe_inline
policy.img_src :self, :https, :data
policy.connect_src :self, :https
end
# Strong parameters in controllers
class UsersController < ApplicationController
def create
@user = User.new(user_params)
# ...
end
private
def user_params
params.require(:user).permit(:name, :email, :role)
end
end
Implementation Approaches
Organizations implement vulnerability assessment using different strategies based on resources, risk tolerance, and compliance requirements. Each approach balances thoroughness against operational overhead.
The scheduled scanning approach runs vulnerability scans at fixed intervals. Weekly or monthly scans examine the entire application stack. This approach suits stable applications with infrequent deployments. Scheduled scans identify newly disclosed vulnerabilities in existing dependencies. The disadvantage: vulnerabilities introduced between scans remain undetected until the next scan cycle.
# Scheduled vulnerability scan via cron job
# config/schedule.rb (using whenever gem)
every :sunday, at: '2am' do
rake "security:full_scan"
end
# lib/tasks/security.rake
namespace :security do
task full_scan: :environment do
results = VulnerabilityScanner.new.scan_all
VulnerabilityReport.create!(
scan_date: Time.current,
findings: results,
status: results.critical.any? ? 'failed' : 'passed'
)
if results.critical.any?
SecurityMailer.critical_findings(results).deliver_now
end
end
end
The continuous integration approach integrates vulnerability scans into CI/CD pipelines. Every commit triggers security checks. Builds fail when scans detect critical vulnerabilities. This approach catches security issues immediately but increases build time and may block deployments for false positives.
The hybrid approach combines scheduled comprehensive scans with fast CI checks. CI pipelines run quick dependency scans and static analysis. Scheduled scans perform deeper analysis including dynamic testing and infrastructure checks. This approach balances speed and thoroughness.
The risk-based approach prioritizes assessment based on component criticality. Public-facing APIs receive continuous monitoring. Internal admin tools receive weekly scans. Development environments receive monthly checks. This approach optimizes resource allocation but requires accurate risk classification.
# Risk-based scanning configuration
class VulnerabilityAssessment
SCAN_FREQUENCIES = {
critical: 1.hour,
high: 4.hours,
medium: 1.day,
low: 1.week
}
def schedule_scans
Component.find_each do |component|
frequency = SCAN_FREQUENCIES[component.risk_level]
ScheduledScan.create!(
component: component,
frequency: frequency,
next_run: Time.current + frequency
)
end
end
end
The agent-based approach deploys monitoring agents on production systems. Agents continuously monitor for suspicious activity and configuration changes. This approach provides real-time visibility but adds runtime overhead and operational complexity.
The agentless approach uses external scanners without installing software on target systems. Scanners authenticate via APIs or credentials to examine systems remotely. This approach avoids agent maintenance but provides less runtime visibility.
Tools & Ecosystem
Ruby vulnerability assessment relies on several specialized tools. Each tool addresses different aspects of security scanning.
Bundler Audit checks RubyGems dependencies against known vulnerabilities. The tool maintains a database of CVEs affecting Ruby gems. Regular database updates ensure detection of newly disclosed vulnerabilities. Bundler Audit integrates easily into CI pipelines and pre-commit hooks.
# .github/workflows/security.yml
name: Security Scan
on: [push, pull_request]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 3.2
- name: Install dependencies
run: bundle install
- name: Run Bundler Audit
run: bundle exec bundler-audit check --update
- name: Run Brakeman
run: bundle exec brakeman -A -q --no-pager
Brakeman analyzes Rails applications for security vulnerabilities. The tool understands Rails conventions and identifies framework-specific issues. Brakeman detects SQL injection, XSS, CSRF, mass assignment, redirect vulnerabilities, and dangerous code evaluation. The tool produces minimal false positives but requires configuration for complex applications.
RubyCritic combines static analysis tools including Reek, Flay, and Flog. While not specifically security-focused, code complexity metrics indicate areas likely containing vulnerabilities. High complexity code requires more thorough security review.
Dawn Scanner performs security scanning for Ruby and Rails applications. The tool checks for outdated dependencies, insecure configurations, and code vulnerabilities. Dawn provides detailed remediation guidance for identified issues.
# Running Dawn scanner
# gem install dawnscanner
# dawn -C /path/to/rails/app
# Programmatic usage
require 'dawn/engine'
engine = Dawn::Engine.new
engine.load_knowledge_base
results = engine.scan('/path/to/app')
results.each do |finding|
puts "#{finding.severity}: #{finding.message}"
puts "File: #{finding.file}:#{finding.line}"
puts "Remediation: #{finding.remediation}"
end
Dependency-Check by OWASP scans project dependencies for known vulnerabilities. The tool supports multiple ecosystems including Ruby. Dependency-Check integrates with Jenkins, GitLab CI, and GitHub Actions.
Snyk provides commercial vulnerability scanning with free tiers for open source projects. The tool monitors dependencies continuously and creates pull requests with security patches. Snyk supports Ruby, JavaScript, Python, and other languages.
GitHub Dependabot automatically detects vulnerable dependencies in repositories. The tool creates pull requests updating dependencies to secure versions. Dependabot supports semantic versioning and maintains compatibility with existing code.
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "bundler"
directory: "/"
schedule:
interval: "daily"
open-pull-requests-limit: 10
reviewers:
- "security-team"
labels:
- "dependencies"
- "security"
Custom scanning scripts extend these tools for organization-specific requirements. Scripts check custom security policies, validate internal compliance requirements, and integrate with existing security infrastructure.
# Custom security scanner
class CustomSecurityScanner
def scan(directory)
findings = []
findings += check_environment_variables(directory)
findings += check_database_configurations(directory)
findings += check_api_keys(directory)
findings += check_logging_configuration(directory)
findings
end
private
def check_environment_variables(directory)
findings = []
env_file = File.join(directory, '.env')
if File.exist?(env_file)
findings << {
severity: 'MEDIUM',
message: '.env file should not be committed',
file: env_file
}
end
findings
end
def check_api_keys(directory)
findings = []
Dir.glob("#{directory}/**/*.rb").each do |file|
content = File.read(file)
if content =~ /api[_-]?key\s*=\s*['"][a-zA-Z0-9]{20,}['"]/i
findings << {
severity: 'CRITICAL',
message: 'Hardcoded API key detected',
file: file
}
end
end
findings
end
end
Security Implications
Vulnerability assessment itself introduces security considerations. Assessment tools require access to source code, dependencies, and infrastructure. Improper tool configuration exposes sensitive information or creates new attack vectors.
Scan result data contains security-sensitive information. Results reveal application architecture, dependency versions, and specific vulnerabilities. Organizations must protect scan results with access controls and encryption. Leaking scan results provides attackers with a roadmap of system weaknesses.
# Secure storage of vulnerability scan results
class VulnerabilityScanResult < ApplicationRecord
encrypts :raw_findings
encrypts :remediation_details
belongs_to :application
has_many :vulnerability_findings, dependent: :destroy
scope :critical, -> { where(max_severity: 'CRITICAL') }
scope :recent, -> { where('created_at > ?', 30.days.ago) }
def self.create_from_scan(scan_data)
transaction do
result = create!(
application_id: scan_data[:application_id],
scan_type: scan_data[:scan_type],
max_severity: calculate_max_severity(scan_data[:findings]),
raw_findings: scan_data.to_json
)
scan_data[:findings].each do |finding|
result.vulnerability_findings.create!(
vulnerability_id: finding[:cve],
severity: finding[:severity],
component: finding[:component],
remediation: finding[:fix]
)
end
result
end
end
end
Automated remediation introduces risk. Automatically updating dependencies may introduce breaking changes or new vulnerabilities. Organizations balance security improvements against stability requirements. Staging environments test automatic updates before production deployment.
False positives in vulnerability scanning create alert fatigue. Teams ignore warnings after repeated false alarms. Tuning scanners to reduce false positives requires ongoing effort. Documented suppressions explain why specific findings do not apply.
# Vulnerability finding suppression with audit trail
class VulnerabilitySuppression < ApplicationRecord
belongs_to :vulnerability_finding
belongs_to :suppressed_by, class_name: 'User'
validates :reason, presence: true
validates :expiration_date, presence: true
scope :active, -> { where('expiration_date > ?', Time.current) }
scope :expired, -> { where('expiration_date <= ?', Time.current) }
after_create :notify_security_team
def self.suppress(finding, reason:, suppressed_by:, expires_in: 90.days)
create!(
vulnerability_finding: finding,
reason: reason,
suppressed_by: suppressed_by,
expiration_date: expires_in.from_now
)
end
private
def notify_security_team
SecurityMailer.suppression_created(self).deliver_later
end
end
Scanner credentials require secure management. Scanners need access to production systems for complete assessment. Compromised scanner credentials grant attackers similar access. Credentials should use minimal required permissions, rotate regularly, and log all access.
Vulnerability disclosure policies govern how organizations handle discovered vulnerabilities. Responsible disclosure gives vendors time to patch before public announcement. Bug bounty programs incentivize external researchers to report vulnerabilities privately. Clear disclosure policies prevent security through obscurity while protecting users.
Practical Examples
This example demonstrates implementing a comprehensive vulnerability assessment pipeline for a Rails application. The pipeline runs multiple security checks and consolidates results.
# app/services/security_pipeline.rb
class SecurityPipeline
def initialize(application_path)
@application_path = application_path
@results = []
end
def run
run_bundler_audit
run_brakeman
run_custom_checks
consolidate_results
end
private
def run_bundler_audit
output = `cd #{@application_path} && bundle exec bundler-audit check --format json 2>&1`
results = JSON.parse(output)
results['vulnerabilities'].each do |vuln|
@results << {
tool: 'bundler-audit',
severity: determine_severity(vuln['criticality']),
category: 'dependency',
gem: vuln['gem'],
version: vuln['version'],
cve: vuln['cve'],
advisory: vuln['url'],
patched_versions: vuln['patched_versions']
}
end
rescue JSON::ParserError => e
@results << {
tool: 'bundler-audit',
severity: 'ERROR',
message: "Failed to parse output: #{e.message}"
}
end
def run_brakeman
output = `cd #{@application_path} && bundle exec brakeman -f json -q 2>&1`
results = JSON.parse(output)
results['warnings'].each do |warning|
@results << {
tool: 'brakeman',
severity: warning['confidence'],
category: warning['warning_type'],
file: warning['file'],
line: warning['line'],
message: warning['message'],
code: warning['code']
}
end
rescue JSON::ParserError => e
@results << {
tool: 'brakeman',
severity: 'ERROR',
message: "Failed to parse output: #{e.message}"
}
end
def run_custom_checks
checker = CustomSecurityChecker.new(@application_path)
@results += checker.check_secrets
@results += checker.check_configurations
@results += checker.check_permissions
end
def consolidate_results
ScanResult.create!(
application_path: @application_path,
scan_date: Time.current,
total_findings: @results.size,
critical_count: count_by_severity('CRITICAL'),
high_count: count_by_severity('HIGH'),
medium_count: count_by_severity('MEDIUM'),
low_count: count_by_severity('LOW'),
findings: @results.to_json,
status: determine_status
)
end
def count_by_severity(severity)
@results.count { |r| r[:severity] == severity }
end
def determine_status
return 'failed' if count_by_severity('CRITICAL') > 0
return 'warning' if count_by_severity('HIGH') > 0
'passed'
end
def determine_severity(criticality)
case criticality
when 'Critical' then 'CRITICAL'
when 'High' then 'HIGH'
when 'Medium' then 'MEDIUM'
else 'LOW'
end
end
end
This example shows implementing custom security checks for Rails configuration issues.
class CustomSecurityChecker
def initialize(application_path)
@application_path = application_path
end
def check_secrets
findings = []
# Check for exposed credentials
credentials_file = File.join(@application_path, 'config', 'credentials.yml.enc')
unless File.exist?(credentials_file)
findings << {
severity: 'HIGH',
category: 'configuration',
message: 'Credentials file missing - secrets may be stored insecurely',
remediation: 'Run rails credentials:edit to create encrypted credentials'
}
end
# Check for secrets in version control
secrets_file = File.join(@application_path, 'config', 'secrets.yml')
if File.exist?(secrets_file)
findings << {
severity: 'CRITICAL',
category: 'secrets',
file: secrets_file,
message: 'Secrets file should not exist - use encrypted credentials',
remediation: 'Migrate to Rails encrypted credentials'
}
end
findings
end
def check_configurations
findings = []
# Check production.rb settings
production_config = File.join(@application_path, 'config', 'environments', 'production.rb')
if File.exist?(production_config)
content = File.read(production_config)
unless content.include?('config.force_ssl')
findings << {
severity: 'HIGH',
category: 'configuration',
file: production_config,
message: 'SSL not enforced in production',
remediation: 'Add config.force_ssl = true to production.rb'
}
end
if content.include?('config.consider_all_requests_local = true')
findings << {
severity: 'MEDIUM',
category: 'configuration',
file: production_config,
message: 'Debug mode enabled in production',
remediation: 'Set config.consider_all_requests_local = false'
}
end
end
findings
end
def check_permissions
findings = []
# Check file permissions on sensitive files
sensitive_files = [
'config/database.yml',
'config/master.key',
'.env'
]
sensitive_files.each do |file_path|
full_path = File.join(@application_path, file_path)
next unless File.exist?(full_path)
stat = File.stat(full_path)
mode = stat.mode & 0777
if mode & 0044 != 0 # World or group readable
findings << {
severity: 'MEDIUM',
category: 'permissions',
file: full_path,
message: "File has overly permissive permissions: #{mode.to_s(8)}",
remediation: "Run chmod 600 #{file_path}"
}
end
end
findings
end
end
This example demonstrates tracking vulnerability remediation over time.
class VulnerabilityTracker
def track_remediation(application_id, scan_results)
current_findings = scan_results.map do |result|
fingerprint = generate_fingerprint(result)
{
fingerprint: fingerprint,
severity: result[:severity],
details: result
}
end
previous_scan = VulnerabilityScan.where(application_id: application_id)
.order(created_at: :desc)
.first
if previous_scan
comparison = compare_scans(previous_scan.findings, current_findings)
notify_changes(comparison)
end
VulnerabilityScan.create!(
application_id: application_id,
scan_date: Time.current,
findings: current_findings.to_json,
total_count: current_findings.size
)
end
private
def generate_fingerprint(finding)
data = [
finding[:category],
finding[:file],
finding[:message]
].compact.join(':')
Digest::SHA256.hexdigest(data)
end
def compare_scans(previous, current)
previous_fingerprints = Set.new(previous.map { |f| f['fingerprint'] })
current_fingerprints = Set.new(current.map { |f| f[:fingerprint] })
{
new: current_fingerprints - previous_fingerprints,
resolved: previous_fingerprints - current_fingerprints,
persisting: current_fingerprints & previous_fingerprints
}
end
def notify_changes(comparison)
if comparison[:new].any?
SecurityMailer.new_vulnerabilities(comparison[:new]).deliver_now
end
if comparison[:resolved].any?
SecurityMailer.resolved_vulnerabilities(comparison[:resolved]).deliver_now
end
end
end
Common Pitfalls
Teams commonly ignore low and medium severity vulnerabilities, focusing only on critical issues. Over time, these accumulate into significant technical debt. Medium severity vulnerabilities often become critical when combined with other weaknesses. Regular remediation of all severity levels prevents vulnerability accumulation.
False positive fatigue leads teams to disable security checks or ignore warnings. Initial configuration effort reduces false positives. Documenting legitimate suppressions with clear reasoning maintains team confidence in security tools. Regular review of suppressed findings ensures continued validity.
# Track and review suppressed vulnerabilities
class SuppressionReview
def review_expired_suppressions
expired = VulnerabilitySuppression.expired.includes(:vulnerability_finding)
expired.each do |suppression|
finding = suppression.vulnerability_finding
# Re-scan to check if vulnerability still exists
current_state = rescan_specific_finding(finding)
if current_state.vulnerable?
# Escalate - suppression expired but vulnerability remains
SecurityMailer.suppression_expired(suppression).deliver_now
suppression.update!(status: 'expired_vulnerable')
else
# Mark as resolved
finding.update!(status: 'resolved')
suppression.update!(status: 'expired_resolved')
end
end
end
private
def rescan_specific_finding(finding)
scanner = SecurityScanner.new
scanner.check_specific_vulnerability(
component: finding.component,
vulnerability_id: finding.vulnerability_id
)
end
end
Organizations deploy vulnerability scanners without fixing identified issues. Scanning alone provides no security benefit. Effective programs establish remediation timelines: critical vulnerabilities within 24 hours, high within 7 days, medium within 30 days, low within 90 days. Track remediation metrics to ensure timely fixes.
Development teams treat security scanning as a checklist item rather than continuous process. Security requires ongoing attention. New vulnerabilities appear daily. Regular scans and monitoring catch emerging threats.
Insufficient testing before deploying security patches breaks applications. Critical security updates sometimes introduce breaking changes or new bugs. Staging environments test patches before production deployment. Automated test suites catch regression issues.
# Automated security patch testing
class SecurityPatchDeployment
def deploy_patch(gem_name, target_version)
# Create temporary branch
branch_name = "security-patch-#{gem_name}-#{target_version}"
create_branch(branch_name)
# Update gem version
update_gemfile(gem_name, target_version)
# Run test suite
test_results = run_tests
if test_results.passed?
create_pull_request(
branch: branch_name,
title: "[Security] Update #{gem_name} to #{target_version}",
labels: ['security', 'dependencies']
)
else
notify_team_of_test_failures(gem_name, test_results)
rollback_branch(branch_name)
end
end
private
def run_tests
# Run full test suite
system("bundle exec rspec")
TestResult.new($?)
end
end
Teams skip vulnerability assessment for internal tools and administrative interfaces. Attackers target internal systems after gaining initial access. Internal applications require the same security scrutiny as public-facing systems.
Missing dependency scanning for transitive dependencies creates blind spots. Application directly requires gem A. Gem A requires vulnerable gem B. Scanners must examine the entire dependency tree. Bundler Audit checks all dependencies in Gemfile.lock, including transitive ones.
Organizations run vulnerability scans but fail to integrate results into existing workflows. Security findings should appear in project management systems, integrate with CI/CD pipelines, and trigger alerts in monitoring systems. Isolated security tools create information silos.
Treating all vulnerabilities equally ignores context. A denial-of-service vulnerability in an internal admin tool poses less risk than an injection vulnerability in a public API. Risk-based prioritization considers vulnerability severity, system exposure, and data sensitivity.
Reference
Vulnerability Severity Levels
| Severity | CVSS Score | Response Time | Description |
|---|---|---|---|
| Critical | 9.0-10.0 | 24 hours | Immediate exploitation possible, severe impact |
| High | 7.0-8.9 | 7 days | Exploitation likely, significant impact |
| Medium | 4.0-6.9 | 30 days | Exploitation possible, moderate impact |
| Low | 0.1-3.9 | 90 days | Exploitation difficult, minimal impact |
Common Vulnerability Types in Ruby Applications
| Type | Description | Detection Method |
|---|---|---|
| SQL Injection | Unsanitized input in database queries | Static analysis, Brakeman |
| XSS | Unescaped user input in views | Static analysis, Brakeman |
| CSRF | Missing anti-forgery tokens | Framework configuration audit |
| Mass Assignment | Unprotected model attributes | Strong parameters review |
| Deserialization | Unsafe YAML or Marshal loading | Code review, pattern matching |
| Command Injection | Unsanitized input to system calls | Static analysis |
| Path Traversal | Unsanitized file paths | Code review, static analysis |
| Insecure Dependencies | Vulnerable gem versions | Bundler Audit, Dependabot |
Essential Security Scanning Tools
| Tool | Purpose | Integration |
|---|---|---|
| Bundler Audit | Gem vulnerability scanning | CLI, CI/CD, pre-commit hooks |
| Brakeman | Rails static analysis | CLI, CI/CD, IDE plugins |
| RuboCop Security | Security linting rules | CLI, CI/CD, IDE plugins |
| Snyk | Dependency monitoring | GitHub, GitLab, CI/CD |
| Dependabot | Automated dependency updates | GitHub, GitLab |
| OWASP Dependency-Check | Multi-language dependency scanning | Jenkins, CI/CD |
Vulnerability Assessment Workflow
| Phase | Actions | Outputs |
|---|---|---|
| Discovery | Run scanners, inventory components | Vulnerability list |
| Analysis | Determine severity, assess exploitability | Risk assessment |
| Prioritization | Rank by risk, assign owners | Remediation plan |
| Remediation | Apply patches, update dependencies | Fixed systems |
| Verification | Re-scan, validate fixes | Confirmation report |
| Monitoring | Track new vulnerabilities | Alerts, notifications |
Common Brakeman Warning Types
| Warning Type | Risk | Remediation |
|---|---|---|
| SQL Injection | High | Use parameterized queries, prepared statements |
| Cross-Site Scripting | High | Escape output, use sanitize helpers |
| Command Injection | Critical | Avoid system calls, validate input |
| Mass Assignment | Medium | Use strong parameters |
| Unscoped Find | Medium | Add authorization checks |
| Redirect | Medium | Validate redirect targets |
| File Access | Medium | Validate file paths |
| Dangerous Send | High | Avoid dynamic method calls |
Security Configuration Checklist
| Configuration | Location | Setting |
|---|---|---|
| Force SSL | production.rb | config.force_ssl = true |
| Secure Cookies | production.rb | config.force_ssl = true (enables secure flag) |
| HSTS Header | production.rb | Enabled automatically with force_ssl |
| Content Security Policy | initializers/content_security_policy.rb | Configure policy directives |
| Secret Key Base | credentials.yml.enc | Encrypted, never in code |
| Database Credentials | credentials.yml.enc | Encrypted, never in code |
| API Keys | credentials.yml.enc | Encrypted, never in code |
Bundler Audit Commands
| Command | Purpose |
|---|---|
| bundler-audit check | Scan current dependencies |
| bundler-audit check --update | Update vulnerability database then scan |
| bundler-audit check --verbose | Show detailed scan information |
| bundler-audit stats | Show vulnerability database statistics |
Risk Calculation Formula
Risk Score = (CVSS Base Score × Exposure Factor × Exploitability Factor)
Exposure Factors:
- Public Internet: 1.5
- Authenticated Users: 1.2
- Internal Network: 0.8
- Localhost Only: 0.5
Exploitability Factors:
- Public Exploit Available: 1.5
- Proof of Concept Available: 1.2
- No Known Exploit: 0.8