activerecord-instrumentation 0.3.0.jlauer2 → 0.4.0.jlauer1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a4923138123e7321b389d97d69503b1fa5db021f5f50438209dbfe06f3942aa7
4
- data.tar.gz: f31bd6a40eedfdf3f10dfbfb147a9ac0e15a23f7382dd04b09e9bd6265f4aab0
3
+ metadata.gz: 48fc834ad963b8c31ea09c9974ca1d024a692a71592a20e75f0079c9a2c93203
4
+ data.tar.gz: 6be927a6854e55963f0fa1ab1ca0f5aa05c44e2e75d4eb8bebe7b3a459a00c9a
5
5
  SHA512:
6
- metadata.gz: 8bb7a590213347f0ea7f9da3e42addd4abd2d790fcfea5becba2acc964fc1188a46373dae31277dcc3f8eb9fade5faa24620cbafbd3031b84f153d6c14d8fa00
7
- data.tar.gz: 19998dd7f54661abb0d0e0530e962d95482c48543fabfc50f39c03f529fee299a48c24283ce2e49c6d190b82b820688584dbd95a6fb86b1a53b592c1ddf87b94
6
+ metadata.gz: f4980f5d2f6d3c2081c42b1c5a2761fa10470b820ac75db0d1eda27e2b85fc4d973ddf901b69acc0aa43d0ab09e688934f7b847c6ed2c354431fa73c4e76d2ab
7
+ data.tar.gz: 2be68549771581ee9e470bcc2664036206fbaa973d6adecdfef4b7f03993c3b2b9b8b45e170ed11da4293453925c3bdebd38192c00937edaa2ac555b15c4de63
@@ -22,7 +22,11 @@
22
22
  - "./spec/*_helper*.rb"
23
23
  - "./tasks/ci.rake"
24
24
  - "Gemfile"
25
-
25
+
26
+ Layout/LineLength:
27
+ Exclude:
28
+ - "./**/*_spec.rb"
29
+
26
30
  Metrics/ModuleLength:
27
31
  Exclude:
28
32
  - "./**/*_spec*.rb"
@@ -31,5 +35,5 @@
31
35
  Exclude:
32
36
  - "./**/*_spec*.rb"
33
37
 
34
- Rails/ApplicationRecord:
38
+ Rails:
35
39
  Enabled: false
@@ -1,7 +1,10 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
- ## 0.3.0.jlauer2 04/22/2020
4
+ ## 0.4.0.jlauer1 04/24/2020
5
+ * Add SQL sanitizers
6
+
7
+ ## 0.3.0 04/22/2020
5
8
  * Set up build pipeline with circleci and gem-publisher
6
9
  * Fixed linting issues
7
10
  * Renamed gem to `activerecord-instrumentation`
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- activerecord-instrumentation (0.3.0.jlauer2)
4
+ activerecord-instrumentation (0.4.0.jlauer1)
5
5
  activerecord (~> 6.0)
6
6
  opentracing (~> 0.5)
7
7
 
@@ -5,11 +5,13 @@ require "opentracing"
5
5
 
6
6
  require "active_record/open_tracing/version"
7
7
  require "active_record/open_tracing/processor"
8
+ require "active_record/open_tracing/sql_sanitizer"
8
9
 
9
10
  module ActiveRecord
10
11
  module OpenTracing
11
- def self.instrument(tracer: ::OpenTracing.global_tracer)
12
- processor = Processor.new(tracer)
12
+ def self.instrument(tracer: ::OpenTracing.global_tracer, sanitizer: nil)
13
+ sql_sanitizer = sanitizer && SqlSanitizer.build_sanitizer(sanitizer)
14
+ processor = Processor.new(tracer, sanitizer: sql_sanitizer)
13
15
 
14
16
  ActiveSupport::Notifications.subscribe("sql.active_record") do |*args|
15
17
  processor.call(*args)
@@ -8,12 +8,15 @@ module ActiveRecord
8
8
  SPAN_KIND = "client"
9
9
  DB_TYPE = "sql"
10
10
 
11
- def initialize(tracer)
11
+ attr_reader :tracer, :sanitizer
12
+
13
+ def initialize(tracer, sanitizer: nil)
12
14
  @tracer = tracer
15
+ @sanitizer = sanitizer
13
16
  end
14
17
 
15
18
  def call(_event_name, start, finish, _id, payload)
