feat: validate query conditions (#10595)
Query conditions can take in arbitrary values, this can cause SQL errors. This PR fixes it
This commit is contained in:
@@ -68,6 +68,7 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
|
||||
@contacts = fetch_contacts(contacts)
|
||||
rescue CustomExceptions::CustomFilter::InvalidAttribute,
|
||||
CustomExceptions::CustomFilter::InvalidOperator,
|
||||
CustomExceptions::CustomFilter::InvalidQueryOperator,
|
||||
CustomExceptions::CustomFilter::InvalidValue => e
|
||||
render_could_not_create_error(e.message)
|
||||
end
|
||||
|
||||
@@ -46,6 +46,7 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
|
||||
@conversations_count = result[:count]
|
||||
rescue CustomExceptions::CustomFilter::InvalidAttribute,
|
||||
CustomExceptions::CustomFilter::InvalidOperator,
|
||||
CustomExceptions::CustomFilter::InvalidQueryOperator,
|
||||
CustomExceptions::CustomFilter::InvalidValue => e
|
||||
render_could_not_create_error(e.message)
|
||||
end
|
||||
|
||||
@@ -81,4 +81,12 @@ module FilterHelper
|
||||
def default_filter(query_hash, filter_operator_value)
|
||||
"#{filter_config[:table_name]}.#{query_hash[:attribute_key]} #{filter_operator_value} #{query_hash[:query_operator]}"
|
||||
end
|
||||
|
||||
def validate_single_condition(condition)
|
||||
return if condition['query_operator'].nil?
|
||||
return if condition['query_operator'].empty?
|
||||
|
||||
operator = condition['query_operator'].upcase
|
||||
raise CustomExceptions::CustomFilter::InvalidQueryOperator.new({}) unless %w[AND OR].include?(operator)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -27,6 +27,7 @@ class AutomationRule < ApplicationRecord
|
||||
validate :json_conditions_format
|
||||
validate :json_actions_format
|
||||
validate :query_operator_presence
|
||||
validate :query_operator_value
|
||||
validates :account_id, presence: true
|
||||
|
||||
after_update_commit :reauthorized!, if: -> { saved_change_to_conditions? }
|
||||
@@ -83,6 +84,24 @@ class AutomationRule < ApplicationRecord
|
||||
operators = conditions.select { |obj, _| obj['query_operator'].nil? }
|
||||
errors.add(:conditions, 'Automation conditions should have query operator.') if operators.length > 1
|
||||
end
|
||||
|
||||
# This validation ensures logical operators are being used correctly in automation conditions.
|
||||
# And we don't push any unsanitized query operators to the database.
|
||||
def query_operator_value
|
||||
conditions.each do |obj|
|
||||
validate_single_condition(obj)
|
||||
end
|
||||
end
|
||||
|
||||
def validate_single_condition(condition)
|
||||
query_operator = condition['query_operator']
|
||||
|
||||
return if query_operator.nil?
|
||||
return if query_operator.empty?
|
||||
|
||||
operator = query_operator.upcase
|
||||
errors.add(:conditions, 'Query operator must be either "AND" or "OR"') unless %w[AND OR].include?(operator)
|
||||
end
|
||||
end
|
||||
|
||||
AutomationRule.include_mod_with('Audit::AutomationRule')
|
||||
|
||||
@@ -15,7 +15,7 @@ class AutomationRules::ConditionValidationService
|
||||
|
||||
def perform
|
||||
@rule.conditions.each do |condition|
|
||||
return false unless valid_condition?(condition)
|
||||
return false unless valid_condition?(condition) && valid_query_operator?(condition)
|
||||
end
|
||||
|
||||
true
|
||||
@@ -23,6 +23,15 @@ class AutomationRules::ConditionValidationService
|
||||
|
||||
private
|
||||
|
||||
def valid_query_operator?(condition)
|
||||
query_operator = condition['query_operator']
|
||||
|
||||
return true if query_operator.nil?
|
||||
return true if query_operator.empty?
|
||||
|
||||
%w[AND OR].include?(query_operator.upcase)
|
||||
end
|
||||
|
||||
def valid_condition?(condition)
|
||||
key = condition['attribute_key']
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ class Contacts::FilterService < FilterService
|
||||
end
|
||||
|
||||
def perform
|
||||
validate_query_operator
|
||||
@contacts = query_builder(@filters['contacts'])
|
||||
|
||||
{
|
||||
|
||||
@@ -7,6 +7,7 @@ class Conversations::FilterService < FilterService
|
||||
end
|
||||
|
||||
def perform
|
||||
validate_query_operator
|
||||
@conversations = query_builder(@filters['conversations'])
|
||||
mine_count, unassigned_count, all_count, = set_count_for_all_conversations
|
||||
assigned_count = all_count - unassigned_count
|
||||
|
||||
@@ -204,4 +204,10 @@ class FilterService
|
||||
end
|
||||
base_relation.where(@query_string, @filter_values.with_indifferent_access)
|
||||
end
|
||||
|
||||
def validate_query_operator
|
||||
@params[:payload].each do |query_hash|
|
||||
validate_single_condition(query_hash)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user