tls-map 1.1.0 → 1.3.2

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: 83caecd8dd0a4d170d686c141a7b39860f99f29f2bc4e436418eca7b877807ff
4
- data.tar.gz: 7a3a245f86067d8c9d59fb8f75b207e320c09d11b701624adf86842da27afc4e
3
+ metadata.gz: 0eadde9b90bc4cfb3dc23e8b6263fdaf5fc1815a262ee8c6f2db3dfc0fe54db7
4
+ data.tar.gz: 7c80ee6fa969f77643e8256cc0d4a33f1de8bf1f540fefe7d1641e3691e76044
5
5
  SHA512:
6
- metadata.gz: fa1240ae951c33a26838ee04f15b55e03ec452cd9b03790394ebee99a142acc91b0bda3074f95bf97f495d3b553ef8363c17221d2c519772690ca5573954a80d
7
- data.tar.gz: cd72c3ff9281bfe37a7f866a33fc73340dbd70f37ee370cfd3a01a1742760d3f0f110a778ded5b03aca8b01873832f66ca24ebeef51ba3cab09d57c3e81b5377
6
+ metadata.gz: a7303c826807b76c35d3899b02ce0af2874471e7dc55f19d6dcef85447270c5ea8fbb67e7bdb21fa5acf9a352f5355adcaa83e23bc4aeb83f42ca0d2149964aa
7
+ data.tar.gz: c148a6770e6619bb7d32a435d7ad314e55871f4188cbff01c1f21c8e02e58497e3c6d32ec007bb27b6b9020c85bbd596a32e99575c7a1fab3cd4d224f7969184
data/bin/tls-map CHANGED
@@ -10,31 +10,44 @@ require 'tls_map/cli'
10
10
  require 'docopt'
11
11
  require 'paint'
12
12
 
13
+ # can't specify 2 options with the same name even if used in different commands
14
+ # https://github.com/docopt/docopt/issues/296#issuecomment-857477191
13
15
  doc = <<~DOCOPT
14
- TLS map
16
+ TLS map #{TLSmap::VERSION}
15
17
 
16
18
  Usage:
17
19
  tls-map search <critera> <term> [-o <output> --force -e -a] [--no-color --debug]
20
+ tls-map bulk <critera> <file> [-q <output> --force] [--no-color --debug]
18
21
  tls-map export <filename> <format> [--force] [--debug]
22
+ tls-map extract <filename> <format> [--no-color --debug]
19
23
  tls-map update [--debug]
20
24
  tls-map -h | --help
21
25
  tls-map --version
22
26
 
23
- Search options: (offline)
27
+ Search options: (offline) search and translate cipher names between SSL/TLS libraries
24
28
  <critera> The type of term. Accepted values: codepoint, iana, openssl, gnutls, nss.
25
29
  <term> The cipher algorithm name.
26
30
  -o, --output <output> Displayed fields. Accepted values: all, codepoint, iana, openssl, gnutls, nss. [default: all]
27
31
  -e, --extended (Online) Display additional information about the cipher (requires output = all or iana)
28
32
  -a, --acronym (Online) Display full acronym name (requires -e / --extended option)
29
33
 
30
- Export options: (offline)
34
+ Bulk options: (offline) search and translate cipher names between SSL/TLS libraries in bulk
35
+ <critera> The type of term. Accepted values: codepoint, iana, openssl, gnutls, nss.
36
+ <file> File containing the cipher algorithm names, one per line.
37
+ -q, --output2 <output> Displayed fields. Accepted values: codepoint, iana, openssl, gnutls, nss. [default: iana]
38
+
39
+ Export options: (offline) export the list of all ciphers (mapping) in various formats
31
40
  <filename> The output file name to write to.
32
41
  <format> Supported formats: markdown (a markdown table), json_pretty (expanded JSON), json_compact (minified JSON), marshal (Ruby marshalized hash).
33
42
 
43
+ Extract options: (offline) extract ciphers from external tools output file
44
+ <filename> The external tool output file
45
+ <format> Supported formats: sslyze, sslscan2, testssl, ssllabs-scan (check the documentation for the expected file format)
46
+
34
47
  Update options: (online) DANGEROUS, will break database integrity, force option will be required