16
- span = @tracer.start_span(
19
+ span = tracer.start_span(
17
20
  payload[:name] || DEFAULT_OPERATION_NAME,
18
21
  start_time: start,
19
22
  tags: tags_for_payload(payload)
@@ -45,12 +48,16 @@ module ActiveRecord
45
48
  "span.kind" => SPAN_KIND,
46
49
  "db.instance" => db_instance,
47
50
  "db.cached" => payload.fetch(:cached, false),
48
- "db.statement" => payload.fetch(:sql).squish,
51
+ "db.statement" => sanitize_sql(payload.fetch(:sql).squish),
49
52
  "db.type" => DB_TYPE,
50
53
  "peer.address" => db_address
51
54
  }
52
55
  end
53
56
 
57
+ def sanitize_sql(sql)
58
+ sanitizer ? sanitizer.sanitize(sql) : sql
59
+ end
60
+
54
61
  def db_instance
55
62
  @db_instance ||= db_config.fetch(:database)
56
63
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record/open_tracing/sql_sanitizer/base"
4
+ require "active_record/open_tracing/sql_sanitizer/mysql"
5
+ require "active_record/open_tracing/sql_sanitizer/postgres"
6
+ require "active_record/open_tracing/sql_sanitizer/sql_server"
7
+ require "active_record/open_tracing/sql_sanitizer/sqlite"
8
+ require "active_record/open_tracing/sql_sanitizer/regexes"
9
+
10
+ module ActiveRecord
11
+ module OpenTracing
12
+ module SqlSanitizer
13
+ KLASSES = {
14
+ mysql: Mysql,
15
+ postgres: Postgres,
16
+ sql_server: SqlServer,
17
+ sqlite: Sqlite
18
+ }.freeze
19
+
20
+ class << self
21
+ def build_sanitizer(sanitizer_name)
22
+ sanitizer_klass(sanitizer_name).build
23
+ end
24
+
25
+ private
26
+
27
+ def sanitizer_klass(sanitizer_name)
28
+ key = KLASSES.keys.detect do |name|
29
+ sanitizer_name.to_sym == name
30
+ end || (raise NameError, "Unknown sanitizer #{sanitizer_name.inspect}")
31
+
32
+ KLASSES.fetch(key)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module OpenTracing
5
+ module SqlSanitizer
6
+ class Base
7
+ require "active_record/open_tracing/sql_sanitizer/regexes"
8
+ include ActiveRecord::OpenTracing::SqlSanitizer::Regexes
9
+
10
+ def sanitize(sql)
11
+ scrubbed = scrub(sql.dup)
12
+ apply_substitutions(scrubbed)
13
+ end
14
+
15
+ private
16
+
17
+ def substitutions
18
+ raise NotImplementedError
19
+ end
20
+
21
+ def apply_substitutions(str)
22
+ substitutions.inject(str.dup) do |memo, (regex, replacement)|
23
+ if replacement.respond_to?(:call)
24
+ memo.gsub(regex, &replacement)
25
+ else
26
+ memo.gsub(regex, replacement)
27
+ end
28
+ end.strip
29
+ end
30
+
31
+ def encodings?(encodings = %w[UTF-8 binary])
32
+ encodings.all? do |enc|
33
+ begin
34
+ Encoding.find(enc)
35
+ rescue StandardError
36
+ false
37
+ end
38
+ end
39
+ end
40
+
41
+ MAX_SQL_LENGTH = 16384
42
+
43
+ def scrub(str)
44
+ # safeguard - don't sanitize or scrub large SQL statements
45
+ return "" if !str.is_a?(String) || str.length > MAX_SQL_LENGTH
46
+
47
+ # Whatever encoding it is, it is valid and we can operate on it
48
+ return str if str.valid_encoding?
49
+
50
+ # Prefer scrub over convert
51
+ if str.respond_to?(:scrub)
52
+ str.scrub("_")
53
+ elsif encodings?(%w[UTF-8 binary])
54
+ str.encode("UTF-8", "binary", invalid: :replace, undef: :replace, replace: "_")
55
+ else
56
+ # Unable to scrub invalid sql encoding, returning empty string
57
+ ""
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module OpenTracing
5
+ module SqlSanitizer
6
+ class Mysql < Base
7
+ def substitutions
8
+ [
9
+ [MYSQL_VAR_INTERPOLATION, ""],
10
+ [MYSQL_REMOVE_SINGLE_QUOTE_STRINGS, "?"],
11
+ [MYSQL_REMOVE_DOUBLE_QUOTE_STRINGS, "?"],
12
+ [MYSQL_REMOVE_INTEGERS, "?"],
13
+ [MYSQL_IN_CLAUSE, "IN (?)"],
14
+ [MULTIPLE_QUESTIONS, "?"]
15
+ ]
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module OpenTracing
5
+ module SqlSanitizer
6
+ class Postgres < Base
7
+ def substitutions
8
+ [
9
+ [PSQL_PLACEHOLDER, "?"],
10
+ [PSQL_VAR_INTERPOLATION, ""],
11
+ [PSQL_AFTER_WHERE, ->(c) { c.gsub(PSQL_REMOVE_STRINGS, "?") }],
12
+ [PSQL_REMOVE_INTEGERS, "?"],
13
+ [PSQL_IN_CLAUSE, "IN (?)"],
14
+ [MULTIPLE_SPACES, " "]
15
+ ]
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module OpenTracing
5
+ module SqlSanitizer
6
+ module Regexes
7
+ MULTIPLE_SPACES = /\s+/.freeze
8
+ MULTIPLE_QUESTIONS = /\?(,\?)+/.freeze
9
+
10
+ PSQL_VAR_INTERPOLATION = /\[\[.*\]\]\s*$/.freeze
11
+ PSQL_REMOVE_STRINGS = /'(?:[^']|'')*'/.freeze
12
+ PSQL_REMOVE_INTEGERS = /(?<!LIMIT )\b\d+\b/.freeze
13
+ PSQL_PLACEHOLDER = /\$\d+/.freeze
14
+ PSQL_IN_CLAUSE = /IN\s+\(\?[^\)]*\)/.freeze
15
+ PSQL_AFTER_WHERE = /(?:WHERE\s+).*?(?:SELECT|$)/i.freeze
16
+
17
+ MYSQL_VAR_INTERPOLATION = /\[\[.*\]\]\s*$/.freeze
18
+ MYSQL_REMOVE_INTEGERS = /(?<!LIMIT )\b\d+\b/.freeze
19
+ MYSQL_REMOVE_SINGLE_QUOTE_STRINGS = /'(?:\\'|[^']|'')*'/.freeze
20
+ MYSQL_REMOVE_DOUBLE_QUOTE_STRINGS = /"(?:\\"|[^"]|"")*"/.freeze
21
+ MYSQL_IN_CLAUSE = /IN\s+\(\?[^\)]*\)/.freeze
22
+
23
+ SQLITE_VAR_INTERPOLATION = /\[\[.*\]\]\s*$/.freeze
24
+ SQLITE_REMOVE_STRINGS = /'(?:[^']|'')*'/.freeze
25
+ SQLITE_REMOVE_INTEGERS = /(?<!LIMIT )\b\d+\b/.freeze
26
+
27
+ SQLSERVER_EXECUTESQL = /EXEC sp_executesql N'(.*?)'.*/.freeze
28
+ SQLSERVER_REMOVE_INTEGERS = /(?<!LIMIT )\b(?<!@)\d+\b/.freeze
29
+ SQLSERVER_IN_CLAUSE = /IN\s+\(\?[^\)]*\)/.freeze
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module OpenTracing
5
+ module SqlSanitizer
6
+ class SqlServer < Base
7
+ def substitutions
8
+ [
9
+ [SQLSERVER_EXECUTESQL, '\1'],
10
+ [SQLSERVER_REMOVE_INTEGERS, "?"],
11
+ [SQLSERVER_IN_CLAUSE, "IN (?)"]
12
+ ]
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module OpenTracing
5
+ module SqlSanitizer
6
+ class Sqlite < Base
7
+ def substitutions
8
+ [
9
+ [SQLITE_VAR_INTERPOLATION, ""],
10
+ [SQLITE_REMOVE_STRINGS, "?"],
11
+ [SQLITE_REMOVE_INTEGERS, "?"],
12
+ [MULTIPLE_SPACES, " "]
13
+ ]
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module OpenTracing
5
- VERSION = "0.3.0.jlauer2"
5
+ VERSION = "0.4.0.jlauer1"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-instrumentation
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0.jlauer2
4
+ version: 0.4.0.jlauer1
5
5
  platform: ruby
6
6
  authors:
7
7
  - SaleMove TechMovers
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-04-22 00:00:00.000000000 Z
12
+ date: 2020-04-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -200,6 +200,13 @@ files:
200
200
  - activerecord-instrumentation.gemspec
201
201
  - lib/active_record/open_tracing.rb
202
202
  - lib/active_record/open_tracing/processor.rb
203
+ - lib/active_record/open_tracing/sql_sanitizer.rb
204
+ - lib/active_record/open_tracing/sql_sanitizer/base.rb
205
+ - lib/active_record/open_tracing/sql_sanitizer/mysql.rb
206
+ - lib/active_record/open_tracing/sql_sanitizer/postgres.rb
207
+ - lib/active_record/open_tracing/sql_sanitizer/regexes.rb
208
+ - lib/active_record/open_tracing/sql_sanitizer/sql_server.rb
209
+ - lib/active_record/open_tracing/sql_sanitizer/sqlite.rb
203
210
  - lib/active_record/open_tracing/version.rb
204
211
  homepage: https://github.com/doximity/ruby-activerecord-opentracing
205
212
  licenses: