DEV: Quote values when constructing SQL (#18827)

All of these cases should already be safe, but still good to quote for
"defense in depth".
This commit is contained in:
Daniel Waterworth 2022-11-01 14:05:13 -05:00 committed by GitHub
parent a356e2fe30
commit 167181f4b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 39 additions and 37 deletions

View File

@ -65,7 +65,7 @@ class GroupsController < ApplicationController
if !guardian.is_staff?
# hide automatic groups from all non stuff to de-clutter page
groups = groups.where("automatic IS FALSE OR groups.id = #{Group::AUTO_GROUPS[:moderators]}")
groups = groups.where("automatic IS FALSE OR groups.id = ?", Group::AUTO_GROUPS[:moderators])
type_filters.delete(:automatic)
end
@ -129,7 +129,7 @@ class GroupsController < ApplicationController
format.json do
groups = Group.visible_groups(current_user)
if !guardian.is_staff?
groups = groups.where("automatic IS FALSE OR groups.id = #{Group::AUTO_GROUPS[:moderators]}")
groups = groups.where("automatic IS FALSE OR groups.id = ?", Group::AUTO_GROUPS[:moderators])
end
render_json_dump(

View File

@ -29,7 +29,7 @@ module Jobs
Emoji.clear_cache
Post.where("cooked LIKE '%#{Emoji.base_url}%'").find_each do |post|
Post.where("cooked LIKE ?", "%#{Emoji.base_url}%").find_each do |post|
post.rebake!
end
end

View File

@ -46,8 +46,9 @@ module Jobs
# UserHistory for delete_user logs the user's IP. Note this is quite ugly but we don't
# have a better way of querying on details right now.
UserHistory.where(
"action = :action AND details LIKE 'id: #{@user_id}\n%'",
action: UserHistory.actions[:delete_user]
"action = :action AND details LIKE :details",
action: UserHistory.actions[:delete_user],
details: "id: #{@user_id}\n%",
).update_all(ip_address: new_ip)
end

View File

@ -41,9 +41,9 @@ module Jobs
if upload.sha1.present?
# TODO: Remove this check after UploadReferences records were created
encoded_sha = Base62.encode(upload.sha1.hex)
next if ReviewableQueuedPost.pending.where("payload->>'raw' LIKE '%#{upload.sha1}%' OR payload->>'raw' LIKE '%#{encoded_sha}%'").exists?
next if Draft.where("data LIKE '%#{upload.sha1}%' OR data LIKE '%#{encoded_sha}%'").exists?
next if UserProfile.where("bio_raw LIKE '%#{upload.sha1}%' OR bio_raw LIKE '%#{encoded_sha}%'").exists?
next if ReviewableQueuedPost.pending.where("payload->>'raw' LIKE ? OR payload->>'raw' LIKE ?", "%#{upload.sha1}%", "%#{encoded_sha}%").exists?
next if Draft.where("data LIKE ? OR data LIKE ?", "%#{upload.sha1}%", "%#{encoded_sha}%").exists?
next if UserProfile.where("bio_raw LIKE ? OR bio_raw LIKE ?", "%#{upload.sha1}%", "%#{encoded_sha}%").exists?
upload.destroy
else

View File

@ -23,12 +23,12 @@ module Jobs
.where(staged: false)
.joins(:user_option, :user_stat, :user_emails)
.where("user_options.email_digests")
.where("user_stats.bounce_score < #{SiteSetting.bounce_score_threshold}")
.where("user_stats.bounce_score < ?", SiteSetting.bounce_score_threshold)
.where("user_emails.primary")
.where("COALESCE(last_emailed_at, '2010-01-01') <= CURRENT_TIMESTAMP - ('1 MINUTE'::INTERVAL * user_options.digest_after_minutes)")
.where("COALESCE(user_stats.digest_attempted_at, '2010-01-01') <= CURRENT_TIMESTAMP - ('1 MINUTE'::INTERVAL * user_options.digest_after_minutes)")
.where("COALESCE(last_seen_at, '2010-01-01') <= CURRENT_TIMESTAMP - ('1 MINUTE'::INTERVAL * user_options.digest_after_minutes)")
.where("COALESCE(last_seen_at, '2010-01-01') >= CURRENT_TIMESTAMP - ('1 DAY'::INTERVAL * #{SiteSetting.suppress_digest_email_after_days})")
.where("COALESCE(last_seen_at, '2010-01-01') >= CURRENT_TIMESTAMP - ('1 DAY'::INTERVAL * ?)", SiteSetting.suppress_digest_email_after_days)
.order("user_stats.digest_attempted_at ASC NULLS FIRST")
# If the site requires approval, make sure the user is approved

View File

@ -480,7 +480,7 @@ class Upload < ActiveRecord::Base
db = RailsMultisite::ConnectionManagement.current_db
scope = Upload.by_users
.where("url NOT LIKE '%/original/_X/%' AND url LIKE '%/uploads/#{db}%'")
.where("url NOT LIKE '%/original/_X/%' AND url LIKE ?", "%/uploads/#{db}%")
.order(id: :desc)
scope = scope.limit(limit) if limit

View File

@ -446,10 +446,10 @@ class Search
def post_action_type_filter(posts, post_action_type)
posts.where("posts.id IN (
SELECT pa.post_id FROM post_actions pa
WHERE pa.user_id = #{@guardian.user.id} AND
pa.post_action_type_id = #{post_action_type} AND
WHERE pa.user_id = ? AND
pa.post_action_type_id = ? AND
deleted_at IS NULL
)")
)", @guardian.user.id, post_action_type)
end
advanced_filter(/^in:(likes)$/i) do |posts, match|
@ -464,17 +464,17 @@ class Search
# search based on a RegisteredBookmarkable's #search_query method.
advanced_filter(/^in:(bookmarks)$/i) do |posts, match|
if @guardian.user
posts.where(<<~SQL)
posts.where(<<~SQL, @guardian.user.id)
posts.id IN (
SELECT bookmarkable_id FROM bookmarks
WHERE bookmarks.user_id = #{@guardian.user.id} AND bookmarks.bookmarkable_type = 'Post'
WHERE bookmarks.user_id = ? AND bookmarks.bookmarkable_type = 'Post'
)
SQL
end
end
advanced_filter(/^in:posted$/i) do |posts|
posts.where("posts.user_id = #{@guardian.user.id}") if @guardian.user
posts.where("posts.user_id = ?", @guardian.user.id) if @guardian.user
end
advanced_filter(/^in:(created|mine)$/i) do |posts|
@ -640,7 +640,7 @@ class Search
advanced_filter(/^user:(.+)$/i) do |posts, match|
user_id = User.where(staged: false).where('username_lower = ? OR id = ?', match.downcase, match.to_i).pluck_first(:id)
if user_id
posts.where("posts.user_id = #{user_id}")
posts.where("posts.user_id = ?", user_id)
else
posts.where("1 = 0")
end
@ -656,7 +656,7 @@ class Search
end
if user_id
posts.where("posts.user_id = #{user_id}")
posts.where("posts.user_id = ?", user_id)
else
posts.where("1 = 0")
end
@ -1043,13 +1043,13 @@ class Search
posts.where("topics.category_id in (?)", category_ids)
elsif is_topic_search
posts.where("topics.id = #{@search_context.id}")
posts.where("topics.id = ?", @search_context.id)
.order("posts.post_number #{@order == :latest ? "DESC" : ""}")
elsif @search_context.is_a?(Tag)
posts = posts
.joins("LEFT JOIN topic_tags ON topic_tags.topic_id = topics.id")
.joins("LEFT JOIN tags ON tags.id = topic_tags.tag_id")
posts.where("tags.id = #{@search_context.id}")
posts.where("tags.id = ?", @search_context.id)
end
else
posts = categories_ignored(posts) unless @category_filter_matched
@ -1243,8 +1243,8 @@ class Search
end
if min_id > 0
low_set = query.dup.where("post_search_data.post_id < #{min_id}")
high_set = query.where("post_search_data.post_id >= #{min_id}")
low_set = query.dup.where("post_search_data.post_id < ?", min_id)
high_set = query.where("post_search_data.post_id >= ?", min_id)
return { default: wrap_rows(high_set), remaining: wrap_rows(low_set) }
end

View File

@ -506,7 +506,7 @@ def recover_uploads_from_index(path)
db = RailsMultisite::ConnectionManagement.current_db
cdn_path = SiteSetting.cdn_path("/uploads/#{db}").sub(/https?:/, "")
Post.where("cooked LIKE '%#{cdn_path}%'").each do |post|
Post.where("cooked LIKE ?", "%#{cdn_path}%").each do |post|
regex = Regexp.new("((https?:)?#{Regexp.escape(cdn_path)}[^,;\\]\\>\\t\\n\\s)\"\']+)")
uploads = []
post.raw.scan(regex).each do |match|
@ -663,9 +663,10 @@ def correct_inline_uploads
verbose = ENV["VERBOSE"]
scope = Post.joins(:upload_references).distinct("posts.id")
.where(<<~SQL)
raw LIKE '%/uploads/#{RailsMultisite::ConnectionManagement.current_db}/original/%'
SQL
.where(
"raw LIKE ?",
"%/uploads/#{RailsMultisite::ConnectionManagement.current_db}/original/%",
)
affected_posts_count = scope.count
fixed_count = 0

View File

@ -31,7 +31,7 @@ def gather_uploads
puts "", "Gathering uploads for '#{current_db}'...", ""
Upload.where("url ~ '^\/uploads\/'")
.where("url !~ '^\/uploads\/#{current_db}'")
.where("url !~ ?", "^\/uploads\/#{current_db}")
.find_each do |upload|
begin
old_db = upload.url[/^\/uploads\/([^\/]+)\//, 1]

View File

@ -311,9 +311,9 @@ class TopicQuery
def list_group_topics(group)
list = default_results.where("
topics.user_id IN (
SELECT user_id FROM group_users gu WHERE gu.group_id = #{group.id.to_i}
SELECT user_id FROM group_users gu WHERE gu.group_id = ?
)
")
", group.id.to_i)
create_list(:group_topics, {}, list)
end

View File

@ -118,20 +118,20 @@ class TopicQuery
result = result.joins("INNER JOIN group_users gu ON gu.group_id = tag.group_id AND gu.user_id = #{user.id.to_i}")
end
elsif type == :user
result = result.where("topics.id IN (SELECT topic_id FROM topic_allowed_users WHERE user_id = #{user.id.to_i})")
result = result.where("topics.id IN (SELECT topic_id FROM topic_allowed_users WHERE user_id = ?)", user.id.to_i)
elsif type == :all
group_ids = group_with_messages_ids(user)
result =
if group_ids.present?
result.where(<<~SQL)
result.where(<<~SQL, user.id.to_i, group_ids)
topics.id IN (
SELECT topic_id
FROM topic_allowed_users
WHERE user_id = #{user.id.to_i}
WHERE user_id = ?
UNION ALL
SELECT topic_id FROM topic_allowed_groups
WHERE group_id IN (#{group_ids.join(",")})
WHERE group_id IN (?)
)
SQL
else
@ -259,10 +259,10 @@ class TopicQuery
end
def have_posts_from_others(list, user)
list.where(<<~SQL)
list.where(<<~SQL, user.id.to_i)
NOT (
topics.participant_count = 1
AND topics.user_id = #{user.id.to_i}
AND topics.user_id = ?
AND topics.moderator_posts_count = 0
)
SQL

View File

@ -58,7 +58,7 @@ task "poll:migrate_old_polls" => :environment do
options = post.custom_fields["polls"]["poll"]["options"]
# iterate over all votes
PluginStoreRow.where(plugin_name: "poll")
.where("key LIKE 'poll_vote_#{post_id}_%'")
.where("key LIKE ?", "poll_vote_#{post_id}_%")
.pluck(:key, :value)
.each do |poll_vote_key, vote|
# extract the user_id