35
48
 
36
49
  Other options:
37
- --force Force parsing even if intigrity check failed (DANGEROUS, may result in command execution vulnerability)
50
+ --force Force parsing even if integrity check failed (DANGEROUS, may result in command execution vulnerability)
38
51
  --no-color Disable colorized output
39
52
  --debug Display arguments
40
53
  -h, --help Show this screen
@@ -75,10 +88,24 @@ begin
75
88
  end
76
89
  end
77
90
  end
91
+ elsif args['bulk']
92
+ cli = TLSmap::CLI.new(args['--force'])
93
+ res = cli.bulk_search(args['<critera>'].to_sym, args['<file>'], args['--output2'].to_sym)
94
+ puts Paint['No match found', :red] if res.empty?
95
+ res.each do |h|
96
+ puts Paint[h[args['--output2'].to_sym], :green]
97
+ end
78
98
  elsif args['export']
79
99
  cli = TLSmap::CLI.new(args['--force'])
80
100
  cli.export(args['<filename>'], args['<format>'].to_sym)
81
101
  puts "#{args['<filename>']} exported"
102
+ elsif args['extract']
103
+ extractor = TLSmap::App::Extractor.new
104
+ ciphers = extractor.parse(args['<format>'], args['<filename>'])
105
+ ciphers.each do |k, v|
106
+ puts Paint[k, :blue] unless v.empty?
107
+ puts Paint[v.join("\n"), :white] unless v.empty?
108
+ end
82
109
  elsif args['update']
83
110
  cli = TLSmap::CLI.new
84
111
  cli.update
data/lib/tls_map.rb CHANGED
@@ -11,6 +11,7 @@ require 'tls_map/gnutls'
11
11
  require 'tls_map/nss'
12
12
  require 'tls_map/output'
13
13
  require 'tls_map/ciphersuiteinfo'
14
+ require 'tls_map/extractor'
14
15
 
15
16
  # TLS map module
16
17
  module TLSmap
@@ -18,11 +19,11 @@ module TLSmap
18
19
  class App
19
20
  # Will automatically fetch source files and parse them.
20
21
  def initialize
21
- @iana_file = tmpfile('iana', IANA_URL)
22
- @openssl_file = tmpfile('openssl', OPENSSL_URL)
23
- @openssl_file2 = tmpfile('openssl', OPENSSL_URL2)
24
- @gnutls_file = tmpfile('gnutls', GNUTLS_URL)
25
- @nss_file = tmpfile('nss', NSS_URL)
22
+ @iana_file = Utils.tmpfile('iana', IANA_URL)
23
+ @openssl_file = Utils.tmpfile('openssl', OPENSSL_URL)
24
+ @openssl_file2 = Utils.tmpfile('openssl', OPENSSL_URL2)
25
+ @gnutls_file = Utils.tmpfile('gnutls', GNUTLS_URL)
26
+ @nss_file = Utils.tmpfile('nss', NSS_URL)
26
27
 
27
28
  @tls_map = []
28
29
  parse
@@ -36,13 +37,13 @@ module TLSmap
36
37
  end
37
38
 
38
39
  # Search for corresponding cipher algorithms in other libraries
39
- # @param critera [Symbol] The type of +term+.
40
- # Accepted values: +:codepoint+, +:iana+, +:openssl+, +:gnutls+, +:nss+.
40
+ # @param critera [Symbol] The type of `term`.
41
+ # Accepted values: `:codepoint`, `:iana`, `:openssl`, `:gnutls`, `:nss`.
41
42
  # @param term [String] The cipher algorithm name.
42
43
  # @param output [Symbol] The corresponding type to be included in the return value.
43
- # Accepted values: +:all+ (default), +:codepoint+, +:iana+, +:openssl+,
44
- # +:gnutls+, +:nss+.
45
- # @return [Hash] The corresponding type matching +term+.
44
+ # Accepted values: `:all` (default), `:codepoint`, `:iana`, `:openssl`,
45
+ # `:gnutls`, `:nss`.
46
+ # @return [Hash] The corresponding type matching `term`.
46
47
  def search(critera, term, output = :all)
