Browse Source

Move up host resolving for speed ehanchement

master
Aeris 3 years ago
parent
commit
c1499fc6e6

+ 4
- 8
bin/check_https.rb View File

@@ -6,14 +6,10 @@ require 'cryptcheck'
6 6
 
7 7
 name = ARGV[0] || 'index'
8 8
 file = ::File.join 'output', "#{name}.yml"
9
-
10 9
 if ::File.exist? file
11
-	::CryptCheck::Logger.level = :none
12
-	::CryptCheck::Tls::Https.analyze_from_file "output/#{name}.yml", "output/#{name}.html"
10
+	::CryptCheck::Logger.level = ENV['LOG'] || :none
11
+	::CryptCheck::Tls::Https.analyze_file file, "output/#{name}.html"
13 12
 else
14
-	::CryptCheck::Logger.level = (ARGV[1] || :info).to_sym
15
-	server = ::CryptCheck::Tls::Https::Server.new ARGV[0]
16
-	grade = ::CryptCheck::Tls::Https::Grade.new server
17
-	::CryptCheck::Logger.info { '' }
18
-	grade.display
13
+	::CryptCheck::Logger.level = ENV['LOG'] || :info
14
+	::CryptCheck::Tls::Https.analyze ARGV[0], (ARGV[1] || 443)
19 15
 end

+ 0
- 1
bin/check_https_alexa View File

@@ -1 +0,0 @@
1
-runner

+ 0
- 21
bin/check_https_alexa.rb View File

@@ -1,21 +0,0 @@
1
-#!/usr/bin/env ruby
2
-$:.unshift File.expand_path File.join File.dirname(__FILE__), '../lib'
3
-require 'rubygems'
4
-require 'bundler/setup'
5
-require 'cryptcheck'
6
-
7
-GROUP_NAME = 'Top 100 Alexa'
8
-
9
-::CryptCheck::Logger.level = :none
10
-
11
-hosts = []
12
-::File.open('top-1m.csv', 'r') do |file|
13
-	i = 0
14
-	while line = file.gets
15
-		hosts << [GROUP_NAME, line.strip.split(',')[1]]
16
-		i += 1
17
-		break if i == 100
18
-	end
19
-end
20
-
21
-::CryptCheck::Tls::Https.analyze hosts, 'output/alexa.html'

+ 7
- 9
bin/check_smtp.rb View File

@@ -4,16 +4,14 @@ require 'rubygems'
4 4
 require 'bundler/setup'
5 5
 require 'cryptcheck'
6 6
 
7
-name = ARGV[0]
8
-if name
9
-	::CryptCheck::Logger.level = (ARGV[1] || :info).to_sym
10
-	server = ::CryptCheck::Tls::Smtp::Server.new ARGV[0]
11
-	grade = ::CryptCheck::Tls::Smtp::Grade.new server
12
-	::CryptCheck::Logger.info { '' }
13
-	grade.display
7
+name = ARGV[0] || 'smtp'
8
+file = ::File.join 'output', "#{name}.yml"
9
+if ::File.exist? file
10
+	::CryptCheck::Logger.level = ENV['LOG'] || :none
11
+	::CryptCheck::Tls::Smtp.analyze_file file, "output/#{name}.html"
14 12
 else
15
-	::CryptCheck::Logger.level = :none
16
-	::CryptCheck::Tls::Smtp.analyze_from_file 'output/smtp.yml', 'output/smtp.html'
13
+	::CryptCheck::Logger.level = ENV['LOG'] || :info
14
+	::CryptCheck::Tls::Smtp.analyze ARGV[0]
17 15
 end
18 16
 
19 17
 

+ 2
- 11
bin/check_ssh.rb View File

@@ -4,14 +4,5 @@ require 'rubygems'
4 4
 require 'bundler/setup'
5 5
 require 'cryptcheck'
6 6
 
7
-name, port, level = case ARGV.length
8
-	when 1 then [ARGV[0], 22, :info]
9
-	when 2 then [ARGV[0], ARGV[1], :info]
10
-	when 3 then [ARGV[0], ARGV[1], ARGV[2]]
11
-end
12
-
13
-::CryptCheck::Logger.level = level
14
-server = ::CryptCheck::Ssh::Server.new name, port
15
-#grade = ::CryptCheck::Ssh::Grade.new server
16
-#::CryptCheck::Logger.info { '' }
17
-#grade.display
7
+::CryptCheck::Logger.level = ENV['LOG'] || :info
8
+::CryptCheck::Ssh.analyze ARGV[0], (ARGV[1] || 22)

+ 2
- 5
bin/check_tls.rb View File

@@ -4,8 +4,5 @@ require 'rubygems'
4 4
 require 'bundler/setup'
5 5
 require 'cryptcheck'
6 6
 
7
-::CryptCheck::Logger.level = (ARGV[2] || :info).to_sym
8
-server = ::CryptCheck::Tls::TcpServer.new ARGV[0], ARGV[1]
9
-grade = ::CryptCheck::Tls::Grade.new server
10
-::CryptCheck::Logger.info { '' }
11
-grade.display
7
+::CryptCheck::Logger.level = ENV['LOG'] || :info
8
+::CryptCheck::Tls::analyze ARGV[0], ARGV[1]

+ 7
- 15
bin/check_xmpp.rb View File

@@ -4,20 +4,12 @@ require 'rubygems'
4 4
 require 'bundler/setup'
5 5
 require 'cryptcheck'
6 6
 
7
-name, type, level = case ARGV.length
8
-						when 1 then [ARGV[0], :s2s, :info]
9
-						when 2 then [ARGV[0], ARGV[1].to_sym, :info]
10
-						when 3 then [ARGV[0], ARGV[1].to_sym, ARGV[2].to_sym]
11
-					end
12
-
13
-if name
14
-	::CryptCheck::Logger.level = level
15
-	server = ::CryptCheck::Tls::Xmpp::Server.new name, type
16
-	grade = ::CryptCheck::Tls::Xmpp::Grade.new server
17
-	::CryptCheck::Logger.info { '' }
18
-	grade.display
7
+name = ARGV[0] || 'xmpp'
8
+file = ::File.join 'output', "#{name}.yml"
9
+if ::File.exist? file
10
+	::CryptCheck::Logger.level = ENV['LOG'] || :none
11
+	::CryptCheck::Tls::Xmpp.analyze_file file, "output/#{name}.html"
19 12
 else
20
-	::CryptCheck::Logger.level = :none
21
-	::CryptCheck::Tls::Xmpp.analyze_from_file 'output/xmpp.yml', 'output/xmpp.html'
13
+	::CryptCheck::Logger.level = ENV['LOG'] || :info
14
+	::CryptCheck::Tls::Xmpp.analyze ARGV[0], type: (ARGV[1] || :s2s).to_sym
22 15
 end
