appmap 0.33.0 → 0.34.0

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: ae23c62b9a3f01cc3b7680706746d2f51c94c3a776978a8ee2d4e0e8501fca01
4
- data.tar.gz: 4aa33af19ed0c31b826f8cffa75e147e2781fe93ed39353ace29cede8a7882b9
3
+ metadata.gz: 47030df4910179ac636b8a2c4c3c29c25225deb1b9cbce851264d1e2ae8dacf1
4
+ data.tar.gz: 84bc3de36a1f7700fe32128c40f8d2bf6d698208689e856fd2bc696c38be6500
5
5
  SHA512:
6
- metadata.gz: 04a3c1bee70c02c96c1b71d5153a6ed44d668d6fc69d6da77cd6201364ede609bee93604f4ace391ddb38f8259841d6b334fdbe9051f1667d41cf3383281fe58
7
- data.tar.gz: c60d868b2c1fa9c1e8742856dd7f946f5bf1284cab01a4478708c1582add5ca8fd16002e150d8a2cd897819edb661a9082cb01ec3e9879686d85d49b341d4ba8
6
+ metadata.gz: 98d8933599f9cdb39d2ab9d69d1c11c7f06972603c30b5d79b3c57dc2ec246aec2c432da4835bcab805d49ec17e757fc0377d583908c6894380e951071b2d797
7
+ data.tar.gz: eefe2496acc6be7ebfe20eee2ab9ed0e708658a039c196757dd00ca18351e0425d3a6d1f0599cca37bd4a2c786469d61fdc7ad0e50a56d9223360204e2bc4206
@@ -1,3 +1,7 @@
1
+ # v0.34.0
2
+
3
+ * Records builtin security and I/O methods from `OpenSSL`, `Net`, and `IO`.
4
+
1
5
  # v0.33.0
2
6
 
3
7
  * Added command `AppMap.open` to open an AppMap in the browser.
@@ -61,7 +61,7 @@ module AppMap
61
61
  location: location,
62
62
  static: static,
63
63
  labels: labels
64
- }.delete_if {|k,v| v.nil?}
64
+ }.delete_if { |k,v| v.nil? || v == [] }
65
65
  end
66
66
  end
67
67
  end
@@ -100,11 +100,16 @@ module AppMap
100
100
  static: static
101
101
  }
102
102
  location = method.source_location
103
- if location
104
- location_file, lineno = location
105
- location_file = location_file[Dir.pwd.length + 1..-1] if location_file.index(Dir.pwd) == 0
106
- function_info[:location] = [ location_file, lineno ].join(':')
107
- end
103
+
104
+ function_info[:location] = \
105
+ if location
106
+ location_file, lineno = location
107
+ location_file = location_file[Dir.pwd.length + 1..-1] if location_file.index(Dir.pwd) == 0
108
+ [ location_file, lineno ].join(':')
109
+ else
110
+ [ method.defined_class, static ? '.' : '#', method.name ].join
111
+ end
112
+
108
113
  function_info[:labels] = package.labels if package.labels
109
114
  object_infos << function_info
110
115
 
@@ -1,31 +1,54 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AppMap
4
- Package = Struct.new(:path, :package_name, :exclude, :labels) do
5
- def initialize(path, package_name, exclude, labels = nil)
6
- super
4
+ class Config
5
+ Package = Struct.new(:path, :package_name, :exclude, :labels) do
6
+ def initialize(path, package_name: nil, exclude: [], labels: [])
7
+ super path, package_name, exclude, labels
8
+ end
9
+
10
+ def to_h
11
+ {
12
+ path: path,
13
+ package_name: package_name,
14
+ exclude: exclude.blank? ? nil : exclude,
15
+ labels: labels.blank? ? nil : labels
16
+ }.compact
17
+ end
7
18
  end
8
19
 