47
48
  @tls_map.each do |alg|
48
49
  term = term.upcase if critera == :codepoint
@@ -54,6 +55,23 @@ module TLSmap
54
55
  {}
55
56
  end
56
57
 
58
+ # Search for corresponding cipher algorithms in other libraries in bulk
59
+ # @param critera [Symbol] The type of `term`.
60
+ # Accepted values: `:codepoint`, `:iana`, `:openssl`, `:gnutls`, `:nss`.
61
+ # @param file [String] File containing the cipher algorithm names, one per line.
62
+ # @param output [Symbol] The corresponding type to be included in the return value.
63
+ # Accepted values: `:all` (default), `:codepoint`, `:iana`, `:openssl`,
64
+ # `:gnutls`, `:nss`.
65
+ # @return [Array<Hash>] The corresponding type, same as {search} return value
66
+ # but one per line stored in an array.
67
+ def bulk_search(critera, file, output = :all)
68
+ res = []
69
+ File.foreach(file) do |line|
70
+ res.push(search(critera, line.chomp, output))
71
+ end
72
+ res
73
+ end
74
+
57
75
  protected :parse
58
76
  end
59
77
  end
@@ -10,11 +10,13 @@ require 'yaml'
10
10
  module TLSmap
11
11
  class App
12
12
  # Partial wrapper around ciphersuite.info API to get extra info about a cipher
13
+ #
13
14
  # Documentation:
14
- # - https://ciphersuite.info/blog/2019/04/05/how-to-use-our-api/
15
- # - https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md
16
- # - https://ciphersuite.info/api/
17
- # - https://github.com/hcrudolph/ciphersuite.info
15
+ #
16
+ # - https://ciphersuite.info/blog/2019/04/05/how-to-use-our-api/
17
+ # - https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md
18
+ # - https://ciphersuite.info/api/
19
+ # - https://github.com/hcrudolph/ciphersuite.info
18
20
  class Extended
19
21
  # Root URL of Cipher Suite Info
20
22
  ROOT = 'https://ciphersuite.info/'
@@ -43,13 +45,10 @@ module TLSmap
43
45
  2 => { title: 'High', color: :red }
44
46
  }.freeze
45
47
 
46
- include Utils
47
- protected :tmpfile
48
-
49
48
  # Will automatically fetch source files and parse them.
50
49
  def initialize
51
- @tech_file = tmpfile('tech', TECH_DATA)
52
- @vuln_file = tmpfile('vuln', VULN_DATA)
50
+ @tech_file = Utils.tmpfile('tech', TECH_DATA)
51
+ @vuln_file = Utils.tmpfile('vuln', VULN_DATA)
53
52
  @tech = parse_tech
54
53
  @vuln = parse_vuln
55
54
  end
@@ -57,8 +56,8 @@ module TLSmap
57
56
  # Retrieve advanced about a cipher on Cipher Suite Info API and enhanced it
58
57
  # @param iana_name [String] IANA cipher name
59
58
  # @return [Hash] Hash containing advanced information. The keys are the same as {DICO}. All valeus are string
60
- # except +vulns+ which is an array of hashes containing two keys: +:severity+ (integer) and +:description+
61
- # (string). Each hash in +vulns+ correspond to a vulnerability.
59
+ # except `vulns` which is an array of hashes containing two keys: `:severity` (integer) and `:description`
60
+ # (string). Each hash in `vulns` correspond to a vulnerability.
62
61
  def extend(iana_name) # rubocop:disable Metrics/MethodLength
63
62
  obj = Net::HTTP.get(URI("#{API_ROOT}cs/#{iana_name}/"))
64
63
  out = JSON.parse(obj)[iana_name]
@@ -99,7 +98,7 @@ module TLSmap
99
98
 
100
99
  # Translate cipher related acronyms
101
100
  # @param term [String] Acronym, eg. DSS
102
- # @return [String] The long name of the acronym, eg. Digital Signature Standard or +nil+ if it's not found
101
+ # @return [String] The long name of the acronym, eg. Digital Signature Standard or `nil` if it's not found
103
102
  def translate_acronym(term)