23
-

+ 93
- 0
lib/cryptcheck.rb View File

@@ -1,7 +1,13 @@
1 1
 require 'colorize'
2
+require 'ipaddr'
3
+require 'timeout'
4
+require 'yaml'
2 5
 require 'cryptcheck/tls/fixture'
3 6
 
4 7
 module CryptCheck
8
+	MAX_ANALYSIS_DURATION = 600
9
+	PARALLEL_ANALYSIS     = 10
10
+
5 11
 	autoload :Logger, 'cryptcheck/logger'
6 12
 	autoload :Tls, 'cryptcheck/tls'
7 13
 	module Tls
@@ -32,9 +38,96 @@ module CryptCheck
32 38
 		end
33 39
 	end
34 40
 
41
+	autoload :Ssh, 'cryptcheck/ssh'
35 42
 	module Ssh
36 43
 		autoload :Packet, 'cryptcheck/ssh/packet'
37 44
 		autoload :Server, 'cryptcheck/ssh/server'
38 45
 		autoload :SshNotSupportedServer, 'cryptcheck/ssh/server'
46
+		autoload :Grade, 'cryptcheck/ssh/grade'
47
+	end
48
+
49
+	private
50
+	def self.addresses(host)
51
+		begin
52
+			ip = IPAddr.new host
53
+			[[ip.family, ip.to_s, nil]]
54
+		rescue IPAddr::InvalidAddressError
55
+			begin
56
+				::Addrinfo.getaddrinfo(host, nil, nil, :STREAM)
57
+						.collect { |a| [a.afamily, a.ip_address, host] }
58
+			rescue ::SocketError => e
59
+				Logger.error { "Unable to resolv #{host} : #{e}" }
60
+				[]
61
+			end
62
+		end
63
+	end
64
+
65
+	def self.analyze_addresses(host, addresses, port, on_error = TLS_NOT_AVAILABLE, &block)
66
+		begin
67
+			::Timeout::timeout MAX_ANALYSIS_DURATION do
68
+				addresses.each { |family, ip, host| return block.call family, ip, host }
69
+			end
70
+		rescue ::Exception => e
71
+			Logger.error e
72
+		end
73
+		on_error.call host, port
74
+	end
75
+
76
+	def self.analyze(host, port, on_error = Tls::TLS_NOT_AVAILABLE, &block)
77
+		analyze_addresses host, addresses(host), port, on_error, &block
78
+	end
79
+
80
+	def self.analyze_hosts(hosts, template, output, groups: nil, &block)
81
+		results   = {}
82
+		semaphore = ::Mutex.new
83
+		::Parallel.each hosts, progress: 'Analysing', in_threads: PARALLEL_ANALYSIS, finish: lambda { |item, _, _| puts item[1] } do |description, host|
84
+		#hosts.each do |description, host|
85
+			result = block.call host.strip
86
+			semaphore.synchronize do
87
+				if results.include? description
88
+					results[description] << result
89
+				else
90
+					results[description] = [result]
91
+				end
92
+			end
93
+		end
94
+
95
+		results = ::Hash[groups.collect { |g| [g, results[g]] }] if groups
96
+
97
+		results.each do |d, _|
98
+			results[d].sort! do |a, b|
99
+				cmp = score(a) <=> score(b)
100
+				if cmp == 0
101
+					cmp = b.score <=> a.score
102
+					if cmp == 0
103
+						cmp = a.server.hostname <=> b.server.hostname
104
+					end
105
+				end
106
+				cmp
107
+			end
108
+		end
109
+
110
+		::File.write output, ::ERB.new(::File.read template).result(binding)
111
+	end
112
+
113
+	def self.analyze_file(input, template, output, &block)
114
+		config = ::YAML.load_file input
115
+		hosts  = []
116
+		groups = []
117
+
118
+		config.each do |c|
119
+			d, hs = c['description'], c['hostnames']
120
+			groups << d
121
+			hs.each { |host| hosts << [d, host] }
122
+		end
123
+
124
+		self.analyze_hosts hosts, template, output, groups: groups, &block
125
+	end
126
+
127
+	private
128
+	SCORES = %w(A+ A A- B C D E F T M X)
129
+
130
+	def self.score(a)
131
+		SCORES.index a.grade
39 132
 	end
40 133
 end

+ 1
- 1
lib/cryptcheck/logger.rb View File

@@ -4,7 +4,7 @@ module CryptCheck
4 4
 		@@level = :info
5 5
 
6 6
 		def self.level=(level)
7
-			@@level = level
7
+			@@level = level.to_sym
8 8
 		end
9 9
 
10 10
 		def self.log(level, string=nil, output: $stdout, &block)

+ 9
- 0
lib/cryptcheck/ssh.rb View File

@@ -0,0 +1,9 @@
1
+module CryptCheck
2
+	module Ssh
3
+		def self.analyze(host, port=22)
4
+			::CryptCheck.analyze(host, port, Proc.new { SshNotSupportedServer.new host, port }) do |_, ip, host|
5
+				Server.new ip, port, hostname: host
6
+			end
7
+		end
8
+	end
9
+end

+ 0
- 134
lib/cryptcheck/ssh/grade.rb View File

