Class: Honeybadger::Util::SQL Private

Inherits:
Object
  • Object
show all
Defined in:
lib/honeybadger/util/sql.rb

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

Constant Summary collapse

ESCAPE_QUOTES =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

/(\\"|\\')/
SQUOTE_DATA =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

/'(?:[^']|'')*'/
DQUOTE_DATA =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

/"(?:[^"]|"")*"/
NUMBER_DATA =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Underscore separators are allowed in numeric literals by PostgreSQL 16+ and SQLite 3.46+, and \b does not match next to one.

/\b\d+(?:_\d+)*\b/
HEX_DATA =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

/\b0[xbo][0-9a-f]+(?:_[0-9a-f]+)*\b/i
DOUBLE_QUOTERS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

/(postgres|sqlite|postgis)/i
TRUNCATED_HEAD_LENGTH =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

200
TRUNCATED_HEAD_SAFE =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Leading run of characters that can't start a string literal or a comment in any supported adapter: stops at ', $, /, -, #, \ and so on. Double quotes and backticks are kept because they quote identifiers; adapters that use double quotes for strings are cut at the first double quote in .truncate.

/\A[\w\s.,()=*"`]*/

Class Method Summary collapse

Class Method Details

.force_utf_8(string) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



49
50
51
52
53
54
55
56
# File 'lib/honeybadger/util/sql.rb', line 49

def self.force_utf_8(string)
  string.encode(
    Encoding.find("UTF-8"),
    invalid: :replace,
    undef: :replace,
    replace: ""
  )
end

.obfuscate(sql, adapter, max_length: nil) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Obfuscates literal values in a SQL query. When max_length is given and the query is larger than that many bytes, the query is truncated instead (see .truncate): scanning multi-megabyte queries (e.g. an INSERT of a serialized cache value) is slow, and can exceed the Regexp.timeout that Rails 8.1+ sets by default.



25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/honeybadger/util/sql.rb', line 25

def self.obfuscate(sql, adapter, max_length: nil)
  sql = sql.to_s
  return truncate(sql, adapter, max_length) if max_length && sql.bytesize > max_length

  force_utf_8(sql.dup).tap do |s|
    s.gsub!(/\s+/, " ")
    s.gsub!(ESCAPE_QUOTES, "".freeze)
    s.gsub!(SQUOTE_DATA, "'?'".freeze)
    s.gsub!(DQUOTE_DATA, '"?"'.freeze) unless adapter.to_s.match?(DOUBLE_QUOTERS)
    s.gsub!(HEX_DATA, "?".freeze)
    s.gsub!(NUMBER_DATA, "?".freeze)
    s.strip!
  end
end

.truncate(sql, adapter, max_length) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Keeps only the head of the statement (up to the first quoted value or comment) so that no literal data leaks, and notes the original size.



42
43
44
45
46
47
# File 'lib/honeybadger/util/sql.rb', line 42

def self.truncate(sql, adapter, max_length)
  head = force_utf_8(sql.byteslice(0, TRUNCATED_HEAD_LENGTH)).scrub("")[TRUNCATED_HEAD_SAFE]
  head = head[0, head.index('"')] if head.include?('"') && !adapter.to_s.match?(DOUBLE_QUOTERS)

  "#{obfuscate(head, adapter)} ... [truncated #{sql.bytesize} bytes]"
end