104
103
  return @tech[term][:long_name] unless @tech[term].nil?
105
104
 
@@ -108,7 +107,7 @@ module TLSmap
108
107
 
109
108
  # Find vulnerabilities related to a technology
110
109
  # @param tech [String] The technology acronym, eg. CBC
111
- # @return [Array<Hash>] Array of vulnerabilities as described for {extend} return value in the +vulns+ key.
110
+ # @return [Array<Hash>] Array of vulnerabilities as described for {extend} return value in the `vulns` key.
112
111
  def find_vuln(tech)
113
112
  return @tech[tech][:vulnerabilities].map { |vuln| @vuln[vuln] } unless @tech[tech][:vulnerabilities].nil?
114
113
 
data/lib/tls_map/cli.rb CHANGED
@@ -9,7 +9,7 @@ module TLSmap
9
9
  class CLI < App
10
10
  INTEGRITY = '42e44f89550365da2bc8d33d87f88b65d85d6474e90f9edb65e0ea6c78f61a53' # sha2-256
11
11
 
12
- # Load and parse data from marshalized hash (+data/mapping.marshal+).
12
+ # Load and parse data from marshalized hash (`data/mapping.marshal`).
13
13
  # It must match the integrity check for security purpose.
14
14
  # @param force [Boolean] Force parsing even if intigrity check failed (DANGEROUS,
15
15
  # may result in command execution vulnerability)
@@ -31,7 +31,7 @@ module TLSmap
31
31
  end
32
32
 
33
33
  # Check if the password database exists
34
- # @return [Boolean] +true+ if the file exists
34
+ # @return [Boolean] `true` if the file exists
35
35
  def database_exists?
36
36
  exists = File.file?(@database_path)
37
37
  raise "Database does not exist: #{@database_path}" unless exists
