lib/ronin/recon/builtin/api/crt_sh.rb in ronin-recon-0.1.0.rc2 vs lib/ronin/recon/builtin/api/crt_sh.rb in ronin-recon-0.1.0

- old
+ new

@@ -17,12 +17,14 @@ # You should have received a copy of the GNU Lesser General Public License # along with ronin-recon. If not, see <https://www.gnu.org/licenses/>. # require 'ronin/recon/worker' +require 'ronin/support/text/patterns/network' require 'async/http/internet/instance' +require 'set' module Ronin module Recon module API # @@ -31,14 +33,15 @@ # class CrtSh < Worker register 'api/crt_sh' - summary 'Queries https://crt.sh and returns host from each domains certificate.' + summary 'Queries https://crt.sh' description <<~DESC - Queries https://crt.sh and returns host from each domains certificate. + Queries https://crt.sh and returns the host names from each valid + certificate for the domain. DESC accepts Domain outputs Host intensity :passive @@ -65,11 +68,16 @@ @client = Async::HTTP::Client.new( Async::HTTP::Endpoint.for('https','crt.sh') ) end + # Regular expression to verify valid host names. # + # @api private + HOST_NAME_REGEX = /\A#{Support::Text::Patterns::HOST_NAME}\z/ + + # # Returns host from each domains certificate. # # @param [Values::Domain] domain # The domain value to check. # @@ -79,18 +87,21 @@ # # @yieldparam [Values::Host] host # The host from certificate. # def process(domain) - Async do - path = "/?dNSName=#{domain}&exclude=expired&output=json" - response = @client.get(path) - certs = JSON.parse(response.read, symbolize_names: true) + path = "/?dNSName=#{domain}&exclude=expired&output=json" + response = @client.get(path) + certs = JSON.parse(response.read, symbolize_names: true) + hostnames = Set.new - certs.each do |cert| - if (common_name = cert[:common_name]) - yield Host.new(common_name) - end + certs.each do |cert| + common_name = cert[:common_name] + + if common_name && + common_name =~ HOST_NAME_REGEX && + hostnames.add?(common_name) + yield Host.new(common_name) end end end end