9
- def to_h
10
- {
11
- path: path,
12
- package_name: package_name,
13
- exclude: exclude.blank? ? nil : exclude,
14
- labels: labels.blank? ? nil : labels
15
- }.compact
20
+ Hook = Struct.new(:method_names, :package) do
16
21
  end
17
- end
18
22
 
19
- class Config
23
+ OPENSSL_PACKAGE = Package.new('openssl', package_name: 'openssl', labels: %w[security crypto])
24
+
20
25
  # Methods that should always be hooked, with their containing
21
26
  # package and labels that should be applied to them.
22
27
  HOOKED_METHODS = {
23
- 'ActiveSupport::SecurityUtils' => {
24
- secure_compare: Package.new('active_support', nil, nil, ['security'])
25
- }
26
- }
28
+ 'ActiveSupport::SecurityUtils' => Hook.new(:secure_compare, Package.new('active_support', package_name: 'active_support', labels: %w[security crypto]))
29
+ }.freeze
30
+
31
+ BUILTIN_METHODS = {
32
+ 'OpenSSL::PKey::PKey' => Hook.new(:sign, OPENSSL_PACKAGE),
33
+ 'Digest::Instance' => Hook.new(:digest, OPENSSL_PACKAGE),
34
+ 'OpenSSL::X509::Request' => Hook.new(%i[sign verify], OPENSSL_PACKAGE),
35
+ 'OpenSSL::PKCS5' => Hook.new(%i[pbkdf2_hmac_sha1 pbkdf2_hmac], OPENSSL_PACKAGE),
36
+ 'OpenSSL::Cipher' => Hook.new(%i[encrypt decrypt final], OPENSSL_PACKAGE),
37
+ 'OpenSSL::X509::Certificate' => Hook.new(:sign, OPENSSL_PACKAGE),
38
+ 'Logger' => Hook.new(:add, Package.new('logger', labels: %w[log io])),
39
+ 'Net::HTTP' => Hook.new(:request, Package.new('net/http', package_name: 'net/http', labels: %w[http io])),
40
+ 'Net::SMTP' => Hook.new(:send, Package.new('net/smtp', package_name: 'net/smtp', labels: %w[smtp email io])),
41
+ 'Net::POP3' => Hook.new(:mails, Package.new('net/pop3', package_name: 'net/pop', labels: %w[pop pop3 email io])),
42
+ 'Net::IMAP' => Hook.new(:send_command, Package.new('net/imap', package_name: 'net/imap', labels: %w[imap email io])),
43
+ 'IO' => Hook.new(%i[read write open close], Package.new('io', labels: %w[io])),
44
+ 'Marshal' => Hook.new(%i[dump load], Package.new('marshal', labels: %w[serialization marshal])),
45
+ 'Psych' => Hook.new(%i[dump dump_stream load load_stream parse parse_stream], Package.new('yaml', package_name: 'psych', labels: %w[serialization yaml])),
46
+ 'JSON::Ext::Parser' => Hook.new(:parse, Package.new('json', package_name: 'json', labels: %w[serialization json])),
47
+ 'JSON::Ext::Generator::State' => Hook.new(:generate, Package.new('json', package_name: 'json', labels: %w[serialization json]))
48
+ }.freeze
27
49
 
28
50
  attr_reader :name, :packages
51
+
29
52
  def initialize(name, packages = [])
30
53
  @name = name
31
54
  @packages = packages
@@ -41,7 +64,7 @@ module AppMap
41
64
  # Loads configuration from a Hash.
42
65
  def load(config_data)
43
66
  packages = (config_data['packages'] || []).map do |package|
44
- Package.new(package['path'], nil, package['exclude'] || [])
67
+ Package.new(package['path'], exclude: package['exclude'] || [])
45
68
  end
46
69
  Config.new config_data['name'], packages
47
70
  end
@@ -55,9 +78,9 @@ module AppMap
55
78
  end
56
79
 
57
80
  def package_for_method(method)