@@ -0,0 +1,276 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Ruby internal
4
+ require 'json'
5
+ # Project internal
6
+ require 'tls_map/cli'
7
+ # External
8
+ require 'rexml/document'
9
+
10
+ module TLSmap
11
+ class App
12
+ # External tools output data extractor
13
+ #
14
+ # Output files from [SSLyze][1] (JSON), [sslscan2][2] (XML), [testssl.sh][3] (JSON), [ssllabs-scan][4] (JSON)
15
+ #
16
+ # [1]:https://github.com/nabla-c0d3/sslyze
17
+ # [2]:https://github.com/rbsec/sslscan
18
+ # [3]:https://github.com/drwetter/testssl.sh
19
+ # [4]:https://github.com/ssllabs/ssllabs-scan
20
+ #
21
+ # Example of commands:
22
+ #
23
+ # - `sslyze --json_out=example.org.json example.org`
24
+ # - `sslscan2 --show-cipher-ids --xml=example.org.xml example.org`
25
+ # - `--show-cipher-ids` is mandatory else ciphers are not saved to the output
26
+ # - `testssl --jsonfile-pretty example.org.json --mapping no-openssl --cipher-per-proto example.org`
27
+ # - json-pretty is the only supported format, default json or csv, html won't work
28
+ # - `ssllabs-scan --quiet example.org > example.org.json`
29
+ # - The default output is the only supported format, using `-json-flat` won't work
30
+ class Extractor
31
+ # Get the list of ciphers extracted from the tool output file
32
+ # @return [Array<String>] Cipher array (IANA names)
33
+ attr_reader :ciphers
34
+
35
+ # Initialize {TLSmap::App::Extractor} instance
36
+ def initialize
37
+ @ciphers = []
38
+ end
39
+
40
+ # Return only the SSL 2.0 ciphers
41
+ # @return [Array<String>] Cipher array (IANA names)
42
+ def ssl20
43
+ @ciphers['SSL2.0']
44
+ end
45
+
46
+ # Return only the SSL 3.0 ciphers
47
+ # @return [Array<String>] Cipher array (IANA names)
48
+ def ssl30
49
+ @ciphers['SSL3.0']
50
+ end
51
+
52
+ # Return only the TLS 1.0 ciphers
53
+ # @return [Array<String>] Cipher array (IANA names)
54
+ def tls10
55
+ @ciphers['TLS1.0']
56
+ end
57
+
58
+ # Return only the TLS 1.1 ciphers
59
+ # @return [Array<String>] Cipher array (IANA names)
60
+ def tls11
61
+ @ciphers['TLS1.1']
62
+ end
63
+
64
+ # Return only the TLS 1.2 ciphers
65
+ # @return [Array<String>] Cipher array (IANA names)
66
+ def tls12
67
+ @ciphers['TLS1.2']
68
+ end
69
+
70
+ # Return only the TLS 1.3 ciphers
71
+ # @return [Array<String>] Cipher array (IANA names)
72
+ def tls13
73
+ @ciphers['TLS1.3']
74
+ end
75
+
76
+ # Extract the ciphers from the tool output file
77
+ # @param tool [String] Possible values: `sslyze`, `sslscan2`, `testssl`, `ssllabs-scan`
78
+ # @param file [String] Path of the tool output file, beware of the format expected. See {TLSmap::App::Extractor}
79
+ # @return [Array<String>] Cipher array (IANA names)
80
+ def parse(tool, file)
81
+ # Convert string to class
82
+ @ciphers = Object.const_get("TLSmap::App::Extractor::#{normalize(tool)}").parse(file)
83
+ rescue StandardError
84
+ warn helper(tool)
85
+ end
86
+
87
+ # Commands for {helper}
88
+ CMD = {
89
+ 'sslyze' => 'sslyze --json_out=example.org.json example.org',
90
+ 'sslscan2' => 'sslscan2 --show-cipher-ids --xml=example.org.xml example.org',
91
+ 'testssl' => 'testssl --jsonfile-pretty example.org.json --mapping no-openssl --cipher-per-proto example.org',
92
+ 'ssllabs-scan' => 'ssllabs-scan --quiet example.org > example.org.json'
93
+ }.freeze
94
+
95
+ # Get the external tool command used to generate the expected result format
96
+ # @param tool [String] Possible values: `sslyze`, `sslscan2`, `testssl`, `ssllabs-scan`
97
+ # @return [String] external tool command used to generate the expected result format used in input of the extract
98
+ # command (CLI) / {parse} method (library)
99
+ def helper(tool)
100
+ intro = 'You may not be provinding the right format.'
101
+ outro = 'See https://sec-it.github.io/tls-map/yard/TLSmap/App/Extractor'
102
+ "#{intro}\nUse this command: #{CMD[tool]}\n#{outro}"
103
+ end
104
+
105
+ # Convert cmdline tool name to Class name
106
+ def normalize(tool)
107
+ tool.split('-').map(&:capitalize).join
108
+ end
109
+
110
+ protected :normalize, :helper
111
+ private_constant :CMD
112
+
113
+ # Parsing SSLyze
114
+ class Sslyze
115
+ class << self
116
+ # Extract the ciphers from the sslyze output file
117
+ # @param file [String] Path of the sslyze output file, beware of the format expected.
118
+ # See {TLSmap::App::Extractor}
119
+ # @return [Array<String>] Cipher array (IANA names)
120
+ def parse(file)
121
+ data = Utils.json_load_file(file)
122
+ extract_cipher(data)
123
+ end
124
+
125
+ # Extract the ciphers from the sslyze output file
126
+ # @param json_data [Hash] Ruby hash of the parsed JSON
127
+ # @return [Array<String>] Cipher array (IANA names)
128
+ def extract_cipher(json_data)
129
+ ciphers = json_data['server_scan_results'][0]['scan_commands_results']
130
+ raw = {
131
+ 'SSL2.0' => ciphers['ssl_2_0_cipher_suites']['accepted_cipher_suites'],
132
+ 'SSL3.0' => ciphers['ssl_3_0_cipher_suites']['accepted_cipher_suites'],
133
+ 'TLS1.0' => ciphers['tls_1_0_cipher_suites']['accepted_cipher_suites'],
134
+ 'TLS1.1' => ciphers['tls_1_1_cipher_suites']['accepted_cipher_suites'],
135
+ 'TLS1.2' => ciphers['tls_1_2_cipher_suites']['accepted_cipher_suites'],
136
+ 'TLS1.3' => ciphers['tls_1_3_cipher_suites']['accepted_cipher_suites']
137
+ }
138
+ raw.transform_values { |v| v.empty? ? v : v.map { |x| x['cipher_suite']['name'] } }
139
+ end
140
+
141
+ protected :extract_cipher
142
+ end
143
+ end
144
+
145
+ # Parsing sslscan2
146
+ class Sslscan2
147
+ class << self
148
+ # Extract the ciphers from the sslscan2 output file
149
+ # @param file [String] Path of the sslscan2 output file, beware of the format expected.
150
+ # See {TLSmap::App::Extractor}
151
+ # @return [Array<String>] Cipher array (IANA names)
152
+ def parse(file, online = false)
153
+ doc = REXML::Document.new(File.new(file))
154
+ extract_cipher(doc, online)
155
+ end
156
+
157
+ # Extract the ciphers from the sslscan2 output file
158
+ # @param xml_doc [REXML::Document] XML document as returned by `REXML::Document`
159
+ # @param online By default use the offline mode with {TLSmap::CLI} for better performance.
160
+ # Online mode will use {TLSmap::App} and fetch upstream resources to get latest updates but is a lot slower.
161
+ # @return [Array<String>] Cipher array (IANA names)
162
+ def extract_cipher(xml_doc, online = false) # rubocop:disable Metrics/MethodLength
163
+ raw = {
164
+ 'SSL2.0' => [], 'SSL3.0' => [],
165
+ 'TLS1.0' => [], 'TLS1.1' => [], 'TLS1.2' => [], 'TLS1.3' => []
166
+ }
167
+ tm = online ? TLSmap::App.new : TLSmap::CLI.new
168
+ xml_doc.root.each_element('//cipher') do |node|
169
+ sslv = node.attributes['sslversion'].gsub('v', '')
170
+ cipher = tm.search(:codepoint, node.attributes['id'][2..], :iana)[:iana]
171
+ raw[sslv].push(cipher)
172
+ end
173
+ raw
174
+ end
175
+
176
+ protected :extract_cipher
177
+ end
178
+ end
179
+
180
+ # Parsing testssl.sh
181
+ class Testssl
182
+ class << self
183
+ # Extract the ciphers from the testssl output file
184
+ # @param file [String] Path of the testssl output file, beware of the format expected.
185
+ # See {TLSmap::App::Extractor}
186
+ # @return [Array<String>] Cipher array (IANA names)
187
+ def parse(file)
188
+ data = Utils.json_load_file(file)
189
+ extract_cipher(data)
190
+ end
191
+
192
+ # Extract the ciphers from the testssl output file
193
+ # @param json_data [Hash] Ruby hash of the parsed JSON
194
+ # @return [Array<String>] Cipher array (IANA names)
195
+ def extract_cipher(json_data)
196
+ cipher = json_data['scanResult'][0]['cipherTests']
197
+ raw = {
198
+ 'SSL2.0' => [], 'SSL3.0' => [],
199
+ 'TLS1.0' => [], 'TLS1.1' => [], 'TLS1.2' => [], 'TLS1.3' => []
200
+ }
201
+ cipher.each do |node|
202
+ raw[id2prot(node['id'])].push(finding2cipher(node['finding']))
203
+ end
204
+ raw
205
+ end
206
+
207
+ # Convert testssl protocol id to protocol name in TLSmap format
208
+ # @param id [String] testssl protocol id
209
+ # @return [String] protocol name in TLSmap format
210
+ def id2prot(id)
211
+ prot = {
212
+ 'ssl2' => 'SSL2.0', 'ssl3' => 'SSL3.0', 'tls1' => 'TLS1.0',
213
+ 'tls1_1' => 'TLS1.1', 'tls1_2' => 'TLS1.2', 'tls1_3' => 'TLS1.3'
214
+ }
215
+ protv = id.match(/cipher-(\w+)_x\w+/).captures[0]
216
+ prot[protv]
217
+ end
218
+
219
+ # Extract the cipher name from testssl finding
220
+ # @param finding [String] testssl finding
221
+ # @return [String] cipher name (IANA names)
222
+ def finding2cipher(finding)
223
+ /\s(\w+_\w+)\s/.match(finding).captures[0]
224
+ end
225
+
226
+ protected :extract_cipher, :id2prot, :finding2cipher
227
+ end
228
+ end
229
+
230
+ # Parsing ssllabs-scan
231
+ class SsllabsScan
232
+ class << self
233
+ # Extract the ciphers from the ssllabs-scan output file
234
+ # @param file [String] Path of the ssllabs-scan output file, beware of the format expected.
235
+ # See {TLSmap::App::Extractor}
236
+ # @return [Array<String>] Cipher array (IANA names)
237
+ def parse(file)
238
+ data = Utils.json_load_file(file)
239
+ extract_cipher(data)
240
+ end
241
+
242
+ # Extract the ciphers from the ssllabs-scan output file
243
+ # @param json_data [Hash] Ruby hash of the parsed JSON
244
+ # @return [Array<String>] Cipher array (IANA names)
245
+ def extract_cipher(json_data) # rubocop:disable Metrics/MethodLength
246
+ raw = {
247
+ 'SSL2.0' => [], 'SSL3.0' => [],
248
+ 'TLS1.0' => [], 'TLS1.1' => [], 'TLS1.2' => [], 'TLS1.3' => []
249
+ }
250
+ json_data[0]['endpoints'].each do |endpoint|
251
+ endpoint['details']['suites'].each do |suite|
252
+ suite['list'].each do |cipher|
253
+ raw[id2prot(suite['protocol'])].push(cipher['name'])
254
+ end
255
+ end
256
+ end
257
+ raw.transform_values(&:uniq)
258
+ end
259
+
260
+ # Convert ssllabs-scan protocol id to protocol name in TLSmap format
261
+ # @param id [String] ssllabs-scan protocol id
262
+ # @return [String] protocol name in TLSmap format
263
+ def id2prot(id)
264
+ prot = {
265
+ 512 => 'SSL2.0', 768 => 'SSL3.0', 769 => 'TLS1.0',
266
+ 770 => 'TLS1.1', 771 => 'TLS1.2', 772 => 'TLS1.3'
267
+ }
268
+ prot[id]
269
+ end
270
+
271
+ protected :extract_cipher, :id2prot
272
+ end
273
+ end
274
+ end
275
+ end
276
+ end
@@ -35,9 +35,9 @@ module TLSmap
35
35
 