@@ -1,134 +0,0 @@
1
-module CryptCheck
2
-	module Ssh
3
-		class SshNotSupportedGrade
4
-			attr_reader :server, :score, :grade
5
-
6
-			def initialize(server)
7
-				@server, @score, @grade = server, -1, 'X'
8
-			end
9
-		end
10
-
11
-		class Grade
12
-			attr_reader :server, :protocol_score, :key_exchange_score, :cipher_strengths_score, :score, :grade, :error, :warning, :success
13
-
14
-			def initialize(server)
15
-				@server = server
16
-			end
17
-
18
-			def display
19
-				color = case self.grade
20
-							when 'A+' then :blue
21
-							when 'A' then :green
22
-							when 'B', 'C' then :yellow
23
-							when 'E', 'F' then :red
24
-							when 'M', 'T' then { color: :white, background: :red }
25
-						end
26
-
27
-				Logger.info { "Grade : #{self.grade.colorize color }" }
28
-				Logger.info { '' }
29
-				Logger.info { "Protocole : #{self.protocol_score} / 100" }
30
-				Logger.info { "Key exchange : #{self.key_exchange_score} / 100" }
31
-				Logger.info { "Ciphers strength : #{self.cipher_strengths_score} / 100" }
32
-				Logger.info { "Overall score : #{self.score} / 100" }
33
-				Logger.info { '' }
34
-				Logger.info { "Errors : #{self.error.join(' ').colorize :red }" } unless self.error.empty?
35
-				Logger.info { "Warnings : #{self.warning.join(' ').colorize :yellow }" } unless self.warning.empty?
36
-				Logger.info { "Best practices : #{self.success.join(' ').colorize :green }" } unless self.success.empty?
37
-			end
38
-
39
-			private
40
-			def calculate_grade
41
-				@grade = case @score
42
-							 when 0...20 then 'F'
43
-							 when 20...35 then 'E'
44
-							 when 35...50 then 'D'
45
-							 when 50...65 then 'C'
46
-							 when 65...80 then 'B'
47
-							 else 'A'
48
-						 end
49
-
50
-				@grade = [@grade, 'B'].max if !@server.tlsv1_2? or @server.key_size < 2048
51
-				@grade = [@grade, 'C'].max if @server.des3?
52
-				@grade = [@grade, 'F'].max unless @error.empty?
53
-
54
-				@grade = 'M' unless @server.cert_valid
55
-				@grade = 'T' unless @server.cert_trusted
56
-
57
-				@grade = 'A+' if @grade == 'A' and @error.empty? and @warning.empty? and (all_success & @success) == all_success
58
-			end
59
-
60
-			def calculate_error
61
-				@error = []
62
-
63
-				@error << :md5_sig if @server.md5_sig?
64
-				@error << :sslv2 if @server.sslv2?
65
-				@error << :sslv3 if @server.sslv3?
66
-
67
-				@error << :md5 if @server.md5?
68
-				@error << :anonymous if @server.anonymous?
69
-				@error << :dss if @server.dss?
70
-
71
-				@error << :null if @server.null?
72
-				@error << :export if @server.export?
73
-				@error << :des if @server.des?
74
-				@error << :rc4 if @server.rc4?
75
-			end
76
-
77
-			def calculate_warning
78
-				@warning = []
79
-
80
-				@warning << :sha1_sig if @server.sha1_sig?
81
-
82
-				#@warning << :sha1 if @server.sha1?
83
-
84
-				@warning << :des3 if @server.des3?
85
-			end
86
-
87
-			def calculate_success
88
-				@success = []
89
-				@success << :pfs if @server.pfs_only?
90
-			end
91
-
92
-			ALL_ERROR = %i(md5_sig md5 anonymous dss null export des rc4)
93
-			def all_error
94
-				ALL_ERROR
95
-			end
96
-
97
-			ALL_WARNING = %i(sha1_sig des3)
98
-			def all_warning
99
-				ALL_WARNING
100
-			end
101
-
102
-			ALL_SUCCESS = %i(pfs)
103
-			def all_success
104
-				ALL_SUCCESS
105
-			end
106
-
107
-			METHODS_SCORES = { SSLv2: 0, SSLv3: 20, TLSv1: 60, TLSv1_1: 80, TLSv1_2: 100 }
108
-			def calculate_protocol_score
109
-				@protocol_score = @server.supported_protocols.collect { |p| METHODS_SCORES[p] }.min
110
-			end
111
-
112
-			def calculate_key_exchange_score
113
-				@key_exchange_score = case @server.key_size
114
-										  when 0 then 0
115
-										  when 0...512 then 10
116
-										  when 512...1024 then 20
117
-										  when 1024...2048 then 50
118
-										  when 2048...4096 then 90
119
-										  else 100
120
-									  end
121
-			end
122
-
123
-			def calculate_cipher_strengths_score
124
-				@cipher_strengths_score = case @server.cipher_size
125
-					when 0 then 0
126
-					when 0...112 then 10
127
-					when 112...128 then 50
128
-					when 128...256 then 90
129
-					else 100
130
-				end
131
-			end
132
-		end
133
-	end
134
-end

+ 15
- 8
lib/cryptcheck/ssh/server.rb View File

@@ -3,10 +3,10 @@ require 'socket'
3 3
 module CryptCheck
4 4
 	module Ssh
5 5
 		class SshNotSupportedServer
6
-			attr_reader :hostname, :port
6
+			attr_reader :host, :port
7 7
 
8
-			def initialize(hostname, port)
9
-				@hostname, @port = hostname, port
8
+			def initialize(host, port)
9
+				@host, @port = host, port
10 10
 			end
11 11
 		end
12 12
 
@@ -15,7 +15,7 @@ module CryptCheck
15 15
 			class SshNotAvailableException < Exception
16 16
 			end
17 17
 
18
-			attr_reader :hostname, :port, :kex, :encryption, :hmac, :compression, :key
18
+			attr_reader :ip, :port, :hostname, :kex, :encryption, :hmac, :compression, :key
19 19
 
20 20
 			KEX = {
21 21
 					'curve25519-sha256@libssh.org'         => :green,
@@ -89,11 +89,11 @@ module CryptCheck
89 89
 					'ssh-dss-cert-v00@openssh.com'             => :red,		# DSA
90 90
 			}
91 91
 
92
-			def initialize(hostname, port=22)
93
-				@hostname, @port = hostname, port
92
+			def initialize(ip, port=22, hostname:)
93
+				@ip, @port, @hostname = ip, port, hostname
94 94
 
95
-				Logger.info { "#{hostname}:#{port}".colorize :blue }
96
-				kex = ::Socket.tcp hostname, port, connect_timeout: TCP_TIMEOUT do |socket|
95
+				Logger.info { name.colorize :blue }
96
+				kex = ::Socket.tcp ip, port, connect_timeout: TCP_TIMEOUT do |socket|
97 97
 					socket.readline
98 98
 					socket.write "SSH-2.0-CryptCheck\r\n"
99 99
 					Packet.read_kex_init socket
@@ -115,6 +115,13 @@ module CryptCheck
115 115
 				Logger.debug { "SSH not supported : #{e}" }
116 116
 				raise SshNotAvailableException, e
117 117
 			end
118
+
119
+			private
120
+			def name
121
+				name = "#{@hostname || @ip}:#@port"
122
+				name += " [#@ip]" if @hostname
123
+				name
124
+			end
118 125
 		end
119 126
 	end
120 127
 end

+ 11
- 60
lib/cryptcheck/tls.rb View File

@@ -3,60 +3,18 @@ require 'parallel'
3 3
 
4 4
 module CryptCheck
5 5
 	module Tls
6
-		MAX_ANALYSIS_DURATION = 600
7
-		PARALLEL_ANALYSIS     = 10
8
-
9
-		def self.grade(hostname, port, server_class:, grade_class:)
10
-			timeout MAX_ANALYSIS_DURATION do
11
-				grade_class.new server_class.new hostname, port
6
+		TLS_NOT_AVAILABLE = Proc.new { |host, port|
7
+			TlsNotSupportedGrade.new TlsNotSupportedServer.new host, port
8
+		}
9
+
10
+		def self.analyze(host, port)
11
+			::CryptCheck.analyze host, port do |family, ip, host|
12
+				s = TcpServer.new family, ip, port, hostname: host
13
+				g = Grade.new s
14
+				Logger.info { '' }
15
+				g.display
16
+				g
12 17
 			end