58
- defined_class, _, method_name = Hook.qualify_method_name(method)
59
- hooked_method = find_hooked_method(defined_class, method_name)
60
- return hooked_method if hooked_method
81
+ defined_class, _, method_name = ::AppMap::Hook.qualify_method_name(method)
82
+ package = find_package(defined_class, method_name)
83
+ return package if package
61
84
 
62
85
  location = method.source_location
63
86
  location_file, = location
@@ -75,15 +98,18 @@ module AppMap
75
98
  end
76
99
 
77
100
  def always_hook?(defined_class, method_name)
78
- !!find_hooked_method(defined_class, method_name)
101
+ !!find_package(defined_class, method_name)
79
102
  end
80
103
 
81
- def find_hooked_method(defined_class, method_name)
82
- find_hooked_class(defined_class)[method_name]
104
+ def find_package(defined_class, method_name)
105
+ hook = find_hook(defined_class)
106
+ return nil unless hook
107
+
108
+ Array(hook.method_names).include?(method_name) ? hook.package : nil
83
109
  end
84
110
 
85
- def find_hooked_class(defined_class)
86
- HOOKED_METHODS[defined_class] || {}
111
+ def find_hook(defined_class)
112
+ HOOKED_METHODS[defined_class] || BUILTIN_METHODS[defined_class]
87
113
  end
88
114
  end
89
115
  end
@@ -7,6 +7,12 @@ module AppMap
7
7
  LOG = (ENV['DEBUG'] == 'true')
8
8
 
9
9
  class << self
10
+ def lock_builtins
11
+ return if @builtins_hooked
12
+
13
+ @builtins_hooked = true
14
+ end
15
+
10
16
  # Return the class, separator ('.' or '#'), and method name for
11
17
  # the given method.
12
18
  def qualify_method_name(method)
@@ -39,6 +45,8 @@ module AppMap
39
45
  def enable &block
40
46
  require 'appmap/hook/method'
41
47
 
48
+ hook_builtins
49
+
42
50
  tp = TracePoint.new(:end) do |trace_point|
43
51
  cls = trace_point.self
44
52
 
@@ -47,8 +55,6 @@ module AppMap
47
55
 
48
56
  hook = lambda do |hook_cls|
49
57
  lambda do |method_id|
50
- next if method_id.to_s =~ /_hooked_by_appmap$/
51
-
52
58
  method = hook_cls.public_instance_method(method_id)
53
59
  hook_method = Hook::Method.new(hook_cls, method)
54
60
 
@@ -76,5 +82,35 @@ module AppMap
76
82
 
77
83
  tp.enable(&block)
78
84
  end
85
+
86
+ def hook_builtins
87
+ return unless self.class.lock_builtins
88
+
89
+ class_from_string = lambda do |fq_class|
90
+ fq_class.split('::').inject(Object) do |mod, class_name|
91
+ mod.const_get(class_name)
92
+ end
93
+ end
94
+
95
+ Config::BUILTIN_METHODS.each do |class_name, hook|
96
+ require hook.package.package_name if hook.package.package_name
97
+ Array(hook.method_names).each do |method_name|
98
+ method_name = method_name.to_sym
99
+ cls = class_from_string.(class_name)
100
+ method = \
101
+ begin
102
+ cls.instance_method(method_name)
103
+ rescue NameError
104
+ cls.method(method_name) rescue nil
105
+ end
106
+
107
+ if method
108
+ Hook::Method.new(cls, method).activate
109
+ else
110
+ warn "Method #{method_name} not found on #{cls.name}"
111
+ end
112
+ end
113
+ end
114
+ end
79
115
  end
80
116
  end
@@ -154,7 +154,7 @@ module AppMap
154
154
  end
155
155
 
156
156
  labels = labels.map(&:to_s).map(&:strip).reject(&:blank?).map(&:downcase).uniq
157
- description.reject!(&:nil?).reject(&:blank?)
157
+ description.reject!(&:nil?).reject!(&:blank?)
158
158
  default_description = description.last
