Module: TingYun::Agent::Database::Obfuscator::ObfuscationHelpers

Included in:
TingYun::Agent::Database::Obfuscator
Defined in:
lib/ting_yun/agent/database/obfuscator.rb

Constant Summary collapse

COMPONENTS_REGEX_MAP =
{
    :single_quotes => /'(?:[^']|'')*?(?:\\'.*|'(?!'))/,
    :double_quotes => /"(?:[^"]|"")*?(?:\\".*|"(?!"))/,
    :dollar_quotes => /(\$(?!\d)[^$]*?\$).*?(?:\1|$)/,
    :uuids => /\{?(?:[0-9a-fA-F]\-*){32}\}?/,
    :numeric_literals => /\b-?(?:[0-9]+\.)?[0-9]+([eE][+-]?[0-9]+)?\b/,
    :boolean_literals => /\b(?:true|false|null)\b/i,
    :hexadecimal_literals => /0x[0-9a-fA-F]+/,
    :comments => /(?:#|--).*?(?=\r|\n|$)/i,
    :multi_line_comments => /\/\*(?:[^\/]|\/[^*])*?(?:\*\/|\/\*.*)/,
    :oracle_quoted_strings => /q'\[.*?(?:\]'|$)|q'\{.*?(?:\}'|$)|q'\<.*?(?:\>'|$)|q'\(.*?(?:\)'|$)/
}
DIALECT_COMPONENTS =
{
    :fallback   => COMPONENTS_REGEX_MAP.keys,
    :mysql      => [:single_quotes, :double_quotes, :numeric_literals, :boolean_literals,
                    :hexadecimal_literals, :comments, :multi_line_comments],
    :postgres   => [:single_quotes, :dollar_quotes, :uuids, :numeric_literals,
                    :boolean_literals, :comments, :multi_line_comments],
    :sqlite     => [:single_quotes, :numeric_literals, :boolean_literals, :hexadecimal_literals,
                    :comments, :multi_line_comments],
    :oracle     => [:single_quotes, :oracle_quoted_strings, :numeric_literals, :comments,
                    :multi_line_comments],
    :cassandra  => [:single_quotes, :uuids, :numeric_literals, :boolean_literals,
                    :hexadecimal_literals, :comments, :multi_line_comments]
}
CLEANUP_REGEX =

We use these to check whether the query contains any quote characters after obfuscation. If so, that’s a good indication that the original query was malformed, and so our obfuscation can’t reliably find literals. In such a case, we’ll replace the entire query with a placeholder.

{
    :mysql => /'|"|\/\*|\*\//,
    :mysql2 => /'|"|\/\*|\*\//,
    :postgres => /'|\/\*|\*\/|\$(?!\?)/,
    :sqlite => /'|\/\*|\*\//,
    :cassandra => /'|\/\*|\*\//,
    :oracle => /'|\/\*|\*\//,
    :oracle_enhanced => /'|\/\*|\*\//
}
QUOTED_STRINGS_REGEX =
/'(?:[^']|'')*'|"(?:[^"]|"")*"/
LABEL_LINE_REGEX =
/^([^:\n]*:\s+).*$/.freeze
PLACEHOLDER =
'?'.freeze
FAILED_TO_OBFUSCATE_MESSAGE =
"Failed to obfuscate SQL query - quote characters remained after obfuscation".freeze
MYSQL_COMPONENTS_REGEX =
self.generate_regex(:mysql)
POSTGRES_COMPONENTS_REGEX =
self.generate_regex(:postgres)
SQLITE_COMPONENTS_REGEX =
self.generate_regex(:sqlite)
ORACLE_COMPONENTS_REGEX =
self.generate_regex(:oracle)
CASSANDRA_COMPONENTS_REGEX =
self.generate_regex(:cassandra)
FALLBACK_REGEX =
self.generate_regex(:fallback)

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.generate_regex(dialect) ⇒ Object



107
108
109
110
# File 'lib/ting_yun/agent/database/obfuscator.rb', line 107

def self.generate_regex(dialect)
  components = DIALECT_COMPONENTS[dialect]
  Regexp.union(components.map{|component| COMPONENTS_REGEX_MAP[component]})
end

Instance Method Details

#detect_unmatched_pairs(obfuscated, adapter) ⇒ Object



139
140
141
142
143
144
145
# File 'lib/ting_yun/agent/database/obfuscator.rb', line 139

def detect_unmatched_pairs(obfuscated, adapter)
  if CLEANUP_REGEX[adapter]
    CLEANUP_REGEX[adapter].match(obfuscated)
  else
    CLEANUP_REGEX[:mysql].match(obfuscated)
  end
end

#obfuscate(sql, adapter) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/ting_yun/agent/database/obfuscator.rb', line 119

def obfuscate(sql, adapter)
  case adapter
    when :mysql, :mysql2
      regex = MYSQL_COMPONENTS_REGEX
    when :postgres
      regex = POSTGRES_COMPONENTS_REGEX
    when :sqlite
      regex = SQLITE_COMPONENTS_REGEX
    when :oracle, :oracle_enhanced
      regex = ORACLE_COMPONENTS_REGEX
    when :cassandra
      regex = CASSANDRA_COMPONENTS_REGEX
    else
      regex = FALLBACK_REGEX
  end
  obfuscated = sql.gsub(regex, PLACEHOLDER)
  obfuscated = FAILED_TO_OBFUSCATE_MESSAGE if detect_unmatched_pairs(obfuscated, adapter)
  obfuscated
end

#obfuscate_postgres_explain(sql) ⇒ Object



87
88
89
90
91
92
93
94
# File 'lib/ting_yun/agent/database/obfuscator.rb', line 87

def obfuscate_postgres_explain(sql)
  sql.gsub!(QUOTED_STRINGS_REGEX) do |match|
    match.start_with?('"') ? match : '?'
  end

  sql.gsub!(LABEL_LINE_REGEX,   '\1?')
  sql
end

#obfuscate_single_quote_literals(sql) ⇒ Object



102
103
104
105
# File 'lib/ting_yun/agent/database/obfuscator.rb', line 102

def obfuscate_single_quote_literals(sql)
  return sql unless sql =~ COMPONENTS_REGEX_MAP[:single_quotes]
  sql.gsub(COMPONENTS_REGEX_MAP[:single_quotes], PLACEHOLDER)
end