36
36
  # Export the mapping to a file, supporting various formats.
37
37
  # @param filename [String] The output file name to write to.
38
- # @param format [Symbol] Supported formats: +:markdown+ (a markdown table),
39
- # +:json_pretty+ (expanded JSON), +:json_compact+ (minified JSON),
40
- # +:marshal+ (Ruby marshalized hash).
38
+ # @param format [Symbol] Supported formats: `:markdown` (a markdown table),
39
+ # `:json_pretty` (expanded JSON), `:json_compact` (minified JSON),
40
+ # `:marshal` (Ruby marshalized hash).
41
41
  def export(filename, format)
42
42
  case format
43
43
  when :markdown then output_markdown(filename)
data/lib/tls_map/utils.rb CHANGED
@@ -3,22 +3,27 @@
3
3
  # Ruby internal
4
4
  require 'net/http'
5
5
  require 'tempfile'
6
+ require 'json'
6
7
 
7
8
  # TLS map module
8
9
  module TLSmap
9
10
  # Generic utilities
10
11
  module Utils
11
- def tmpfile(name, url)
12
+ def self.tmpfile(name, url)
12
13
  tmp = Tempfile.new(name)
13
14
  tmp.write(Net::HTTP.get(URI(url)))
14
15
  tmp.close
15
16
  tmp
