class UsersController < ApplicationController
def search
# Vulnerable: String interpolation in where clause
query = params[:query]
@users = User.where("name LIKE '%#{query}%'")
render json: @users
end
def find_by_id
# Vulnerable: Direct interpolation in SQL
user_id = params[:id]
@user = User.find_by_sql("SELECT * FROM users WHERE id = #{user_id}")
render json: @user
end
def update_status
# Vulnerable: Dynamic SQL with user input
user_id = params[:user_id]
status = params[:status]
sql = "UPDATE users SET status = '#{status}' WHERE id = #{user_id}"
ActiveRecord::Base.connection.execute(sql)
render json: { message: 'Status updated' }
end
def advanced_search
# Vulnerable: Complex query building
conditions = []
conditions << "name LIKE '%#{params[:name]}%'" if params[:name]
conditions << "email LIKE '%#{params[:email]}%'" if params[:email]
where_clause = conditions.join(' AND ')
@users = User.where(where_clause)
render json: @users
end
end
class UsersController < ApplicationController
before_action :validate_search_params, only: [:search, :advanced_search]
def validate_search_params
if params[:query] && params[:query].length > 100
render json: { error: 'Query too long' }, status: 400
return
end
if params[:name] && params[:name].length > 50
render json: { error: 'Name query too long' }, status: 400
return
end
if params[:email] && !params[:email].match?(/\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i)
render json: { error: 'Invalid email format' }, status: 400
return
end
end
def search
# Secure: Use parameterized query
query = params[:query]
if query.present?
# Safe: ActiveRecord handles parameterization
@users = User.where('name ILIKE ?', "%#{query}%")
else
@users = User.none
end
render json: @users
end
def find_by_id
# Secure: Use ActiveRecord find methods
user_id = params[:id]
begin
# Validate ID is numeric
id = Integer(user_id)
@user = User.find(id)
render json: @user
rescue ArgumentError
render json: { error: 'Invalid user ID format' }, status: 400
rescue ActiveRecord::RecordNotFound
render json: { error: 'User not found' }, status: 404
end
end
def update_status
# Secure: Use ActiveRecord update methods
user_id = params[:user_id]
status = params[:status]
# Validate status
allowed_statuses = %w[active inactive pending suspended]
unless allowed_statuses.include?(status)
return render json: { error: 'Invalid status' }, status: 400
end
begin
id = Integer(user_id)
user = User.find(id)
# Safe: ActiveRecord handles parameterization
user.update!(status: status)
render json: { message: 'Status updated', user: user }
rescue ArgumentError
render json: { error: 'Invalid user ID format' }, status: 400
rescue ActiveRecord::RecordNotFound
render json: { error: 'User not found' }, status: 404
rescue ActiveRecord::RecordInvalid => e
render json: { error: e.message }, status: 422
end
end
def advanced_search
# Secure: Build conditions safely
conditions = {}
if params[:name].present?
conditions[:name] = params[:name]
end
if params[:email].present?
conditions[:email] = params[:email]
end
if params[:status].present?
allowed_statuses = %w[active inactive pending suspended]
if allowed_statuses.include?(params[:status])
conditions[:status] = params[:status]
end
end
# Safe: Use hash conditions
@users = User.where(conditions)
# Alternative: Use scopes for complex queries
@users = @users.active if params[:active_only] == 'true'
@users = @users.recent if params[:recent_only] == 'true'
render json: @users
end
# Secure alternative using scopes
def scoped_search
@users = User.all
# Use safe scopes instead of raw SQL
@users = @users.with_name(params[:name]) if params[:name].present?
@users = @users.with_email_domain(params[:domain]) if params[:domain].present?
@users = @users.created_after(params[:after]) if params[:after].present?
render json: @users
end
end
# Secure model with scopes
class User < ApplicationRecord
# Safe scopes that handle parameterization
scope :with_name, ->(name) { where('name ILIKE ?', "%#{sanitize_sql_like(name)}%") }
scope :with_email_domain, ->(domain) { where('email ILIKE ?', "%@#{sanitize_sql_like(domain)}") }
scope :created_after, ->(date) { where('created_at > ?', Date.parse(date)) }
scope :active, -> { where(status: 'active') }
scope :recent, -> { where('created_at > ?', 30.days.ago) }
validates :status, inclusion: { in: %w[active inactive pending suspended] }
validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }
end