class FilesController < ApplicationController
def show
# Vulnerable: Direct user input in file path
filename = params[:filename]
file_path = "#{Rails.root}/uploads/#{filename}"
# Dangerous: No path validation
if File.exist?(file_path)
send_file file_path
else
render plain: 'File not found', status: 404
end
end
def download
# Vulnerable: User controls file path
path = params[:path]
# Extremely dangerous: Can access any file
file_content = File.read(path)
render plain: file_content
end
def read_config
# Vulnerable: Config file access
config_name = params[:config]
config_path = "/etc/#{config_name}.conf"
# Can read sensitive system files
File.open(config_path, 'r') do |file|
render json: { config: file.read }
end
end
end
class FilesController < ApplicationController
ALLOWED_UPLOAD_DIR = Rails.root.join('uploads').freeze
ALLOWED_CONFIG_FILES = %w[app database].freeze
def validate_filename(filename)
return nil if filename.blank?
# Remove any path traversal attempts
clean_filename = File.basename(filename)
# Validate filename format
unless clean_filename.match?(/\A[a-zA-Z0-9._-]+\z/)
raise ArgumentError, 'Invalid filename format'
end
# Check length
if clean_filename.length > 100
raise ArgumentError, 'Filename too long'
end
clean_filename
end
def validate_file_path(filename, base_dir = ALLOWED_UPLOAD_DIR)
clean_filename = validate_filename(filename)
file_path = base_dir.join(clean_filename)
# Ensure path is within allowed directory
unless file_path.to_s.start_with?(base_dir.to_s)
raise ArgumentError, 'Path traversal detected'
end
file_path
end
def show
begin
filename = params[:filename]
# Secure: Validate and construct safe path
file_path = validate_file_path(filename)
unless File.exist?(file_path)
return render json: { error: 'File not found' }, status: 404
end
# Validate file type
allowed_types = %w[.txt .pdf .jpg .png .doc .docx]
file_ext = File.extname(file_path).downcase
unless allowed_types.include?(file_ext)
return render json: { error: 'File type not allowed' }, status: 403
end
# Secure file serving
send_file file_path, disposition: 'attachment'
rescue ArgumentError => e
render json: { error: e.message }, status: 400
rescue => e
Rails.logger.error "File access error: #{e.message}"
render json: { error: 'File access failed' }, status: 500
end
end
def download
# Secure: Don't allow arbitrary path access
render json: { error: 'Direct path access not allowed' }, status: 403
end
def read_config
begin
config_name = params[:config]
# Secure: Allowlist validation
unless ALLOWED_CONFIG_FILES.include?(config_name)
return render json: { error: 'Config file not allowed' }, status: 403
end
# Secure path construction
config_path = Rails.root.join('config', "#{config_name}.yml")
# Ensure path is safe
unless config_path.to_s.start_with?(Rails.root.join('config').to_s)
return render json: { error: 'Invalid config path' }, status: 400
end
unless File.exist?(config_path)
return render json: { error: 'Config file not found' }, status: 404
end
# Read and parse safely
config_content = YAML.safe_load(File.read(config_path))
# Filter sensitive information
filtered_config = filter_sensitive_config(config_content)
render json: { config: filtered_config }
rescue ArgumentError => e
render json: { error: e.message }, status: 400
rescue => e
Rails.logger.error "Config access error: #{e.message}"
render json: { error: 'Config access failed' }, status: 500
end
end
private
def filter_sensitive_config(config)
# Remove sensitive keys
sensitive_keys = %w[password secret_key database_password api_key]
config.reject { |key, _| sensitive_keys.any? { |sk| key.to_s.include?(sk) } }
end
end