13
-		rescue ::Exception => e
14
-			Logger.error { "Error during #{hostname}:#{port} analysis : #{e}" }
15
-			TlsNotSupportedGrade.new TlsNotSupportedServer.new hostname, port
16
-		end
17
-
18
-		def self.analyze(hosts, template, output, groups = nil, port:, server_class:, grade_class:)
19
-			results   = {}
20
-			semaphore = ::Mutex.new
21
-			::Parallel.each hosts, progress: 'Analysing', in_threads: PARALLEL_ANALYSIS, finish: lambda { |item, _, _| puts item[1] } do |description, host|
22
-									 result = grade host.strip, port, server_class: server_class, grade_class: grade_class
23
-									 semaphore.synchronize do
24
-										 if results.include? description
25
-											 results[description] << result
26
-										 else
27
-											 results[description] = [result]
28
-										 end
29
-									 end
30
-								 end
31
-
32
-			results = ::Hash[groups.collect { |g| [g, results[g]] }] if groups
33
-
34
-			results.each do |d, _|
35
-				results[d].sort! do |a, b|
36
-					cmp = score(a) <=> score(b)
37
-					if cmp == 0
38
-						cmp = b.score <=> a.score
39
-						if cmp == 0
40
-							cmp = a.server.hostname <=> b.server.hostname
41
-						end
42
-					end
43
-					cmp
44
-				end
45
-			end
46
-
47
-			::File.write output, ::ERB.new(::File.read(template)).result(binding)
48
-		end
49
-
50
-		def self.analyze_from_file(file, template, output, port:, server_class:, grade_class:)
51
-			config = ::YAML.load_file file
52
-			hosts  = []
53
-			groups = []
54
-			config.each do |c|
55
-				d, hs = c['description'], c['hostnames']
56
-				groups << d
57
-				hs.each { |host| hosts << [d, host] }
58
-			end
59
-			self.analyze hosts, template, output, groups, port: port, server_class: server_class, grade_class: grade_class
60 18
 		end
61 19
 
62 20
 		def self.colorize(cipher)
@@ -80,12 +38,5 @@ module CryptCheck
80 38
 						 end
81 39
 			"#{key.type.to_s.upcase.colorize type_color} #{key.size.to_s.colorize size_color} bits"
82 40
 		end
83
-
84
-		private
85
-		SCORES = %w(A+ A A- B C D E F T M X)
86
-
87
-		def self.score(a)
88
-			SCORES.index a.grade
89
-		end
90 41
 	end
91 42
 end

+ 10
- 4
lib/cryptcheck/tls/https.rb View File

@@ -1,12 +1,18 @@
1 1
 module CryptCheck
2 2
 	module Tls
3 3
 		module Https
4
-			def self.analyze(hosts, output)
5
-				Tls.analyze hosts, 'output/https.erb', output, nil, port: 443, server_class: Server, grade_class: Grade
4
+			def self.analyze(host, port=443)
5
+				::CryptCheck.analyze host, port do |family, ip, host|
6
+					s = Server.new family, ip, port, hostname: host
7
+					g = Grade.new s
8
+					Logger.info { '' }
9
+					g.display
10
+					g
11
+				end
6 12
 			end
7 13
 
8
-			def self.analyze_from_file(file, output)
9
-				Tls.analyze_from_file file, 'output/https.erb', output, port: 443, server_class: Server, grade_class: Grade
14
+			def self.analyze_file(input, output)
15
+				::CryptCheck.analyze_file(input, 'output/https.erb', output) { |host| self.analyze host }
10 16
 			end
11 17
 		end
12 18
 	end

+ 1
- 1
lib/cryptcheck/tls/https/server.rb View File

@@ -6,7 +6,7 @@ module CryptCheck
6 6
 			class Server < Tls::TcpServer
7 7
 				attr_reader :hsts
8 8
 
9
-				def initialize(hostname, port=443)
9
+				def initialize(family, ip, port = 443, hostname: nil)
10 10
 					super
11 11
 					fetch_hsts
12 12
 				end

+ 30
- 37
lib/cryptcheck/tls/server.rb View File

@@ -7,8 +7,8 @@ module CryptCheck
7 7
 		class TlsNotSupportedServer
8 8
 			attr_reader :hostname, :port
9 9
 
10
-			def initialize(hostname, port)
11
-				@hostname, @port = hostname, port
10
+			def initialize(host, port)
11
+				@hostname, @port = host, port
12 12
 			end
13 13
 		end
14 14
 
@@ -32,12 +32,12 @@ module CryptCheck
32 32
 			class ConnectionError < TLSException
33 33
 			end
34 34
 
35
-			attr_reader :hostname, :port, :prefered_ciphers, :cert, :cert_valid, :cert_trusted, :dh
35
+			attr_reader :family, :ip, :port, :hostname, :prefered_ciphers, :cert, :cert_valid, :cert_trusted, :dh
36 36
 
37
-			def initialize(hostname, port)
38
-				@hostname, @port = hostname, port
37
+			def initialize(family, ip, port, hostname: nil)
38
+				@family, @ip, @port, @hostname = family, ip, port, hostname
39 39
 				@dh = []
40
-				Logger.info { "#{hostname}:#{port}".colorize :blue }
40
+				Logger.info { name.colorize :blue }
41 41
 				extract_cert
42 42
 				Logger.info { '' }
43 43
 				Logger.info { "Key : #{Tls.key_to_s self.key}" }
@@ -119,22 +119,28 @@ module CryptCheck
119 119
 			end
120 120
 
121 121
 			private
122
-			def connect(family, host, port, &block)
123
-				socket   = ::Socket.new family, sock_type
124
-				sockaddr = ::Socket.sockaddr_in port, host
125
-				Logger.trace { "Connecting to #{host}:#{port}" }
122
+			def name
123
+				name = "#{@hostname || @ip}:#@port"
124
+				name += " [#@ip]" if @hostname
125
+				name
126
+			end
127
+
128
+			def connect(&block)
129
+				socket   = ::Socket.new @family, sock_type
130
+				sockaddr = ::Socket.sockaddr_in @port, @ip
131
+				Logger.trace { "Connecting to #{name}" }
126 132
 				begin
127 133
 					status = socket.connect_nonblock sockaddr
128
-					Logger.trace { "Connecting to #{host}:#{port} status : #{status}" }
134
+					Logger.trace { "Connecting to #{name} status : #{status}" }
129 135
 					raise ConnectionError, status unless status == 0
130
-					Logger.trace { "Connected to #{host}:#{port}" }
136
+					Logger.trace { "Connected to #{name}" }
131 137
 					block_given? ? block.call(socket) : nil