16
17
  end
17
- end
18
18
 
19
- # TLS mapping
20
- class App
21
- include Utils
22
- protected :tmpfile
19
+ # bring JSON.load_file before ruby 3.0.0
20
+ # https://ruby-doc.org/stdlib-3.0.0/libdoc/json/rdoc/JSON.html#method-i-load_file
21
+ def self.json_load_file(filespec, opts = {})
22
+ if RUBY_VERSION < '3.0.0'
23
+ JSON.parse(File.read(filespec), opts)
24
+ else
25
+ JSON.load_file(filespec, opts)
26
+ end
27
+ end
23
28
  end
24
29
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TLSmap
4
- VERSION = '1.1.0'
4
+ VERSION = '1.3.2'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tls-map
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexandre ZANNI
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-25 00:00:00.000000000 Z
11
+ date: 2021-07-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: docopt
@@ -39,95 +39,19 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '2.2'
41
41
  - !ruby/object:Gem::Dependency
42
- name: bundler
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: 2.1.0
48
- - - "<"
49
- - !ruby/object:Gem::Version
50
- version: '2.3'
51
- type: :development
52
- prerelease: false
53
- version_requirements: !ruby/object:Gem::Requirement
54
- requirements:
55
- - - ">="
56
- - !ruby/object:Gem::Version
57
- version: 2.1.0
58
- - - "<"
59
- - !ruby/object:Gem::Version
60
- version: '2.3'
61
- - !ruby/object:Gem::Dependency
62
- name: commonmarker
63
- requirement: !ruby/object:Gem::Requirement
64
- requirements:
65
- - - "~>"
66
- - !ruby/object:Gem::Version
67
- version: '0.21'
68
- type: :development
69
- prerelease: false
70
- version_requirements: !ruby/object:Gem::Requirement
71
- requirements:
72
- - - "~>"
73
- - !ruby/object:Gem::Version
74
- version: '0.21'
75
- - !ruby/object:Gem::Dependency
76
- name: github-markup
42
+ name: rexml
77
43
  requirement: !ruby/object:Gem::Requirement