159
159
  description.reverse!
160
160
 
@@ -3,7 +3,7 @@
3
3
  module AppMap
4
4
  URL = 'https://github.com/applandinc/appmap-ruby'
5
5
 
6
- VERSION = '0.33.0'
6
+ VERSION = '0.34.0'
7
7
 
8
8
  APPMAP_FORMAT_VERSION = '1.2'
9
9
  end
@@ -27,7 +27,7 @@ describe 'AppMap class Hooking', docker: false do
27
27
 
28
28
  def invoke_test_file(file, setup: nil, &block)
29
29
  AppMap.configuration = nil
30
- package = AppMap::Package.new(file, nil, [])
30
+ package = AppMap::Config::Package.new(file)
31
31
  config = AppMap::Config.new('hook_spec', [ package ])
32
32
  AppMap.configuration = config
33
33
  tracer = nil
@@ -51,7 +51,8 @@ describe 'AppMap class Hooking', docker: false do
51
51
  config, tracer = invoke_test_file(file, setup: setup, &block)
52
52
 
53
53
  events = collect_events(tracer)
54
- expect(Diffy::Diff.new(events, events_yaml).to_s).to eq('')
54
+
55
+ expect(Diffy::Diff.new(events_yaml, events).to_s).to eq('')
55
56
 
56
57
  [ config, tracer ]
57
58
  end
@@ -99,7 +100,7 @@ describe 'AppMap class Hooking', docker: false do
99
100
  InstanceMethod.new.say_default
100
101
  end
101
102
  class_map = AppMap.class_map(tracer.event_methods).to_yaml
102
- expect(Diffy::Diff.new(class_map, <<~YAML).to_s).to eq('')
103
+ expect(Diffy::Diff.new(<<~YAML, class_map).to_s).to eq('')
103
104
  ---
104
105
  - :name: spec/fixtures/hook/instance_method.rb
105
106
  :type: package
@@ -481,12 +482,52 @@ describe 'AppMap class Hooking', docker: false do
481
482
  :class: Module
482
483
  :value: ActiveSupport::SecurityUtils
483
484
  - :id: 3
485
+ :event: :call
486
+ :defined_class: Digest::Instance
487
+ :method_id: digest
488
+ :path: Digest::Instance#digest
489
+ :static: false
490
+ :parameters:
491
+ - :name: arg
492
+ :class: String
493
+ :value: string
494
+ :kind: :rest
495
+ :receiver:
496
+ :class: Digest::SHA256
497
+ :value: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
498
+ - :id: 4
499
+ :event: :return
500
+ :parent_id: 3
501
+ :return_value:
502
+ :class: String
503
+ :value: "G2__)__qc____X____3_].\\x02y__.___/_"
504
+ - :id: 5
505
+ :event: :call
506
+ :defined_class: Digest::Instance
507
+ :method_id: digest
508
+ :path: Digest::Instance#digest
509
+ :static: false
510
+ :parameters:
511
+ - :name: arg
512
+ :class: String
513
+ :value: string
514
+ :kind: :rest
515
+ :receiver:
516
+ :class: Digest::SHA256
517
+ :value: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
518
+ - :id: 6
519
+ :event: :return
520
+ :parent_id: 5
521
+ :return_value:
522
+ :class: String
523
+ :value: "G2__)__qc____X____3_].\\x02y__.___/_"
524
+ - :id: 7
484
525
  :event: :return
485
526
  :parent_id: 2
486
527
  :return_value:
487
528
  :class: TrueClass
488
529
  :value: 'true'
489
- - :id: 4
530
+ - :id: 8
490
531
  :event: :return
491
532
  :parent_id: 1
492
533
  :return_value:
@@ -527,6 +568,23 @@ describe 'AppMap class Hooking', docker: false do
527
568
  :static: true
528
569
  :labels:
529
570
  - security