132 138
 				rescue ::IO::WaitReadable
133
-					Logger.trace { "Waiting for read to #{host}:#{port}" }
139
+					Logger.trace { "Waiting for read to #{name}" }
134 140
 					raise Timeout unless IO.select [socket], nil, nil, TCP_TIMEOUT
135 141
 					retry
136 142
 				rescue ::IO::WaitWritable
137
-					Logger.trace { "Waiting for write to #{host}:#{port}" }
143
+					Logger.trace { "Waiting for write to #{name}" }
138 144
 					raise Timeout unless IO.select nil, [socket], nil, TCP_TIMEOUT
139 145
 					retry
140 146
 				rescue => e
@@ -150,18 +156,18 @@ module CryptCheck
150 156
 
151 157
 			def ssl_connect(socket, context, method, &block)
152 158
 				ssl_socket          = ::OpenSSL::SSL::SSLSocket.new socket, context
153
-				ssl_socket.hostname = @hostname unless method == :SSLv2
154
-				Logger.trace { "SSL connecting to #{@hostname}:#{@port}" }
159
+				ssl_socket.hostname = @hostname if @hostname and method != :SSLv2
160
+				Logger.trace { "SSL connecting to #{name}" }
155 161
 				begin
156 162
 					ssl_socket.connect_nonblock
157
-					Logger.trace { "SSL connected to #{@hostname}:#{@port}" }
163
+					Logger.trace { "SSL connected to #{name}" }
158 164
 					return block_given? ? block.call(ssl_socket) : nil
159 165
 				rescue ::IO::WaitReadable
160
-					Logger.trace { "Waiting for SSL read to #{@hostname}:#{@port}" }
166
+					Logger.trace { "Waiting for SSL read to #{name}" }
161 167
 					raise TLSTimeout unless IO.select [socket], nil, nil, SSL_TIMEOUT
162 168
 					retry
163 169
 				rescue ::IO::WaitWritable
164
-					Logger.trace { "Waiting for SSL write to #{@hostname}:#{@port}" }
170
+					Logger.trace { "Waiting for SSL write to #{name}" }
165 171
 					raise TLSTimeout unless IO.select nil, [socket], nil, SSL_TIMEOUT
166 172
 					retry
167 173
 				rescue ::OpenSSL::SSL::SSLError => e
@@ -189,26 +195,13 @@ module CryptCheck
189 195
 				ssl_context         = ::OpenSSL::SSL::SSLContext.new method
190 196
 				ssl_context.ciphers = ciphers if ciphers
191 197
 				Logger.trace { "Try #{method} connection with #{ciphers}" }
192
-
193
-				[::Socket::AF_INET, ::Socket::AF_INET6].each do |family|
194
-					Logger.trace { "Try connection for family #{family}" }
195
-					addrs = begin
196
-						::Socket.getaddrinfo @hostname, nil, family, :STREAM
197
-					rescue ::SocketError => e
198
-						Logger.error { "Unable to resolv #{@hostname} : #{e}" }
199
-						next
200
-					end
201
-
202
-					addrs.each do |addr|
203
-						connect family, addr[3], @port do |socket|
204
-							ssl_connect socket, ssl_context, method do |ssl_socket|
205
-								return block_given? ? block.call(ssl_socket) : nil
206
-							end
207
-						end
198
+				connect do |socket|
199
+					ssl_connect socket, ssl_context, method do |ssl_socket|
200
+						return block_given? ? block.call(ssl_socket) : nil
208 201
 					end
209 202
 				end
210 203
 
211
-				Logger.debug { "No SSL available on #{@hostname}" }
204
+				Logger.debug { "No SSL available on #{name}" }
212 205
 				raise CipherNotAvailable
213 206
 			end
214 207
 
@@ -223,7 +216,7 @@ module CryptCheck
223 216
 					end
224 217
 				end
225 218
 				raise TLSNotAvailableException unless @cert
226
-				@cert_valid   = ::OpenSSL::SSL.verify_certificate_identity @cert, @hostname
219
+				@cert_valid   = ::OpenSSL::SSL.verify_certificate_identity @cert, (@hostname || @ip)
227 220
 				@cert_trusted = verify_trust @chain, @cert
228 221
 			end
229 222
 

+ 19
- 2
lib/cryptcheck/tls/smtp.rb View File

@@ -1,8 +1,25 @@
1 1
 module CryptCheck
2 2
 	module Tls
3 3
 		module Smtp
4
-			def self.analyze_from_file(file, output)
5
-				Tls.analyze_from_file file, 'output/smtp.erb', output, port: 25, server_class: Server, grade_class: Grade
4
+			def self.analyze(host, port=25, domain: nil)
5
+				domain ||= host
6
+				::CryptCheck.analyze host, port do |family, ip, host|
7
+					s = Server.new family, ip, port, hostname: host, domain: domain
8
+					g = Grade.new s
9
+					Logger.info { '' }
10
+					g.display
11
+					g
12
+				end
13
+			end
14
+
15
+			def self.analyze_domain(domain)
16
+				srv = Resolv::DNS.new.getresources(domain, Resolv::DNS::Resource::IN::MX).sort_by(&:preference).first
17
+				hostname = srv ? srv.exchange.to_s : domain
18
+				self.analyze hostname, domain: domain
19
+			end
20
+
21
+			def self.analyze_file(input, output)
22
+				::CryptCheck.analyze_file(input, 'output/smtp.erb', output) { |host| self.analyze_domain host }
6 23
 			end
7 24
 		end
8 25
 	end

+ 2
- 12
lib/cryptcheck/tls/smtp/server.rb View File

@@ -1,22 +1,12 @@
1
-require 'resolv'
2
-
3 1
 module CryptCheck
4 2
 	module Tls
5 3
 		module Smtp
6 4
 			class Server < Tls::TcpServer
7
-				RESOLVER = Resolv::DNS.new
8
-
9 5
 				attr_reader :domain
10 6
 
11
-				def initialize(domain, port=25)
7
+				def initialize(family, ip, port, hostname: nil, domain:)
12 8
 					@domain = domain
13
-					srv = RESOLVER.getresources(domain, Resolv::DNS::Resource::IN::MX).sort_by(&:preference).first
14
-					if srv
15
-						hostname = srv.exchange.to_s
16
-					else # DNS is not correctly set, guess config…
17
-						hostname = domain
18
-					end
19
-					super hostname, port
9
+					super family, ip, port, hostname: hostname
20 10
 				end
21 11
 
22 12
 				def ssl_connect(socket, context, method, &block)

+ 24
- 37
lib/cryptcheck/tls/xmpp.rb View File

@@ -4,49 +4,36 @@ require 'parallel'
4 4
 module CryptCheck
5 5
 	module Tls
6 6
 		module Xmpp
