lib/contrast/agent/protect/rule/sqli.rb in contrast-agent-4.9.1 vs lib/contrast/agent/protect/rule/sqli.rb in contrast-agent-4.10.0
- old
+ new
@@ -1,17 +1,24 @@
# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
# frozen_string_literal: true
require 'contrast/agent/protect/rule/base_service'
require 'contrast/agent/protect/policy/applies_sqli_rule'
+require 'contrast/agent/protect/rule/sql_sample_builder'
module Contrast
module Agent
module Protect
module Rule
# The Ruby implementation of the Protect SQL Injection rule.
class Sqli < Contrast::Agent::Protect::Rule::BaseService
+ # Generate a sample for the SQLI injection detection rule, allowing for reporting to and rendering
+ # by TeamServer
+ include SqlSampleBuilder::SqliSample
+ # Defining build_attack_with_match method
+ include SqlSampleBuilder::AttackBuilder
+
NAME = 'sql-injection'
BLOCK_MESSAGE = 'SQLi rule triggered. Response blocked.'
def rule_name
NAME
@@ -28,79 +35,9 @@
return unless result
append_to_activity(context, result)
raise Contrast::SecurityException.new(self, BLOCK_MESSAGE) if blocked?
- end
-
- def build_attack_with_match context, input_analysis_result, result, query_string, **kwargs
- attack_string = input_analysis_result.value
- regexp = Regexp.new(Regexp.escape(attack_string), Regexp::IGNORECASE)
-
- return unless query_string.match?(regexp)
-
- database = kwargs[:database]
- scanner = select_scanner(database)
-
- ss = StringScanner.new(query_string)
- length = attack_string.length
- while ss.scan_until(regexp)
- # the pos of StringScanner is at the end of the regexp (input string),
- # we need the beginning
- idx = ss.pos - attack_string.length
- last_boundary, boundary = scanner.crosses_boundary(query_string, idx, input_analysis_result.value)
- next unless last_boundary && boundary
-
- result ||= build_attack_result(context)
- record_match(idx, length, boundary, last_boundary, kwargs)
- append_match(context, input_analysis_result, result, query_string, **kwargs)
- end
-
- result
- end
-
- protected
-
- def build_sample context, input_analysis_result, candidate_string, **kwargs
- input = input_analysis_result.value
-
- sample = build_base_sample(context, input_analysis_result)
- sample.sqli = Contrast::Api::Dtm::SqlInjectionDetails.new
- sample.sqli.query = Contrast::Utils::StringUtils.protobuf_safe_string(candidate_string)
- sample.sqli.start_idx = sample.sqli.query.index(input).to_i
- sample.sqli.end_idx = sample.sqli.start_idx + input.length
- sample.sqli.boundary_overrun_idx = kwargs[:boundary_overrun_idx].to_i
- sample.sqli.input_boundary_idx = kwargs[:input_boundary_idx].to_i
- sample
- end
-
- private
-
- def record_match idx, length, boundary, last_boundary, kwargs
- kwargs[:start_idx] = idx
- kwargs[:end_idx] = idx + length
- kwargs[:boundary_overrun_idx] = boundary
- kwargs[:input_boundary_idx] = last_boundary
- end
-
- def append_match context, input_analysis_result, result, query_string, **kwargs
- input_analysis_result.attack_count = input_analysis_result.attack_count + 1
- update_successful_attack_response(context, input_analysis_result, result, query_string)
- append_sample(context, input_analysis_result, result, query_string, **kwargs)
- end
-
- def select_scanner database
- @sql_scanners ||= {
- Contrast::Agent::Protect::Policy::AppliesSqliRule::DATABASE_MYSQL =>
- Contrast::Agent::Protect::Rule::Sqli::MysqlSqlScanner.new,
- Contrast::Agent::Protect::Policy::AppliesSqliRule::DATABASE_PG =>
- Contrast::Agent::Protect::Rule::Sqli::PostgresSqlScanner.new,
- Contrast::Agent::Protect::Policy::AppliesSqliRule::DATABASE_SQLITE =>
- Contrast::Agent::Protect::Rule::Sqli::SqliteSqlScanner.new
- }.cs__freeze
-
- @default_sql_scanner ||= Contrast::Agent::Protect::Rule::Sqli::DefaultSqlScanner.new
- @sql_scanners[database.to_s] || @default_sql_scanner
end
end
end
end
end