78
44
  requirements:
79
45
  - - "~>"
80
46
  - !ruby/object:Gem::Version
81
- version: '4.0'
82
- type: :development
83
- prerelease: false
84
- version_requirements: !ruby/object:Gem::Requirement
85
- requirements:
86
- - - "~>"
87
- - !ruby/object:Gem::Version
88
- version: '4.0'
89
- - !ruby/object:Gem::Dependency
90
- name: redcarpet
91
- requirement: !ruby/object:Gem::Requirement
92
- requirements:
93
- - - "~>"
94
- - !ruby/object:Gem::Version
95
- version: '3.5'
96
- type: :development
97
- prerelease: false
98
- version_requirements: !ruby/object:Gem::Requirement
99
- requirements:
100
- - - "~>"
101
- - !ruby/object:Gem::Version
102
- version: '3.5'
103
- - !ruby/object:Gem::Dependency
104
- name: rubocop
105
- requirement: !ruby/object:Gem::Requirement
106
- requirements:
107
- - - "~>"
108
- - !ruby/object:Gem::Version
109
- version: '1.10'
110
- type: :development
111
- prerelease: false
112
- version_requirements: !ruby/object:Gem::Requirement
113
- requirements:
114
- - - "~>"
115
- - !ruby/object:Gem::Version
116
- version: '1.10'
117
- - !ruby/object:Gem::Dependency
118
- name: yard
119
- requirement: !ruby/object:Gem::Requirement
120
- requirements:
121
- - - "~>"
122
- - !ruby/object:Gem::Version
123
- version: '0.9'
124
- type: :development
47
+ version: '3.2'
48
+ type: :runtime
125
49
  prerelease: false
126
50
  version_requirements: !ruby/object:Gem::Requirement
127
51
  requirements:
128
52
  - - "~>"
129
53
  - !ruby/object:Gem::Version
130
- version: '0.9'
54
+ version: '3.2'
131
55
  description: 'CLI & library for mapping TLS cipher algorithm names: IANA, OpenSSL,
132
56
  GnuTLS, NSS'
133
57
  email: alexandre.zanni@engineer.com
@@ -147,6 +71,7 @@ files:
147
71
  - lib/tls_map.rb
148
72
  - lib/tls_map/ciphersuiteinfo.rb
149
73
  - lib/tls_map/cli.rb
74
+ - lib/tls_map/extractor.rb
150
75
  - lib/tls_map/gnutls.rb
151
76
  - lib/tls_map/iana.rb
152
77
  - lib/tls_map/nss.rb