7
-			MAX_ANALYSIS_DURATION = 600
8
-			PARALLEL_ANALYSIS = 10
9
-
10
-			def self.grade(hostname, type=:s2s)
11
-				timeout MAX_ANALYSIS_DURATION do
12
-					Grade.new Server.new hostname, type
7
+			def self.analyze(host, port=nil, domain: nil, type: :s2s)
8
+				domain ||= host
9
+				::CryptCheck.analyze host, port do |family, ip, host|
10
+					s = Server.new family, ip, port, hostname: host, type: type, domain: domain
11
+					g = Grade.new s
12
+					Logger.info { '' }
13
+					g.display
14
+					g
13 15
 				end
14
-			rescue ::Exception => e
15
-				Logger.error { "Error during #{hostname}:#{type} analysis : #{e}" }
16
-				TlsNotSupportedGrade.new TlsNotSupportedServer.new hostname, type
17 16
 			end
18 17
 
19
-			def self.analyze(hosts, output)
20
-				servers = []
21
-				semaphore = ::Mutex.new
22
-				::Parallel.each hosts, progress: 'Analysing', in_threads: PARALLEL_ANALYSIS, finish: lambda { |item, _, _| puts item } do |host|
23
-										 result = grade host.strip
24
-										 semaphore.synchronize { servers << result }
25
-									 end
26
-				servers.sort! do |a, b|
27
-					cmp = score(a) <=> score(b)
28
-					if cmp == 0
29
-						cmp = b.score <=> a.score
30
-						if cmp == 0
31
-							cmp = a.server.hostname <=> b.server.hostname
32
-						end
33
-					end
34
-					cmp
18
+			def self.analyze_domain(domain, type: :s2s)
19
+				service, port = case type
20
+									when :s2s
21
+										['_xmpp-server', 5269]
22
+									when :c2s
23
+										['_xmpp-client', 5222]
24
+								end
25
+				srv = Resolv::DNS.new.getresources("#{service}._tcp.#{domain}", Resolv::DNS::Resource::IN::SRV)
26
+							  .sort_by(&:priority).first
27
+				if srv
28
+					hostname, port = srv.target.to_s, srv.port
29
+				else # DNS is not correctly set, guess config…
30
+					hostname = domain
35 31
 				end
36
-
37
-				::File.write output, ::ERB.new(::File.read('output/xmpp.erb')).result(binding)
32
+				self.analyze hostname, port, domain: domain, type: type
38 33
 			end
39 34
 
40
-			def self.analyze_from_file(file, output)
41
-				hosts = ::YAML.load_file file
42
-				self.analyze hosts, output
43
-			end
44
-
45
-			private
46
-			SCORES = %w(A+ A A- B C D E F T M X)
47
-
48
-			def self.score(a)
49
-				SCORES.index a.grade
35
+			def self.analyze_file(input, output)
36
+				::CryptCheck.analyze_file(input, 'output/xmpp.erb', output) { |host| self.analyze_domain host }
50 37
 			end
51 38
 		end
52 39
 	end

+ 16
- 21
lib/cryptcheck/tls/xmpp/server.rb View File

@@ -1,45 +1,40 @@
1 1
 require 'nokogiri'
2
-require 'resolv'
3 2
 
4 3
 module CryptCheck
5 4
 	module Tls
6 5
 		module Xmpp
7 6
 			TLS_NAMESPACE = 'urn:ietf:params:xml:ns:xmpp-tls'
8
-			RESOLVER = Resolv::DNS.new
9 7
 
10 8
 			class Server < Tls::TcpServer
11 9
 				attr_reader :domain
12 10
 
13
-				def initialize(domain, type=:s2s, hostname: nil)
11
+				def initialize(family, ip, port=nil, hostname: nil, domain: nil, type: :s2s)
12
+					domain         ||= hostname
14 13
 					@type, @domain = type, domain
15
-					service, port = case type
16
-								  when :s2s then ['_xmpp-server', 5269]
17
-								  when :c2s then ['_xmpp-client', 5222]
18
-							  end
19
-					unless hostname
20
-						srv = RESOLVER.getresources("#{service}._tcp.#{domain}", Resolv::DNS::Resource::IN::SRV).sort_by(&:priority).first
21
-						if srv
22
-							hostname, port = srv.target.to_s, srv.port
23
-						else # DNS is not correctly set, guess config…
24
-							hostname = domain
25
-						end
26
-					end
27
-					super hostname, port
14
+					port           = case type
15
+										 when :s2s
16
+											 5269
17
+										 when :c2s
18
+											 5222
19
+									 end unless port
20
+					super family, ip, port, hostname: hostname
28 21
 					Logger.info { '' }
29 22
 					Logger.info { self.required? ? 'Required'.colorize(:green) : 'Not required'.colorize(:yellow) }
30 23
 				end
31 24
 
32 25
 				def ssl_connect(socket, context, method, &block)
33 26
 					type = case @type
34
-								 when :s2s then 'jabber:server'
35
-								 when :c2s then 'jabber:client'
36
-							 end
27
+							   when :s2s then
28
+								   'jabber:server'
29
+							   when :c2s then
30
+								   'jabber:client'
31
+						   end
37 32
 					socket.write "<?xml version='1.0' ?><stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='#{type}' to='#{@domain}' version='1.0'>"
38 33
 					response = ''
39 34
 					loop do
40 35
 						response += socket.recv 1024
41
-						xml = ::Nokogiri::XML response
42
-						error = xml.xpath '//stream:error'
36
+						xml      = ::Nokogiri::XML response
37
+						error    = xml.xpath '//stream:error'
43 38
 						raise Exception, error.text unless error.empty?
44 39
 						unless xml.xpath('//stream:features').empty?
45 40
 							response = xml

+ 101
- 0
output/alexa.yml View File