571
+ - crypto
572
+ - :name: openssl
573
+ :type: package
574
+ :children:
575
+ - :name: Digest
576
+ :type: class
577
+ :children:
578
+ - :name: Instance
579
+ :type: class
580
+ :children:
581
+ - :name: digest
582
+ :type: function
583
+ :location: Digest::Instance#digest
584
+ :static: false
585
+ :labels:
586
+ - security
587
+ - crypto
530
588
  YAML
531
589
 
532
590
  config, tracer = invoke_test_file 'spec/fixtures/hook/compare.rb' do
@@ -538,7 +596,7 @@ describe 'AppMap class Hooking', docker: false do
538
596
  expect(entry[:name]).to eq('secure_compare')
539
597
  spec = Gem::Specification.find_by_name('activesupport')
540
598
  entry[:location].gsub!(spec.base_dir + '/', '')
541
- expect(Diffy::Diff.new(cm.to_yaml, classmap_yaml).to_s).to eq('')
599
+ expect(Diffy::Diff.new(classmap_yaml, cm.to_yaml).to_s).to eq('')
542
600
  end
543
601
  end
544
602
  end
@@ -56,10 +56,12 @@ class CLITest < Minitest::Test
56
56
  Class frequency:
57
57
  ----------------
58
58
  1 Main
59
+ 1 IO
59
60
 
60
61
  Method frequency:
61
62
  ----------------
62
63
  1 Main.say_hello
64
+ 1 IO#write
63
65
  OUTPUT
64
66
  end
65
67
 
@@ -80,12 +82,20 @@ class CLITest < Minitest::Test
80
82
  {
81
83
  "name": "Main",
82
84
  "count": 1
85
+ },
86
+ {
87
+ "name": "IO",
88
+ "count": 1
83
89
  }
84
90
  ],
85
91
  "method_frequency": [
86
92
  {
87
93
  "name": "Main.say_hello",
88
94
  "count": 1
95
+ },
96
+ {
97
+ "name": "IO#write",
98
+ "count": 1
89
99
  }
90
100
  ]
