tls-map 1.1.0 → 1.3.2

Sign up to get free protection for your applications and to get access to all the features.
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