@@ -0,0 +1,101 @@
1
+- description: Top 100 Alexa
2
+  - google.com
3
+  - facebook.com
4
+  - youtube.com
5
+  - baidu.com
6
+  - yahoo.com
7
+  - amazon.com
8
+  - wikipedia.org
9
+  - qq.com
10
+  - google.co.in
11
+  - twitter.com
12
+  - live.com
13
+  - taobao.com
14
+  - sina.com.cn
15
+  - msn.com
16
+  - yahoo.co.jp
17
+  - linkedin.com
18
+  - google.co.jp
19
+  - weibo.com
20
+  - bing.com
21
+  - yandex.ru
22
+  - vk.com
23
+  - hao123.com
24
+  - ebay.com
25
+  - instagram.com
26
+  - google.de
27
+  - amazon.co.jp
28
+  - mail.ru
29
+  - 360.cn
30
+  - google.ru
31
+  - google.co.uk
32
+  - t.co
33
+  - pinterest.com
34
+  - google.com.br
35
+  - reddit.com
36
+  - netflix.com
37
+  - google.fr
38
+  - tmall.com
39
+  - sohu.com
40
+  - paypal.com
41
+  - microsoft.com
42
+  - wordpress.com
43
+  - blogspot.com
44
+  - google.it
45
+  - tumblr.com
46
+  - google.es
47
+  - onclickads.net
48
+  - imgur.com
49
+  - aliexpress.com
50
+  - imdb.com
51
+  - apple.com
52
+  - ok.ru
53
+  - xvideos.com
54
+  - ask.com
55
+  - fc2.com
56
+  - stackoverflow.com
57
+  - google.com.mx
58
+  - gmw.cn
59
+  - amazon.de
60
+  - google.com.hk
61
+  - alibaba.com
62
+  - office.com
63
+  - google.ca
64
+  - google.com.tr
65
+  - rakuten.co.jp
66
+  - xinhuanet.com
67
+  - pornhub.com
68
+  - soso.com
69
+  - tianya.cn
70
+  - google.co.id
71
+  - amazon.in
72
+  - haosou.com
73
+  - blogger.com
74
+  - craigslist.org
75
+  - github.com
76
+  - amazon.co.uk
77
+  - nicovideo.jp
78
+  - kat.cr
79
+  - outbrain.com
80
+  - bongacams.com
81
+  - go.com
82
+  - googleusercontent.com
83
+  - 360.com
84
+  - naver.com
85
+  - google.pl
86
+  - adnetworkperformance.com
87
+  - cnn.com
88
+  - dropbox.com
89
+  - pixnet.net
90
+  - google.com.au
91
+  - flipkart.com
92
+  - chinadaily.com.cn
93
+  - adobe.com
94
+  - xhamster.com
95
+  - jd.com
96
+  - whatsapp.com
97
+  - microsoftonline.com
98
+  - chase.com
99
+  - coccoc.com
100
+  - bbc.co.uk
101
+  - indiatimes.com

+ 1
- 0
output/index.yml View File

@@ -23,6 +23,7 @@
23 23
   - tcit.fr
24 24
   - aplu.fr
25 25
   - tdelmas.ovh
26
+  - gordon.re
26 27
 - description: Associations
27 28
   hostnames:
28 29
   - april.org

+ 3
- 1
output/smtp.erb View File

@@ -92,7 +92,9 @@
92 92
 											else :danger
93 93
 										end
94 94
 								%>
95
-								<th id="<%= s.domain %>"><%= s.domain %></th>
95
+								<th id="<%= s.domain %>">
96
+									<a href="#<%= s.domain %>"><%= s.domain %></a>
97
+								</th>
96 98
 								<td class="<%= rank_color %>">
97 99
 									<%= n.grade %>
98 100
 								</td>

+ 98
- 86
output/xmpp.erb View File

@@ -30,7 +30,22 @@
30 30
 			<div class="row">
31 31
 				<div class="col-md-12">
32 32
 					<table class="table table-bordered table-hover table-condensed">
33
-						<thead>
33
+						<tbody>
34
+							<%
35
+								first = true
36
+								results.each do |r|
37
+									unless first
38
+							%>
39
+							<tr>
40
+								<th colspan="15">&nbsp;</th>
41
+							</tr>
42
+							<%
43
+								end
44
+								first = false
45
+							%>
46
+							<tr>
47
+								<th colspan="15" id="<%= r[0] %>"><%= r[0] %></th>
48
+							</tr>
34 49
 							<tr>
35 50
 								<th rowspan="2">Host</th>
36 51
 								<td rowspan="2">Grade</td>
@@ -57,8 +72,88 @@
57 72
 								<td class="info">PFS</td>
58 73
 								<td class="success">Required</td>
59 74
 							</tr>
60
-                        </thead>
61
-						<tfoot>
75
+							<% r[1].each do |n|
76
+								s = n.server
77
+							%>
78
+							<tr>
79
+								<th id="<%= s.domain %>">
80
+									<a href="#<%= s.domain %>"><%= s.domain %></a>
81
+								</th>
82
+								<% if s.is_a? Tls::TlsNotSupportedServer %>
83
+									<td class="critical" colspan="16">
84
+										No SSL/TLS
85
+									</td>
86
+								<% else
87
+									   rank_color = case n.grade
88
+														when 'A+' then :info
89
+														when 'A', 'A-' then :success
90
+														when 'B', 'C' then :warning
91
+														when 'T', 'M' then :critical
92
+														else :danger
93
+													end %>
94
+									<td class="<%= rank_color %>">
95
+										<%= n.grade %>
96
+									</td>
97
+
98
+									<% key = s.key %>
99
+									<td class="<%= key.rsa_equivalent_size < 2048 ? :danger : key.rsa_equivalent_size < 4096 ? :warning : :success %>">
100
+										<%= "#{key.size} (#{key.type.to_s.upcase})" %>
101
+										<span class="sr-only">(<%= key.size < 2048 ? '☹' : '☺' %>)</span>
102
+									</td>
103
+									<td class="<%= s.sha1_sig? ? :warning : :success %>">
104
+										<%= s.sha1_sig? ? '✓' : '✗' %>
105
+										<span class="sr-only">(<%= s.sha1_sig? ? '☹' : '☺' %>)</span>
106
+									</td>
107
+
108
+									<td class="<%= s.sslv2? ? :critical : :success %>">
109
+										<%= s.sslv2? ? '✓' : '✗' %>
110
+										<span class="sr-only">(<%= s.sslv2? ? '☹' : '☺' %>)</span>
111
+									</td>
112
+									<td class="<%= s.sslv3? ? :critical : :success %>">
113
+										<%= s.sslv3? ? '✓' : '✗' %>
114
+										<span class="sr-only">(<%= s.sslv3? ? '☹' : '☺' %>)</span>
115
+									</td>
116
+									<td class="<%= s.tlsv1_2? ? :success : :danger %>">
117
+										<%= s.tlsv1_2? ? '✓' : '✗' %>
118
+										<span class="sr-only">(<%= s.tlsv1_2? ? '☺' : '☹' %>)</span>
119
+									</td>
120
+									<td class="<%= s.tls? ? (s.tls_only? ? :info : :success) : :danger %>">
121
+										<%= s.tls? ? '✓' : '✗' %>
122
+										<span class="sr-only">(<%= s.tls? ? '☺' : '☹' %>)</span>
123
+									</td>
124
+
125
+									<td class="<%= s.cipher_size < 112 ? :danger : s.cipher_size < 128 ? :warning : :success %>">
126
+										<%= s.cipher_size %>
127
+										<span class="sr-only">(<%= s.cipher_size < 128 ? '☹' : '☺' %>)</span>
128
+									</td>
129
+									<td class="<%= s.md5? ? :critical : :success %>">
130
+										<%= s.md5? ? '✓' : '✗' %>
131
+										<span class="sr-only">(<%= s.md5? ? '☹' : '☺' %>)</span>
132
+									</td>
133
+									<td class="<%= s.sha1? ? :warning : :success %>">
134
+										<%= s.sha1? ? '✓' : '✗' %>
135
+										<span class="sr-only">(<%= s.sha1? ? '☹' : '☺' %>)</span>
136
+									</td>
137
+									<td class="<%= (s.rc4? or s.des?) ? :critical : :success %>">
138
+										<%= (s.rc4? or s.des?) ? '✓' : '✗' %>
139
+										<span class="sr-only">(<%= (s.rc4? or s.des?) ? '☹' : '☺' %>)</span>
140
+									</td>
141
+									<td class="<%= s.des3? ? :danger : :success %>">
142
+										<%= s.des3? ? '✓' : '✗' %>
143
+										<span class="sr-only">(<%= s.des3? ? '☹' : '☺' %>)</span>
144
+									</td>
145
+
146
+									<td class="<%= s.pfs? ? (s.pfs_only? ? :info : :success) : :danger %>">
147
+										<%= s.pfs? ? '✓' : '✗' %>
148
+										<span class="sr-only">(<%= s.pfs? ? '☺' : '☹' %>)</span>
149
+									</td>
150
+									<td class="<%= s.required? ? :success : :danger %>">
151
+										<%= s.required? ? '✓' : '✗' %>
152
+										<span class="sr-only">(<%= s.required? ? '☺' : '☹' %>)</span>
153
+									</td>
154
+								<% end %>
155
+							</tr>
156
+							<% end %>
62 157
 							<tr>