91
101
  }
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'appmap', git: 'applandinc/appmap-ruby', branch: `git rev-parse --abbrev-ref HEAD`.strip
@@ -0,0 +1,3 @@
1
+ name: openssl_recorder
2
+ packages:
3
+ - path: lib
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ # From the manual page https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL.html
4
+
5
+ require 'openssl'
6
+
7
+ module Example
8
+ def Example.sign
9
+ ca_key = OpenSSL::PKey::RSA.new 2048
10
+ pass_phrase = 'my secure pass phrase goes here'
11
+
12
+ cipher = OpenSSL::Cipher.new 'AES-256-CBC'
13
+
14
+ open 'tmp/ca_key.pem', 'w', 0644 do |io|
15
+ io.write ca_key.export(cipher, pass_phrase)
16
+ end
17
+
18
+ ca_name = OpenSSL::X509::Name.parse '/CN=ca/DC=example'
19
+
20
+ ca_cert = OpenSSL::X509::Certificate.new
21
+ ca_cert.serial = 0
22
+ ca_cert.version = 2
23
+ ca_cert.not_before = Time.now
24
+ ca_cert.not_after = Time.now + 86400
25
+
26
+ ca_cert.public_key = ca_key.public_key
27
+ ca_cert.subject = ca_name
28
+ ca_cert.issuer = ca_name
29
+
30
+ extension_factory = OpenSSL::X509::ExtensionFactory.new
31
+ extension_factory.subject_certificate = ca_cert
32
+ extension_factory.issuer_certificate = ca_cert
33
+
34
+ ca_cert.add_extension extension_factory.create_extension('subjectKeyIdentifier', 'hash')
35
+ ca_cert.add_extension extension_factory.create_extension('basicConstraints', 'CA:TRUE', true)
36
+
37
+ ca_cert.add_extension extension_factory.create_extension(
38
+ 'keyUsage', 'cRLSign,keyCertSign', true)
39
+
40
+ ca_cert.sign ca_key, OpenSSL::Digest::SHA1.new
41
+
42
+ open 'tmp/ca_cert.pem', 'w' do |io|
43
+ io.write ca_cert.to_pem
44
+ end
45
+
46
+ csr = OpenSSL::X509::Request.new
47
+ csr.version = 0
48
+ csr.subject = OpenSSL::X509::Name.new([ ['CN', 'the name to sign', OpenSSL::ASN1::UTF8STRING] ])
49
+ csr.public_key = ca_key.public_key
50
+ csr.sign ca_key, OpenSSL::Digest::SHA1.new
51
+
52
+ open 'tmp/csr.pem', 'w' do |io|
53
+ io.write csr.to_pem
54
+ end
55
+
56
+ csr = OpenSSL::X509::Request.new File.read 'tmp/csr.pem'
57
+
58
+ raise 'CSR can not be verified' unless csr.verify csr.public_key
59
+
60
+ csr_cert = OpenSSL::X509::Certificate.new
61
+ csr_cert.serial = 0
62
+ csr_cert.version = 2
63
+ csr_cert.not_before = Time.now
64
+ csr_cert.not_after = Time.now + 600
65
+
66
+ csr_cert.subject = csr.subject
67
+ csr_cert.public_key = csr.public_key
68
+ csr_cert.issuer = ca_cert.subject
69
+
70
+ extension_factory = OpenSSL::X509::ExtensionFactory.new
71
+ extension_factory.subject_certificate = csr_cert
72
+ extension_factory.issuer_certificate = ca_cert
73
+
74
+ csr_cert.add_extension extension_factory.create_extension('basicConstraints', 'CA:FALSE')
75
+
76
+ csr_cert.add_extension extension_factory.create_extension(
77
+ 'keyUsage', 'keyEncipherment,dataEncipherment,digitalSignature')
78
+
79
+ csr_cert.add_extension extension_factory.create_extension('subjectKeyIdentifier', 'hash')
80
+
81
+ csr_cert.sign ca_key, OpenSSL::Digest::SHA1.new
82
+
83
+ 'tmp/csr_cert.pem'.tap do |fname|
84
+ open fname, 'w' do |io|
85
+ io.write csr_cert.to_pem
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ if __FILE__ == $0
92
+ cert_file = Example.sign
93
+ puts "Wrote cert file #{cert_file}"
94
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ # From the manual page https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL.html
4
+
5
+ require 'openssl'
6
+
7
+ module Example
8
+ def Example.encrypt
9
+ cipher = OpenSSL::Cipher.new 'AES-256-CBC'
10
+ cipher.encrypt
11
+ iv = cipher.random_iv
12
+
13
+ pwd = 'some hopefully not to easily guessable password'
14
+ salt = OpenSSL::Random.random_bytes 16
15
+ iter = 20000
16
+ key_len = cipher.key_len
17
+ digest = OpenSSL::Digest::SHA256.new
18
+
19
+ key = OpenSSL::PKCS5.pbkdf2_hmac(pwd, salt, iter, key_len, digest)
20
+ cipher.key = key
21
+
22
+ document = 'the document'
23
+
24
+ encrypted = cipher.update document
25
+ encrypted << cipher.final
26
+ encrypted
27
+ end
28
+ end
29
+
30
+ if __FILE__ == $0
31
+ ciphertext = Example.encrypt
32
+ require 'base64'
33
+ puts "Computed ciphertext #{Base64.urlsafe_encode64(ciphertext)}"
34
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ # From the manual page https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL.html
4
+
5
+ require 'appmap'
6
+ require 'openssl'
7
+ require 'openssl/digest'
8
+
9
+ module Example
10
+ def Example.sign
11
+ key = OpenSSL::PKey::RSA.new 2048
12
+
13
+ document = 'the document'
14
+
15
+ digest = OpenSSL::Digest::SHA256.new
16
+ key.sign digest, document
17
+ end
18
+ end
19
+
20
+ if __FILE__ == $0
21
+ appmap = AppMap.record do
22
+ Example.sign
23
+ puts 'Computed signature'
24
+ end
25
+ appmap['metadata'] = [ 'recorder' => __FILE__ ]
26
+
27
+ File.write('appmap.json', JSON.generate(appmap))
28
+ end
@@ -0,0 +1,203 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'test_helper'
5
+ require 'English'
6
+
7
+ class OpenSSLTest < Minitest::Test
8
+ def perform_test(test_name)
9
+ Bundler.with_clean_env do
10
+ Dir.chdir 'test/fixtures/openssl_recorder' do
11
+ FileUtils.rm_rf 'tmp'
12
+ system 'bundle config --local local.appmap ../../..'
13
+ system 'bundle'
14
+ system({ 'APPMAP' => 'true', 'DEBUG' => 'true' }, %(bundle exec ruby lib/openssl_#{test_name}.rb))
15
+
16
+ yield
17
+ end
18
+ end
19
+ end
20
+
21
+ def test_key_sign
22
+ perform_test 'key_sign' do
23
+ appmap_file = 'appmap.json'
24
+
25
+ assert File.file?(appmap_file), 'appmap output file does not exist'
26
+ appmap = JSON.parse(File.read(appmap_file))
27
+ assert_equal AppMap::APPMAP_FORMAT_VERSION, appmap['version']
28
+ assert_equal [ { 'recorder' => 'lib/openssl_key_sign.rb' } ], appmap['metadata']
29
+ assert_equal JSON.parse(<<~JSON), appmap['classMap']
30
+ [
31
+ {
32
+ "name": "lib",
33
+ "type": "package",
34
+ "children": [
35
+ {
36
+ "name": "Example",
37
+ "type": "class",
38
+ "children": [
39
+ {
40
+ "name": "sign",
41
+ "type": "function",
42
+ "location": "lib/openssl_key_sign.rb:10",
43
+ "static": true
44
+ }
45
+ ]
46
+ }
47
+ ]
48
+ },
49
+ {
50
+ "name": "openssl",
51
+ "type": "package",
52
+ "children": [
53
+ {
54
+ "name": "OpenSSL",
55
+ "type": "class",
56
+ "children": [
57
+ {
58
+ "name": "PKey",
59
+ "type": "class",
60
+ "children": [
61
+ {
62
+ "name": "PKey",
63
+ "type": "class",
64
+ "children": [
65
+ {
66
+ "name": "sign",
67
+ "type": "function",
68
+ "location": "OpenSSL::PKey::PKey#sign",
69
+ "static": false,
70
+ "labels": [
71
+ "security",
72
+ "crypto"
73
+ ]
74
+ }
75
+ ]
76
+ }
77
+ ]
78
+ }
79
+ ]
80
+ }
81
+ ]
82
+ },
83
+ {
84
+ "name": "io",
85
+ "type": "package",
86
+ "children": [
87
+ {
88
+ "name": "IO",
89
+ "type": "class",
90
+ "children": [
91
+ {
92
+ "name": "write",
93
+ "type": "function",
94
+ "location": "IO#write",
95
+ "static": false,
96
+ "labels": [
97
+ "io"
98
+ ]
99
+ }
100
+ ]
101
+ }
102
+ ]
103
+ }
104
+ ]
105
+ JSON
106
+ sanitized_events = appmap['events'].map(&:deep_symbolize_keys).map(&AppMap::Util.method(:sanitize_event)).map do |event|
107
+ delete_value = ->(obj) { (obj || {}).delete(:value) }
108
+ delete_value.call(event[:receiver])
109
+ delete_value.call(event[:return_value])
110
+ event
111
+ end
112
+
113
+ diff = Diffy::Diff.new(<<~JSON.strip, JSON.pretty_generate(sanitized_events).strip)
114
+ [
115
+ {
116
+ "id": 1,
117
+ "event": "call",
118
+ "defined_class": "Example",
119
+ "method_id": "sign",
120
+ "path": "lib/openssl_key_sign.rb",
121
+ "lineno": 10,
122
+ "static": true,
123
+ "parameters": [
124
+
125
+ ],
126
+ "receiver": {
127
+ "class": "Module"
128
+ }
129
+ },
130
+ {
131
+ "id": 2,
132
+ "event": "call",
133
+ "defined_class": "OpenSSL::PKey::PKey",
134
+ "method_id": "sign",
135
+ "path": "OpenSSL::PKey::PKey#sign",
136
+ "static": false,
137
+ "parameters": [
138
+ {
139
+ "name": "arg",
140
+ "class": "OpenSSL::Digest::SHA256",
141
+ "value": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
142
+ "kind": "req"
143
+ },
144
+ {
145
+ "name": "arg",
146
+ "class": "String",
147
+ "value": "the document",
148
+ "kind": "req"
149
+ }
150
+ ],
151
+ "receiver": {
152
+ "class": "OpenSSL::PKey::RSA"
153
+ }
154
+ },
155
+ {
156
+ "id": 3,
157
+ "event": "return",
158
+ "parent_id": 2,
159
+ "return_value": {
160
+ "class": "String"
161
+ }
162
+ },
163
+ {
164
+ "id": 4,
165
+ "event": "return",
166
+ "parent_id": 1,
167
+ "return_value": {
168
+ "class": "String"
169
+ }
170
+ },
171
+ {
172
+ "id": 5,
173
+ "event": "call",
174
+ "defined_class": "IO",
175
+ "method_id": "write",
176
+ "path": "IO#write",
177
+ "static": false,
178
+ "parameters": [
179
+ {
180
+ "name": "arg",
181
+ "class": "String",
182
+ "value": "Computed signature",
183
+ "kind": "rest"
184
+ }
185
+ ],
186
+ "receiver": {
187
+ "class": "IO"
188
+ }
189
+ },
190
+ {
191
+ "id": 6,
192
+ "event": "return",
193
+ "parent_id": 5,
194
+ "return_value": {
195
+ "class": "Integer"
196
+ }
197
+ }
198
+ ]
199
+ JSON
200
+ assert_equal '', diff.to_s
201
+ end
202
+ end
203
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appmap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.33.0
4
+ version: 0.34.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Gilpin
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-08-15 00:00:00.000000000 Z
11
+ date: 2020-08-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -503,6 +503,11 @@ files:
503
503
  - test/fixtures/minitest_recorder/appmap.yml
504
504
  - test/fixtures/minitest_recorder/lib/hello.rb
505
505
  - test/fixtures/minitest_recorder/test/hello_test.rb
506
+ - test/fixtures/openssl_recorder/Gemfile
507
+ - test/fixtures/openssl_recorder/appmap.yml
508
+ - test/fixtures/openssl_recorder/lib/openssl_cert_sign.rb
509
+ - test/fixtures/openssl_recorder/lib/openssl_encrypt.rb
510
+ - test/fixtures/openssl_recorder/lib/openssl_key_sign.rb
506
511
  - test/fixtures/process_recorder/appmap.yml
507
512
  - test/fixtures/process_recorder/hello.rb
508
513
  - test/fixtures/rspec_recorder/Gemfile
@@ -512,6 +517,7 @@ files:
512
517
  - test/fixtures/rspec_recorder/spec/labeled_hello_spec.rb
513
518
  - test/fixtures/rspec_recorder/spec/plain_hello_spec.rb
514
519
  - test/minitest_test.rb
520
+ - test/openssl_test.rb
515
521
  - test/record_process_test.rb
516
522
  - test/rspec_test.rb
517
523
  - test/test_helper.rb