63 158
 								<th rowspan="2">Host</th>
64 159
 								<td rowspan="2">Grade</td>
@@ -85,89 +180,6 @@
85 180
 								<td class="info">PFS</td>
86 181
 								<td class="success">Required</td>
87 182
 							</tr>
88
-						</tfoot>
89
-						<tbody>
90
-							<% servers.each do |n|
91
-								s = n.server
92
-							%>
93
-							<tr>
94
-								<th id="<%= s.hostname %>">
95
-									<a href="#<%= s.hostname %>"><%= s.hostname %></a>
96
-								</th>
97
-								<% if s.is_a? Tls::TlsNotSupportedServer %>
98
-								<td class="critical" colspan="16">
99
-									No SSL/TLS
100
-								</td>
101
-								<% else
102
-										rank_color = case n.grade
103
-											when 'A+' then :info
104
-											when 'A', 'A-' then :success
105
-											when 'B', 'C' then :warning
106
-											when 'T', 'M' then :critical
107
-											else :danger
108
-										end %>
109
-								<td class="<%= rank_color %>">
110
-									<%= n.grade %>
111
-								</td>
112
-
113
-								<% key = s.key %>
114
-								<td class="<%= key.rsa_equivalent_size < 2048 ? :danger : key.rsa_equivalent_size < 4096 ? :warning : :success %>">
115
-									<%= "#{key.size} (#{key.type.to_s.upcase})" %>
116
-									<span class="sr-only">(<%= key.size < 2048 ? '☹' : '☺' %>)</span>
117
-								</td>
118
-								<td class="<%= s.sha1_sig? ? :warning : :success %>">
119
-									<%= s.sha1_sig? ? '✓' : '✗' %>
120
-									<span class="sr-only">(<%= s.sha1_sig? ? '☹' : '☺' %>)</span>
121
-								</td>
122
-
123
-								<td class="<%= s.sslv2? ? :critical : :success %>">
124
-									<%= s.sslv2? ? '✓' : '✗' %>
125
-									<span class="sr-only">(<%= s.sslv2? ? '☹' : '☺' %>)</span>
126
-								</td>
127
-								<td class="<%= s.sslv3? ? :critical : :success %>">
128
-									<%= s.sslv3? ? '✓' : '✗' %>
129
-									<span class="sr-only">(<%= s.sslv3? ? '☹' : '☺' %>)</span>
130
-								</td>
131
-								<td class="<%= s.tlsv1_2? ? :success : :danger %>">
132
-									<%= s.tlsv1_2? ? '✓' : '✗' %>
133
-									<span class="sr-only">(<%= s.tlsv1_2? ? '☺' : '☹' %>)</span>
134
-								</td>
135
-								<td class="<%= s.tls? ? (s.tls_only? ? :info : :success) : :danger %>">
136
-									<%= s.tls? ? '✓' : '✗' %>
137
-									<span class="sr-only">(<%= s.tls? ? '☺' : '☹' %>)</span>
138
-								</td>
139
-
140
-								<td class="<%= s.cipher_size < 112 ? :danger : s.cipher_size < 128 ? :warning : :success %>">
141
-									<%= s.cipher_size %>
142
-									<span class="sr-only">(<%= s.cipher_size < 128 ? '☹' : '☺' %>)</span>
143
-								</td>
144
-								<td class="<%= s.md5? ? :critical : :success %>">
145
-									<%= s.md5? ? '✓' : '✗' %>
146
-									<span class="sr-only">(<%= s.md5? ? '☹' : '☺' %>)</span>
147
-								</td>
148
-								<td class="<%= s.sha1? ? :warning : :success %>">
149
-									<%= s.sha1? ? '✓' : '✗' %>
150
-									<span class="sr-only">(<%= s.sha1? ? '☹' : '☺' %>)</span>
151
-								</td>
152
-								<td class="<%= (s.rc4? or s.des?) ? :critical : :success %>">
153
-									<%= (s.rc4? or s.des?) ? '✓' : '✗' %>
154
-									<span class="sr-only">(<%= (s.rc4? or s.des?) ? '☹' : '☺' %>)</span>
155
-								</td>
156
-								<td class="<%= s.des3? ? :danger : :success %>">
157
-									<%= s.des3? ? '✓' : '✗' %>
158
-									<span class="sr-only">(<%= s.des3? ? '☹' : '☺' %>)</span>
159
-								</td>
160
-
161
-								<td class="<%= s.pfs? ? (s.pfs_only? ? :info : :success) : :danger %>">
162
-									<%= s.pfs? ? '✓' : '✗' %>
163
-									<span class="sr-only">(<%= s.pfs? ? '☺' : '☹' %>)</span>
164
-								</td>
165
-								<td class="<%= s.required? ? :success : :danger %>">
166
-									<%= s.required? ? '✓' : '✗' %>
167
-									<span class="sr-only">(<%= s.required? ? '☺' : '☹' %>)</span>
168
-								</td>
169
-								<% end %>
170
-							</tr>
171 183
 							<% end %>
172 184
 						</tbody>
173 185
 					</table>

Loading…
Cancel
Save