diff --git a/README.md b/README.md index e1f3ea5..7ec1b14 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ Commands: completion convert dump + grep help import print diff --git a/gemspec.yml b/gemspec.yml index ddccef8..9176c51 100644 --- a/gemspec.yml +++ b/gemspec.yml @@ -24,6 +24,7 @@ generated_files: - man/ronin-nmap-completion.1 - man/ronin-nmap-convert.1 - man/ronin-nmap-dump.1 + - man/ronin-nmap-grep.1 - man/ronin-nmap-import.1 - man/ronin-nmap-print.1 - man/ronin-nmap-scan.1 diff --git a/lib/ronin/nmap/cli/commands/grep.rb b/lib/ronin/nmap/cli/commands/grep.rb new file mode 100644 index 0000000..0ecedd5 --- /dev/null +++ b/lib/ronin/nmap/cli/commands/grep.rb @@ -0,0 +1,378 @@ +# frozen_string_literal: true +# +# ronin-nmap - A Ruby library for automating nmap and importing nmap scans. +# +# Copyright (c) 2023 Hal Brodigan (postmodern.mod3@gmail.com) +# +# ronin-nmap is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# ronin-nmap is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with ronin-nmap. If not, see . +# + +require 'ronin/nmap/cli/command' + +require 'command_kit/colors' +require 'command_kit/printing/indent' +require 'nmap/xml' + +module Ronin + module Nmap + class CLI + module Commands + # + # Parses and searches nmap XML file(s) for the pattern. + # + # ## Usage + # + # ronin-nmap grep [options] PATTERN XML_FILE [...] + # + # ## Options + # + # -h, --help Print help information + # + # ## Arguments + # + class Grep < Command + + include CommandKit::Colors + include CommandKit::Printing::Indent + + usage '[options] PATTERN XML_FILE [...]' + + argument :pattern, required: true, + desc: 'The pattern to search for' + + argument :xml_file, required: true, + repeats: true, + desc: 'The nmap XML file to search' + + description 'Parses and searches nmap XML file(s) for the pattern' + + man_page 'ronin-nmap-grep.1' + + # + # Runs the `ronin-nmap grep` command. + # + # @param [String] pattern + # The pattern to search for. + # + # @param [Array] xml_files + # The nmap `.xml` file(s) to grep. + # + def run(pattern,*xml_files) + xml_files.each do |xml_file| + unless File.file?(xml_file) + print_error "no such file or directory: #{xml_file}" + next + end + + xml = ::Nmap::XML.open(xml_file) + hosts = grep_xml(xml,pattern) + + highlight_hosts(hosts,pattern) + end + end + + # + # Searches the parsed nmap XML for the text pattern. + # + # @param [::Nmap::XML] xml + # The parsed nmap XML object. + # + # @param [String] pattern + # The text pattern to search for. + # + # @return [Enumerator::Lazy<::Nmap::XML::Host>] + # The nmap XML host objects that contain the text pattern. + # + def grep_xml(xml,pattern) + xml.each_up_host.lazy.filter do |host| + match_host(host,pattern) + end + end + + # + # Determines if the nmap XML host object contains the text pattern. + # + # @param [::Nmap::XML::Host] host + # The nmap XML host object to search. + # + # @param [String] pattern + # The text pattern to search for. + # + # @return [Boolean] + # + def match_host(host,pattern) + hostnames = host.each_hostname + open_ports = host.each_open_port + host_script = host.host_script + + hostnames.any? { |hostname| match_hostname(hostname,pattern) } || + open_ports.any? { |port| match_port(port,pattern) } || + (host_script && match_scripts(host_script,pattern)) + end + + # + # Determines if the nmap XML hostname object contains the text + # pattern. + # + # @param [::Nmap::XML::Hostname] hostname + # The nmap XML hostname object to search. + # + # @param [String] pattern + # The text pattern to search for. + # + # @return [Boolean] + # + def match_hostname(hostname,pattern) + hostname.name.match(pattern) + end + + # + # Determines if the nmap XML port object contains the text pattern. + # + # @param [::Nmap::XML::Port] port + # The nmap XML port object to search. + # + # @param [String] pattern + # The text pattern to search for. + # + # @return [Boolean] + # + def match_port(port,pattern) + match_scripts(port,pattern) || if (service = port.service) + match_service(service,pattern) + end + end + + # + # Determines if the nmap XML service object contains the text pattern. + # + # @param [::Nmap::XML::Service] service + # The nmap XML service object to search. + # + # @param [String] pattern + # The text pattern to search for. + # + # @return [Boolean] + # + def match_service(service,pattern) + product = service.product + version = service.version + extra_info = service.extra_info + + service.name.match(pattern) || + (product && product.match(pattern)) || + (version && version.match(pattern)) || + (extra_info && extra_info.match(pattern)) + end + + # + # Determines if the nmap XML scripts object contains the text pattern. + # + # @param [::Nmap::XML::Scripts] has_scripts + # The nmap XML object that includes `Nmap::XML::Scripts`. + # + # @param [String] pattern + # The text pattern to search for. + # + # @return [Boolean] + # + def match_scripts(has_scripts,pattern) + has_scripts.scripts.any? do |id,script| + match_script(script,pattern) + end + end + + # + # Determines if the nmap XML script object contains the text pattern. + # + # @param [::Nmap::XML::Script] script + # The nmap XML script object to search. + # + # @param [String] pattern + # The text pattern to search for. + # + # @return [Boolean] + # + def match_script(script,pattern) + script.id.match(pattern) || script.output.match(pattern) + end + + # + # Prints the nmap hosts with the pattern highlighted in the output. + # + # @param [Enumerator::Lazy<::Nmap::XML::Host>] hosts + # The nmap hosts to print. + # + # @param [String] pattern + # The pattern to highlight in the output. + # + def highlight_hosts(hosts,pattern) + hosts.each do |host| + highlight_host(host,pattern) + puts + end + end + + # + # Prints the nmap host with the pattern highlighted in the output. + # + # @param [::Nmap::XML::Host] host + # The nmap host to print. + # + # @param [String] pattern + # The text pattern to highlight in the output. + # + def highlight_host(host,pattern) + addresses = host.addresses + hostnames = host.hostnames + + unless hostnames.empty? + puts "[ #{addresses.first} / #{highlight(hostnames.first,pattern)} ]" + else + puts "[ #{addresses.first} ]" + end + puts + + indent do + if addresses.length > 1 + puts "[ addresses ]" + puts + + indent do + addresses.each do |address| + puts address + end + end + puts + end + + if hostnames.length > 1 + puts "[ hostnames ]" + puts + + indent do + hostnames.each do |hostname| + puts highlight(hostname,pattern) + end + end + puts + end + + if (host_script = host.host_script) + puts "[ host scripts ]" + puts + + indent do + highlight_scripts(host_script) + end + end + + puts "[ ports ]" + puts + + indent do + host.each_open_port do |port| + highlight_port(port,pattern) + end + end + end + end + + # + # Prints the nmap port with the pattern highlighted in the output. + # + # @param [::Nmap::XML::Port] port + # The nmap XML port object to print. + # + # @param [String] pattern + # The text pattern to highlight in the output. + # + def highlight_port(port,pattern) + port_line = "#{port.number}/#{port.protocol}\t#{port.state}" + + if (service = port.service) + port_line << "\t#{highlight(service,pattern)}" + + if (extra_info = service.extra_info) + port_line << " #{highlight(extra_info,pattern)}" + end + end + + puts port_line + + unless port.scripts.empty? + puts + + indent do + highlight_scripts(port,pattern) + end + end + end + + # + # Prints the nmap scripts with the pattern highlighted in the output. + # + # @param [::Nmap::XML::Scripts] has_scripts + # The nmap XML object that has scripts. + # + # @param [String] pattern + # The text pattern to highlight in the output. + # + def highlight_scripts(has_scripts,pattern) + has_scripts.scripts.each_value do |script| + highlight_script(script,pattern) + puts + end + end + + # + # Prints the nmap script with the pattern highlighted in the output. + # + # @param [::Nmap::XML::Script] script + # The nmap XML script object to print. + # + # @param [String] pattern + # The text pattern to highlight in the output. + # + def highlight_script(script,pattern) + puts "#{highlight(script.id,pattern)}:" + + indent do + script.output.strip.each_line do |line| + puts highlight(line,pattern) + end + end + end + + # + # Highlights the pattern in the text. + # + # @param [String] text + # The text to modify. + # + # @param [String] pattern + # The pattern to highlight. + # + # @return [String] + # The modified text. + # + def highlight(text,pattern) + text.to_s.gsub(pattern,colors.bold(colors.red(pattern))) + end + + end + end + end + end +end diff --git a/man/ronin-nmap-grep.1.md b/man/ronin-nmap-grep.1.md new file mode 100644 index 0000000..7d741dd --- /dev/null +++ b/man/ronin-nmap-grep.1.md @@ -0,0 +1,36 @@ +# ronin-nmap-grep 1 "2023-03-01" Ronin Nmap "User Manuals" + +## NAME + +ronin-nmap-grep - Parses and searches nmap XML file(s) for the pattern. + +## SYNOPSIS + +`ronin-nmap grep` [options] *PATTERN* *XML_FILE* [...] + +## DESCRIPTION + +Parses one or more nmap XML files and searches the scan data for the text +pattern. The scanned hosts that contain the text pattern are then printed +with the text pattern highlighted in red. + +## ARGUMENTS + +*PATTERN* +: The text pattern to search for. + +*XML_FILE* +: The nmap XML file to import. + +## OPTIONS + +`-h`, `--help` +: Print help information + +## AUTHOR + +Postmodern + +## SEE ALSO + +[ronin-nmap-print](ronin-nmap-print.1.md) diff --git a/spec/cli/commands/grep_spec.rb b/spec/cli/commands/grep_spec.rb new file mode 100644 index 0000000..1eae9dd --- /dev/null +++ b/spec/cli/commands/grep_spec.rb @@ -0,0 +1,1236 @@ +require 'spec_helper' +require 'ronin/nmap/cli/commands/grep' +require_relative 'man_page_example' + +require 'stringio' + +describe Ronin::Nmap::CLI::Commands::Grep do + include_examples "man_page" + + let(:fixtures_dir) { File.join(__dir__,'..','..','fixtures') } + let(:xml_file) { File.join(fixtures_dir,'nmap.xml') } + let(:xml) { Nmap::XML.open(xml_file) } + let(:host) { xml.host } + let(:hostname) { xml.host.hostname } + let(:port) { host.open_ports.first } + let(:service) { port.service } + let(:script_id) { 'ssh2-enum-algos' } + let(:script) { port.scripts[script_id] } + + let(:red) { CommandKit::Colors::ANSI::RED } + let(:bold) { CommandKit::Colors::ANSI::BOLD } + let(:reset_color) { CommandKit::Colors::ANSI::RESET_COLOR } + let(:reset_intensity) { CommandKit::Colors::ANSI::RESET_INTENSITY } + let(:reset) { reset_color + reset_intensity } + + let(:stdout) { StringIO.new } + + subject { described_class.new(stdout: stdout) } + before { allow(stdout).to receive(:tty?).and_return(true) } + + describe "#run" do + context "when given a single XML file" do + let(:pattern) { 'nmap' } + + it "must parse the XML file and print the matching hosts, with the pattern highlighted in bold red" do + subject.run(pattern,xml_file) + + expect(stdout.string).to eq( + <<~OUTPUT + [ 45.33.32.156 / scanme.#{bold}#{red}nmap#{reset_color}#{reset_intensity}.org ] + + [ hostnames ] + + scanme.#{bold}#{red}nmap#{reset_color}#{reset_intensity}.org + li982-156.members.linode.com + + [ ports ] + + 22/tcp open ssh protocol 2.0 + + ssh-hostkey: + 1024 ac:00:a0:1a:82:ff:cc:55:99:dc:67:2b:34:97:6b:75 (DSA) + ssh-dss AAAAB3NzaC1kc3MAAACBAOe8o59vFWZGaBmGPVeJBObEfi1AR8yEUYC/Ufkku3sKhGF7wM2m2ujIeZDK5vqeC0S5EN2xYo6FshCP4FQRYeTxD17nNO4PhwW65qAjDRRU0uHFfSAh5wk+vt4yQztOE++sTd1G9OBLzA8HO99qDmCAxb3zw+GQDEgPjzgyzGZ3AAAAFQCBmE1vROP8IaPkUmhM5xLFta/xHwAAAIEA3EwRfaeOPLL7TKDgGX67Lbkf9UtdlpCdC4doMjGgsznYMwWH6a7Lj3vi4/KmeZZdix6FMdFqq+2vrfT1DRqx0RS0XYdGxnkgS+2g333WYCrUkDCn6RPUWR/1TgGMPHCj7LWCa1ZwJwLWS2KX288Pa2gLOWuhZm2VYKSQx6NEDOIAAACBANxIfprSdBdbo4Ezrh6/X6HSvrhjtZ7MouStWaE714ByO5bS2coM9CyaCwYyrE5qzYiyIfb+1BG3O5nVdDuN95sQ/0bAdBKlkqLFvFqFjVbETF0ri3v97w6MpUawfF75ouDrQ4xdaUOLLEWTso6VFJcM6Jg9bDl0FA0uLZUSDEHL + 2048 20:3d:2d:44:62:2a:b0:5a:9d:b5:b3:05:14:c2:a6:b2 (RSA) + ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC6afooTZ9mVUGFNEhkMoRR1Btzu64XXwElhCsHw/zVlIx/HXylNbb9+11dm2VgJQ21pxkWDs+L6+EbYyDnvRURTrMTgHL0xseB0EkNqexs9hYZSiqtMx4jtGNtHvsMxZnbxvVUk2dasWvtBkn8J5JagSbzWTQo4hjKMOI1SUlXtiKxAs2F8wiq2EdSuKw/KNk8GfIp1TA+8ccGeAtnsVptTJ4D/8MhAWsROkQzOowQvnBBz2/8ecEvoMScaf+kDfNQowK3gENtSSOqYw9JLOza6YJBPL/aYuQQ0nJ74Rr5vL44aNIlrGI9jJc2x0bV7BeNA5kVuXsmhyfWbbkB8yGd + 256 96:02:bb:5e:57:54:1c:4e:45:2f:56:4c:4a:24:b2:57 (ECDSA) + ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMD46g67x6yWNjjQJnXhiz/TskHrqQ0uPcOspFrIYW382uOGzmWDZCFV8FbFwQyH90u+j0Qr1SGNAxBZMhOQ8pc= + + ssh2-enum-algos: + kex_algorithms: (8) + curve25519-sha256@libssh.org + ecdh-sha2-nistp256 + ecdh-sha2-nistp384 + ecdh-sha2-nistp521 + diffie-hellman-group-exchange-sha256 + diffie-hellman-group-exchange-sha1 + diffie-hellman-group14-sha1 + diffie-hellman-group1-sha1 + server_host_key_algorithms: (4) + ssh-rsa + ssh-dss + ecdsa-sha2-nistp256 + ssh-ed25519 + encryption_algorithms: (16) + aes128-ctr + aes192-ctr + aes256-ctr + arcfour256 + arcfour128 + aes128-gcm@openssh.com + aes256-gcm@openssh.com + chacha20-poly1305@openssh.com + aes128-cbc + 3des-cbc + blowfish-cbc + cast128-cbc + aes192-cbc + aes256-cbc + arcfour + rijndael-cbc@lysator.liu.se + mac_algorithms: (19) + hmac-md5-etm@openssh.com + hmac-sha1-etm@openssh.com + umac-64-etm@openssh.com + umac-128-etm@openssh.com + hmac-sha2-256-etm@openssh.com + hmac-sha2-512-etm@openssh.com + hmac-ripemd160-etm@openssh.com + hmac-sha1-96-etm@openssh.com + hmac-md5-96-etm@openssh.com + hmac-md5 + hmac-sha1 + umac-64@openssh.com + umac-128@openssh.com + hmac-sha2-256 + hmac-sha2-512 + hmac-ripemd160 + hmac-ripemd160@openssh.com + hmac-sha1-96 + hmac-md5-96 + compression_algorithms: (2) + none + zlib@openssh.com + + 80/tcp open Apache httpd 2.4.7 (Ubuntu) + 9929/tcp open nping-echo + 31337/tcp open ncat-chat users: nobody + 123/udp open NTP v4 + + OUTPUT + ) + end + end + + context "when given multiple XML files" do + it "must grep each XML file and print matching hosts with the pattern highlighted in bold red" + end + end + + describe "#grep_xml" do + let(:pattern) { 'scanme' } + + it "must return an Enumerator::Lazy object containing the matching hosts that contain the given pattern" do + hosts = subject.grep_xml(xml,pattern) + + expect(hosts).to be_kind_of(Enumerator::Lazy) + expect(hosts.first.hostname.name).to match(pattern) + end + end + + describe "#match_host" do + context "when the Nmap::XML::Host object contains the pattern in one of it's hostnames" do + let(:pattern) { 'scanme' } + + it do + expect(subject.match_host(host,pattern)).to be_truthy + end + end + + context "when the Nmap::XML::Host object contains the pattern in one of it's open port's services" do + let(:pattern) { 'httpd' } + + it do + expect(subject.match_host(host,pattern)).to be_truthy + end + end + + context "when the Nmap::XML::Host object contains the pattern in one of it's open ports script IDs" do + let(:pattern) { 'ssh2-enum-algos' } + + it do + expect(subject.match_host(host,pattern)).to be_truthy + end + end + + context "when the Nmap::XML::Host object contains the pattern in one of it's open ports script outputs" do + let(:pattern) { 'rsa' } + + it do + expect(subject.match_host(host,pattern)).to be_truthy + end + end + + context "when the Nmap::XML::Host object contains the pattern in one of it's host scripts" do + let(:pattern) { 'scanme' } + + xit do + pending "need an nmap XML file which contains host scripts" + + expect(subject.match_host(host,pattern)).to be_truthy + end + end + + context "when the Nmap::XML::Host object does not contain the pattern in any of it's hostnames, open port's services, open port's script IDs, open port's script outputs, or it's host scripts" do + let(:pattern) { 'does-not-match' } + + it do + expect(subject.match_host(host,pattern)).to be_falsy + end + end + end + + describe "#match_hostname" do + context "when the Nmap::XML::Hostname object contains the pattern in it's name" do + let(:pattern) { 'nmap' } + + it do + expect(subject.match_hostname(hostname,pattern)).to be_truthy + end + end + + context "when the Nmap::XML::Hostname object does not contain the pattern in it's name" do + let(:pattern) { 'does-not-match' } + + it do + expect(subject.match_hostname(hostname,pattern)).to be_falsy + end + end + end + + describe "#match_port" do + context "when the Nmap::XML::Port contains the pattern in one of it's script IDs" do + let(:pattern) { 'ssh2' } + + it do + expect(subject.match_port(port,pattern)).to be_truthy + end + end + + context "when the Nmap::XML::Port contains the pattern in one of it's script outputs" do + let(:pattern) { 'rsa' } + + it do + expect(subject.match_port(port,pattern)).to be_truthy + end + end + + context "when the Nmap::XML::Port does not contain the pattern in one of it's service, script IDs, script outputs" do + let(:pattern) { 'does-not-match' } + + it do + expect(subject.match_port(port,pattern)).to be_falsy + end + end + + context "when the Nmap::XML::Port object has a #service" do + context "and it contains the pattern" do + let(:port) do + host.open_ports.find { |port| port.number == 80 } + end + let(:pattern) { 'http' } + + it do + expect(subject.match_port(port,pattern)).to be_truthy + end + end + end + end + + describe "#match_service" do + let(:port) do + host.open_ports.find { |port| port.number == 80 } + end + let(:service) { port.service } + + context "when the Nmap::XML::Service contains the pattern in it's product info" do + let(:pattern) { 'http' } + + it do + expect(subject.match_service(service,pattern)).to be_truthy + end + end + + context "when the Nmap::XML::Service contains the pattern in it's version" do + let(:pattern) { '2.4' } + + it do + expect(subject.match_service(service,pattern)).to be_truthy + end + end + + context "when the Nmap::XML::Service contains the pattern in it's extra info" do + let(:pattern) { 'Ubuntu' } + + it do + expect(subject.match_service(service,pattern)).to be_truthy + end + end + + context "when the Nmap::XML::Service does not contain the pattern in any of it's product info, version, or extra info" do + let(:pattern) { 'does-not-match' } + + it do + expect(subject.match_service(service,pattern)).to be_falsy + end + end + end + + describe "#match_scripts" do + let(:has_scripts) { port } + + context "when one of the object's #scripts contains the pattern in it's script ID" do + let(:pattern) { 'ssh2' } + + it do + expect(subject.match_scripts(has_scripts,pattern)).to be_truthy + end + end + + context "when one of the object's #scripts contains the pattern in it's script outputs" do + let(:pattern) { 'rsa' } + + it do + expect(subject.match_scripts(has_scripts,pattern)).to be_truthy + end + end + + context "when none of the object's #scripts contains the pattern in it's script ID" do + let(:pattern) { 'does-not-match' } + + it do + expect(subject.match_scripts(has_scripts,pattern)).to be_falsy + end + end + end + + describe "#match_script" do + context "when the Nmap::XML::Script object contains the pattern in it's script ID" do + let(:pattern) { 'ssh2' } + + it do + expect(subject.match_script(script,pattern)).to be_truthy + end + end + + context "when the Nmap::XML::Script object contains the pattern in it's script outputs" do + let(:pattern) { 'rsa' } + + it do + expect(subject.match_script(script,pattern)).to be_truthy + end + end + + context "when the Nmap::XML::Script object does not contain the pattern in it's script ID or script output" do + let(:pattern) { 'does-not-match' } + + it do + expect(subject.match_script(script,pattern)).to be_falsy + end + end + end + + describe "#highlight_hosts" do + let(:hosts) { xml.up_hosts } + let(:pattern) { 'nmap' } + + it "must print each host, with the pattern highlighted in bold red, with an extra newline after the host" do + subject.highlight_hosts(hosts,pattern) + + expect(stdout.string).to eq( + <<~OUTPUT + [ 45.33.32.156 / scanme.#{bold}#{red}nmap#{reset_color}#{reset_intensity}.org ] + + [ hostnames ] + + scanme.#{bold}#{red}nmap#{reset_color}#{reset_intensity}.org + li982-156.members.linode.com + + [ ports ] + + 22/tcp open ssh protocol 2.0 + + ssh-hostkey: + 1024 ac:00:a0:1a:82:ff:cc:55:99:dc:67:2b:34:97:6b:75 (DSA) + ssh-dss AAAAB3NzaC1kc3MAAACBAOe8o59vFWZGaBmGPVeJBObEfi1AR8yEUYC/Ufkku3sKhGF7wM2m2ujIeZDK5vqeC0S5EN2xYo6FshCP4FQRYeTxD17nNO4PhwW65qAjDRRU0uHFfSAh5wk+vt4yQztOE++sTd1G9OBLzA8HO99qDmCAxb3zw+GQDEgPjzgyzGZ3AAAAFQCBmE1vROP8IaPkUmhM5xLFta/xHwAAAIEA3EwRfaeOPLL7TKDgGX67Lbkf9UtdlpCdC4doMjGgsznYMwWH6a7Lj3vi4/KmeZZdix6FMdFqq+2vrfT1DRqx0RS0XYdGxnkgS+2g333WYCrUkDCn6RPUWR/1TgGMPHCj7LWCa1ZwJwLWS2KX288Pa2gLOWuhZm2VYKSQx6NEDOIAAACBANxIfprSdBdbo4Ezrh6/X6HSvrhjtZ7MouStWaE714ByO5bS2coM9CyaCwYyrE5qzYiyIfb+1BG3O5nVdDuN95sQ/0bAdBKlkqLFvFqFjVbETF0ri3v97w6MpUawfF75ouDrQ4xdaUOLLEWTso6VFJcM6Jg9bDl0FA0uLZUSDEHL + 2048 20:3d:2d:44:62:2a:b0:5a:9d:b5:b3:05:14:c2:a6:b2 (RSA) + ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC6afooTZ9mVUGFNEhkMoRR1Btzu64XXwElhCsHw/zVlIx/HXylNbb9+11dm2VgJQ21pxkWDs+L6+EbYyDnvRURTrMTgHL0xseB0EkNqexs9hYZSiqtMx4jtGNtHvsMxZnbxvVUk2dasWvtBkn8J5JagSbzWTQo4hjKMOI1SUlXtiKxAs2F8wiq2EdSuKw/KNk8GfIp1TA+8ccGeAtnsVptTJ4D/8MhAWsROkQzOowQvnBBz2/8ecEvoMScaf+kDfNQowK3gENtSSOqYw9JLOza6YJBPL/aYuQQ0nJ74Rr5vL44aNIlrGI9jJc2x0bV7BeNA5kVuXsmhyfWbbkB8yGd + 256 96:02:bb:5e:57:54:1c:4e:45:2f:56:4c:4a:24:b2:57 (ECDSA) + ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMD46g67x6yWNjjQJnXhiz/TskHrqQ0uPcOspFrIYW382uOGzmWDZCFV8FbFwQyH90u+j0Qr1SGNAxBZMhOQ8pc= + + ssh2-enum-algos: + kex_algorithms: (8) + curve25519-sha256@libssh.org + ecdh-sha2-nistp256 + ecdh-sha2-nistp384 + ecdh-sha2-nistp521 + diffie-hellman-group-exchange-sha256 + diffie-hellman-group-exchange-sha1 + diffie-hellman-group14-sha1 + diffie-hellman-group1-sha1 + server_host_key_algorithms: (4) + ssh-rsa + ssh-dss + ecdsa-sha2-nistp256 + ssh-ed25519 + encryption_algorithms: (16) + aes128-ctr + aes192-ctr + aes256-ctr + arcfour256 + arcfour128 + aes128-gcm@openssh.com + aes256-gcm@openssh.com + chacha20-poly1305@openssh.com + aes128-cbc + 3des-cbc + blowfish-cbc + cast128-cbc + aes192-cbc + aes256-cbc + arcfour + rijndael-cbc@lysator.liu.se + mac_algorithms: (19) + hmac-md5-etm@openssh.com + hmac-sha1-etm@openssh.com + umac-64-etm@openssh.com + umac-128-etm@openssh.com + hmac-sha2-256-etm@openssh.com + hmac-sha2-512-etm@openssh.com + hmac-ripemd160-etm@openssh.com + hmac-sha1-96-etm@openssh.com + hmac-md5-96-etm@openssh.com + hmac-md5 + hmac-sha1 + umac-64@openssh.com + umac-128@openssh.com + hmac-sha2-256 + hmac-sha2-512 + hmac-ripemd160 + hmac-ripemd160@openssh.com + hmac-sha1-96 + hmac-md5-96 + compression_algorithms: (2) + none + zlib@openssh.com + + 80/tcp open Apache httpd 2.4.7 (Ubuntu) + 9929/tcp open nping-echo + 31337/tcp open ncat-chat users: nobody + 123/udp open NTP v4 + + OUTPUT + ) + end + end + + describe "#highlight_host" do + context "when the host has more than one address" do + it "must print a '[ addresses ]' section with the addresses" + end + + context "when the pattern exists in the first hostname" do + let(:pattern) { 'nmap' } + + it "must print the address and hostnmae in the top section header and in the '[ hostnames ]' section, with the pattern highlighted in bold red" do + subject.highlight_host(host,pattern) + + expect(stdout.string).to eq( + <<~OUTPUT + [ 45.33.32.156 / scanme.#{bold}#{red}nmap#{reset_color}#{reset_intensity}.org ] + + [ hostnames ] + + scanme.#{bold}#{red}nmap#{reset_color}#{reset_intensity}.org + li982-156.members.linode.com + + [ ports ] + + 22/tcp open ssh protocol 2.0 + + ssh-hostkey: + 1024 ac:00:a0:1a:82:ff:cc:55:99:dc:67:2b:34:97:6b:75 (DSA) + ssh-dss AAAAB3NzaC1kc3MAAACBAOe8o59vFWZGaBmGPVeJBObEfi1AR8yEUYC/Ufkku3sKhGF7wM2m2ujIeZDK5vqeC0S5EN2xYo6FshCP4FQRYeTxD17nNO4PhwW65qAjDRRU0uHFfSAh5wk+vt4yQztOE++sTd1G9OBLzA8HO99qDmCAxb3zw+GQDEgPjzgyzGZ3AAAAFQCBmE1vROP8IaPkUmhM5xLFta/xHwAAAIEA3EwRfaeOPLL7TKDgGX67Lbkf9UtdlpCdC4doMjGgsznYMwWH6a7Lj3vi4/KmeZZdix6FMdFqq+2vrfT1DRqx0RS0XYdGxnkgS+2g333WYCrUkDCn6RPUWR/1TgGMPHCj7LWCa1ZwJwLWS2KX288Pa2gLOWuhZm2VYKSQx6NEDOIAAACBANxIfprSdBdbo4Ezrh6/X6HSvrhjtZ7MouStWaE714ByO5bS2coM9CyaCwYyrE5qzYiyIfb+1BG3O5nVdDuN95sQ/0bAdBKlkqLFvFqFjVbETF0ri3v97w6MpUawfF75ouDrQ4xdaUOLLEWTso6VFJcM6Jg9bDl0FA0uLZUSDEHL + 2048 20:3d:2d:44:62:2a:b0:5a:9d:b5:b3:05:14:c2:a6:b2 (RSA) + ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC6afooTZ9mVUGFNEhkMoRR1Btzu64XXwElhCsHw/zVlIx/HXylNbb9+11dm2VgJQ21pxkWDs+L6+EbYyDnvRURTrMTgHL0xseB0EkNqexs9hYZSiqtMx4jtGNtHvsMxZnbxvVUk2dasWvtBkn8J5JagSbzWTQo4hjKMOI1SUlXtiKxAs2F8wiq2EdSuKw/KNk8GfIp1TA+8ccGeAtnsVptTJ4D/8MhAWsROkQzOowQvnBBz2/8ecEvoMScaf+kDfNQowK3gENtSSOqYw9JLOza6YJBPL/aYuQQ0nJ74Rr5vL44aNIlrGI9jJc2x0bV7BeNA5kVuXsmhyfWbbkB8yGd + 256 96:02:bb:5e:57:54:1c:4e:45:2f:56:4c:4a:24:b2:57 (ECDSA) + ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMD46g67x6yWNjjQJnXhiz/TskHrqQ0uPcOspFrIYW382uOGzmWDZCFV8FbFwQyH90u+j0Qr1SGNAxBZMhOQ8pc= + + ssh2-enum-algos: + kex_algorithms: (8) + curve25519-sha256@libssh.org + ecdh-sha2-nistp256 + ecdh-sha2-nistp384 + ecdh-sha2-nistp521 + diffie-hellman-group-exchange-sha256 + diffie-hellman-group-exchange-sha1 + diffie-hellman-group14-sha1 + diffie-hellman-group1-sha1 + server_host_key_algorithms: (4) + ssh-rsa + ssh-dss + ecdsa-sha2-nistp256 + ssh-ed25519 + encryption_algorithms: (16) + aes128-ctr + aes192-ctr + aes256-ctr + arcfour256 + arcfour128 + aes128-gcm@openssh.com + aes256-gcm@openssh.com + chacha20-poly1305@openssh.com + aes128-cbc + 3des-cbc + blowfish-cbc + cast128-cbc + aes192-cbc + aes256-cbc + arcfour + rijndael-cbc@lysator.liu.se + mac_algorithms: (19) + hmac-md5-etm@openssh.com + hmac-sha1-etm@openssh.com + umac-64-etm@openssh.com + umac-128-etm@openssh.com + hmac-sha2-256-etm@openssh.com + hmac-sha2-512-etm@openssh.com + hmac-ripemd160-etm@openssh.com + hmac-sha1-96-etm@openssh.com + hmac-md5-96-etm@openssh.com + hmac-md5 + hmac-sha1 + umac-64@openssh.com + umac-128@openssh.com + hmac-sha2-256 + hmac-sha2-512 + hmac-ripemd160 + hmac-ripemd160@openssh.com + hmac-sha1-96 + hmac-md5-96 + compression_algorithms: (2) + none + zlib@openssh.com + + 80/tcp open Apache httpd 2.4.7 (Ubuntu) + 9929/tcp open nping-echo + 31337/tcp open ncat-chat users: nobody + 123/udp open NTP v4 + OUTPUT + ) + end + end + + context "when the pattern exists in one of the other hostnames" do + let(:pattern) { 'linode' } + + it "must print the '[ hostnames ]' section containing the hostnames, with the pattern highlighted in bold red" do + subject.highlight_host(host,pattern) + + expect(stdout.string).to eq( + <<~OUTPUT + [ 45.33.32.156 / scanme.nmap.org ] + + [ hostnames ] + + scanme.nmap.org + li982-156.members.#{bold}#{red}linode#{reset_color}#{reset_intensity}.com + + [ ports ] + + 22/tcp open ssh protocol 2.0 + + ssh-hostkey: + 1024 ac:00:a0:1a:82:ff:cc:55:99:dc:67:2b:34:97:6b:75 (DSA) + ssh-dss AAAAB3NzaC1kc3MAAACBAOe8o59vFWZGaBmGPVeJBObEfi1AR8yEUYC/Ufkku3sKhGF7wM2m2ujIeZDK5vqeC0S5EN2xYo6FshCP4FQRYeTxD17nNO4PhwW65qAjDRRU0uHFfSAh5wk+vt4yQztOE++sTd1G9OBLzA8HO99qDmCAxb3zw+GQDEgPjzgyzGZ3AAAAFQCBmE1vROP8IaPkUmhM5xLFta/xHwAAAIEA3EwRfaeOPLL7TKDgGX67Lbkf9UtdlpCdC4doMjGgsznYMwWH6a7Lj3vi4/KmeZZdix6FMdFqq+2vrfT1DRqx0RS0XYdGxnkgS+2g333WYCrUkDCn6RPUWR/1TgGMPHCj7LWCa1ZwJwLWS2KX288Pa2gLOWuhZm2VYKSQx6NEDOIAAACBANxIfprSdBdbo4Ezrh6/X6HSvrhjtZ7MouStWaE714ByO5bS2coM9CyaCwYyrE5qzYiyIfb+1BG3O5nVdDuN95sQ/0bAdBKlkqLFvFqFjVbETF0ri3v97w6MpUawfF75ouDrQ4xdaUOLLEWTso6VFJcM6Jg9bDl0FA0uLZUSDEHL + 2048 20:3d:2d:44:62:2a:b0:5a:9d:b5:b3:05:14:c2:a6:b2 (RSA) + ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC6afooTZ9mVUGFNEhkMoRR1Btzu64XXwElhCsHw/zVlIx/HXylNbb9+11dm2VgJQ21pxkWDs+L6+EbYyDnvRURTrMTgHL0xseB0EkNqexs9hYZSiqtMx4jtGNtHvsMxZnbxvVUk2dasWvtBkn8J5JagSbzWTQo4hjKMOI1SUlXtiKxAs2F8wiq2EdSuKw/KNk8GfIp1TA+8ccGeAtnsVptTJ4D/8MhAWsROkQzOowQvnBBz2/8ecEvoMScaf+kDfNQowK3gENtSSOqYw9JLOza6YJBPL/aYuQQ0nJ74Rr5vL44aNIlrGI9jJc2x0bV7BeNA5kVuXsmhyfWbbkB8yGd + 256 96:02:bb:5e:57:54:1c:4e:45:2f:56:4c:4a:24:b2:57 (ECDSA) + ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMD46g67x6yWNjjQJnXhiz/TskHrqQ0uPcOspFrIYW382uOGzmWDZCFV8FbFwQyH90u+j0Qr1SGNAxBZMhOQ8pc= + + ssh2-enum-algos: + kex_algorithms: (8) + curve25519-sha256@libssh.org + ecdh-sha2-nistp256 + ecdh-sha2-nistp384 + ecdh-sha2-nistp521 + diffie-hellman-group-exchange-sha256 + diffie-hellman-group-exchange-sha1 + diffie-hellman-group14-sha1 + diffie-hellman-group1-sha1 + server_host_key_algorithms: (4) + ssh-rsa + ssh-dss + ecdsa-sha2-nistp256 + ssh-ed25519 + encryption_algorithms: (16) + aes128-ctr + aes192-ctr + aes256-ctr + arcfour256 + arcfour128 + aes128-gcm@openssh.com + aes256-gcm@openssh.com + chacha20-poly1305@openssh.com + aes128-cbc + 3des-cbc + blowfish-cbc + cast128-cbc + aes192-cbc + aes256-cbc + arcfour + rijndael-cbc@lysator.liu.se + mac_algorithms: (19) + hmac-md5-etm@openssh.com + hmac-sha1-etm@openssh.com + umac-64-etm@openssh.com + umac-128-etm@openssh.com + hmac-sha2-256-etm@openssh.com + hmac-sha2-512-etm@openssh.com + hmac-ripemd160-etm@openssh.com + hmac-sha1-96-etm@openssh.com + hmac-md5-96-etm@openssh.com + hmac-md5 + hmac-sha1 + umac-64@openssh.com + umac-128@openssh.com + hmac-sha2-256 + hmac-sha2-512 + hmac-ripemd160 + hmac-ripemd160@openssh.com + hmac-sha1-96 + hmac-md5-96 + compression_algorithms: (2) + none + zlib@openssh.com + + 80/tcp open Apache httpd 2.4.7 (Ubuntu) + 9929/tcp open nping-echo + 31337/tcp open ncat-chat users: nobody + 123/udp open NTP v4 + OUTPUT + ) + end + end + + context "when the Nmap::XML::Host has a host_script" do + it "must print a '[ host scripts ]' sections with the host scripts" + end + + context "when the pattern exists in one of the ports or services" do + let(:pattern) { 'http' } + + it "must print the port number, protocol, state, and service name/version, with the pattern highlighted in bold red" do + subject.highlight_host(host,pattern) + + expect(stdout.string).to eq( + <<~OUTPUT + [ 45.33.32.156 / scanme.nmap.org ] + + [ hostnames ] + + scanme.nmap.org + li982-156.members.linode.com + + [ ports ] + + 22/tcp open ssh protocol 2.0 + + ssh-hostkey: + 1024 ac:00:a0:1a:82:ff:cc:55:99:dc:67:2b:34:97:6b:75 (DSA) + ssh-dss AAAAB3NzaC1kc3MAAACBAOe8o59vFWZGaBmGPVeJBObEfi1AR8yEUYC/Ufkku3sKhGF7wM2m2ujIeZDK5vqeC0S5EN2xYo6FshCP4FQRYeTxD17nNO4PhwW65qAjDRRU0uHFfSAh5wk+vt4yQztOE++sTd1G9OBLzA8HO99qDmCAxb3zw+GQDEgPjzgyzGZ3AAAAFQCBmE1vROP8IaPkUmhM5xLFta/xHwAAAIEA3EwRfaeOPLL7TKDgGX67Lbkf9UtdlpCdC4doMjGgsznYMwWH6a7Lj3vi4/KmeZZdix6FMdFqq+2vrfT1DRqx0RS0XYdGxnkgS+2g333WYCrUkDCn6RPUWR/1TgGMPHCj7LWCa1ZwJwLWS2KX288Pa2gLOWuhZm2VYKSQx6NEDOIAAACBANxIfprSdBdbo4Ezrh6/X6HSvrhjtZ7MouStWaE714ByO5bS2coM9CyaCwYyrE5qzYiyIfb+1BG3O5nVdDuN95sQ/0bAdBKlkqLFvFqFjVbETF0ri3v97w6MpUawfF75ouDrQ4xdaUOLLEWTso6VFJcM6Jg9bDl0FA0uLZUSDEHL + 2048 20:3d:2d:44:62:2a:b0:5a:9d:b5:b3:05:14:c2:a6:b2 (RSA) + ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC6afooTZ9mVUGFNEhkMoRR1Btzu64XXwElhCsHw/zVlIx/HXylNbb9+11dm2VgJQ21pxkWDs+L6+EbYyDnvRURTrMTgHL0xseB0EkNqexs9hYZSiqtMx4jtGNtHvsMxZnbxvVUk2dasWvtBkn8J5JagSbzWTQo4hjKMOI1SUlXtiKxAs2F8wiq2EdSuKw/KNk8GfIp1TA+8ccGeAtnsVptTJ4D/8MhAWsROkQzOowQvnBBz2/8ecEvoMScaf+kDfNQowK3gENtSSOqYw9JLOza6YJBPL/aYuQQ0nJ74Rr5vL44aNIlrGI9jJc2x0bV7BeNA5kVuXsmhyfWbbkB8yGd + 256 96:02:bb:5e:57:54:1c:4e:45:2f:56:4c:4a:24:b2:57 (ECDSA) + ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMD46g67x6yWNjjQJnXhiz/TskHrqQ0uPcOspFrIYW382uOGzmWDZCFV8FbFwQyH90u+j0Qr1SGNAxBZMhOQ8pc= + + ssh2-enum-algos: + kex_algorithms: (8) + curve25519-sha256@libssh.org + ecdh-sha2-nistp256 + ecdh-sha2-nistp384 + ecdh-sha2-nistp521 + diffie-hellman-group-exchange-sha256 + diffie-hellman-group-exchange-sha1 + diffie-hellman-group14-sha1 + diffie-hellman-group1-sha1 + server_host_key_algorithms: (4) + ssh-rsa + ssh-dss + ecdsa-sha2-nistp256 + ssh-ed25519 + encryption_algorithms: (16) + aes128-ctr + aes192-ctr + aes256-ctr + arcfour256 + arcfour128 + aes128-gcm@openssh.com + aes256-gcm@openssh.com + chacha20-poly1305@openssh.com + aes128-cbc + 3des-cbc + blowfish-cbc + cast128-cbc + aes192-cbc + aes256-cbc + arcfour + rijndael-cbc@lysator.liu.se + mac_algorithms: (19) + hmac-md5-etm@openssh.com + hmac-sha1-etm@openssh.com + umac-64-etm@openssh.com + umac-128-etm@openssh.com + hmac-sha2-256-etm@openssh.com + hmac-sha2-512-etm@openssh.com + hmac-ripemd160-etm@openssh.com + hmac-sha1-96-etm@openssh.com + hmac-md5-96-etm@openssh.com + hmac-md5 + hmac-sha1 + umac-64@openssh.com + umac-128@openssh.com + hmac-sha2-256 + hmac-sha2-512 + hmac-ripemd160 + hmac-ripemd160@openssh.com + hmac-sha1-96 + hmac-md5-96 + compression_algorithms: (2) + none + zlib@openssh.com + + 80/tcp open Apache #{bold}#{red}http#{reset_color}#{reset_intensity}d 2.4.7 (Ubuntu) + 9929/tcp open nping-echo + 31337/tcp open ncat-chat users: nobody + 123/udp open NTP v4 + OUTPUT + ) + end + end + + context "when the pattern exists in one of the script IDs for one of the ports" do + let(:pattern) { 'ssh2' } + + it "must print the script ID with the pattern highlighted in the script ID" do + subject.highlight_host(host,pattern) + + expect(stdout.string).to eq( + <<~OUTPUT + [ 45.33.32.156 / scanme.nmap.org ] + + [ hostnames ] + + scanme.nmap.org + li982-156.members.linode.com + + [ ports ] + + 22/tcp open ssh protocol 2.0 + + ssh-hostkey: + 1024 ac:00:a0:1a:82:ff:cc:55:99:dc:67:2b:34:97:6b:75 (DSA) + ssh-dss AAAAB3NzaC1kc3MAAACBAOe8o59vFWZGaBmGPVeJBObEfi1AR8yEUYC/Ufkku3sKhGF7wM2m2ujIeZDK5vqeC0S5EN2xYo6FshCP4FQRYeTxD17nNO4PhwW65qAjDRRU0uHFfSAh5wk+vt4yQztOE++sTd1G9OBLzA8HO99qDmCAxb3zw+GQDEgPjzgyzGZ3AAAAFQCBmE1vROP8IaPkUmhM5xLFta/xHwAAAIEA3EwRfaeOPLL7TKDgGX67Lbkf9UtdlpCdC4doMjGgsznYMwWH6a7Lj3vi4/KmeZZdix6FMdFqq+2vrfT1DRqx0RS0XYdGxnkgS+2g333WYCrUkDCn6RPUWR/1TgGMPHCj7LWCa1ZwJwLWS2KX288Pa2gLOWuhZm2VYKSQx6NEDOIAAACBANxIfprSdBdbo4Ezrh6/X6HSvrhjtZ7MouStWaE714ByO5bS2coM9CyaCwYyrE5qzYiyIfb+1BG3O5nVdDuN95sQ/0bAdBKlkqLFvFqFjVbETF0ri3v97w6MpUawfF75ouDrQ4xdaUOLLEWTso6VFJcM6Jg9bDl0FA0uLZUSDEHL + 2048 20:3d:2d:44:62:2a:b0:5a:9d:b5:b3:05:14:c2:a6:b2 (RSA) + ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC6afooTZ9mVUGFNEhkMoRR1Btzu64XXwElhCsHw/zVlIx/HXylNbb9+11dm2VgJQ21pxkWDs+L6+EbYyDnvRURTrMTgHL0xseB0EkNqexs9hYZSiqtMx4jtGNtHvsMxZnbxvVUk2dasWvtBkn8J5JagSbzWTQo4hjKMOI1SUlXtiKxAs2F8wiq2EdSuKw/KNk8GfIp1TA+8ccGeAtnsVptTJ4D/8MhAWsROkQzOowQvnBBz2/8ecEvoMScaf+kDfNQowK3gENtSSOqYw9JLOza6YJBPL/aYuQQ0nJ74Rr5vL44aNIlrGI9jJc2x0bV7BeNA5kVuXsmhyfWbbkB8yGd + 256 96:02:bb:5e:57:54:1c:4e:45:2f:56:4c:4a:24:b2:57 (ECDSA) + ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMD46g67x6yWNjjQJnXhiz/TskHrqQ0uPcOspFrIYW382uOGzmWDZCFV8FbFwQyH90u+j0Qr1SGNAxBZMhOQ8pc= + + #{bold}#{red}ssh2#{reset_color}#{reset_intensity}-enum-algos: + kex_algorithms: (8) + curve25519-sha256@libssh.org + ecdh-sha2-nistp256 + ecdh-sha2-nistp384 + ecdh-sha2-nistp521 + diffie-hellman-group-exchange-sha256 + diffie-hellman-group-exchange-sha1 + diffie-hellman-group14-sha1 + diffie-hellman-group1-sha1 + server_host_key_algorithms: (4) + ssh-rsa + ssh-dss + ecdsa-sha2-nistp256 + ssh-ed25519 + encryption_algorithms: (16) + aes128-ctr + aes192-ctr + aes256-ctr + arcfour256 + arcfour128 + aes128-gcm@openssh.com + aes256-gcm@openssh.com + chacha20-poly1305@openssh.com + aes128-cbc + 3des-cbc + blowfish-cbc + cast128-cbc + aes192-cbc + aes256-cbc + arcfour + rijndael-cbc@lysator.liu.se + mac_algorithms: (19) + hmac-md5-etm@openssh.com + hmac-sha1-etm@openssh.com + umac-64-etm@openssh.com + umac-128-etm@openssh.com + hmac-sha2-256-etm@openssh.com + hmac-sha2-512-etm@openssh.com + hmac-ripemd160-etm@openssh.com + hmac-sha1-96-etm@openssh.com + hmac-md5-96-etm@openssh.com + hmac-md5 + hmac-sha1 + umac-64@openssh.com + umac-128@openssh.com + hmac-sha2-256 + hmac-sha2-512 + hmac-ripemd160 + hmac-ripemd160@openssh.com + hmac-sha1-96 + hmac-md5-96 + compression_algorithms: (2) + none + zlib@openssh.com + + 80/tcp open Apache httpd 2.4.7 (Ubuntu) + 9929/tcp open nping-echo + 31337/tcp open ncat-chat users: nobody + 123/udp open NTP v4 + OUTPUT + ) + end + end + + context "when the pattern exists in one of the script output for one of the ports" do + let(:pattern) { 'rsa' } + + it "must print the script output with the pattern highlighted in the script output" do + subject.highlight_host(host,pattern) + + expect(stdout.string).to eq( + <<~OUTPUT + [ 45.33.32.156 / scanme.nmap.org ] + + [ hostnames ] + + scanme.nmap.org + li982-156.members.linode.com + + [ ports ] + + 22/tcp open ssh protocol 2.0 + + ssh-hostkey: + 1024 ac:00:a0:1a:82:ff:cc:55:99:dc:67:2b:34:97:6b:75 (DSA) + ssh-dss AAAAB3NzaC1kc3MAAACBAOe8o59vFWZGaBmGPVeJBObEfi1AR8yEUYC/Ufkku3sKhGF7wM2m2ujIeZDK5vqeC0S5EN2xYo6FshCP4FQRYeTxD17nNO4PhwW65qAjDRRU0uHFfSAh5wk+vt4yQztOE++sTd1G9OBLzA8HO99qDmCAxb3zw+GQDEgPjzgyzGZ3AAAAFQCBmE1vROP8IaPkUmhM5xLFta/xHwAAAIEA3EwRfaeOPLL7TKDgGX67Lbkf9UtdlpCdC4doMjGgsznYMwWH6a7Lj3vi4/KmeZZdix6FMdFqq+2vrfT1DRqx0RS0XYdGxnkgS+2g333WYCrUkDCn6RPUWR/1TgGMPHCj7LWCa1ZwJwLWS2KX288Pa2gLOWuhZm2VYKSQx6NEDOIAAACBANxIfprSdBdbo4Ezrh6/X6HSvrhjtZ7MouStWaE714ByO5bS2coM9CyaCwYyrE5qzYiyIfb+1BG3O5nVdDuN95sQ/0bAdBKlkqLFvFqFjVbETF0ri3v97w6MpUawfF75ouDrQ4xdaUOLLEWTso6VFJcM6Jg9bDl0FA0uLZUSDEHL + 2048 20:3d:2d:44:62:2a:b0:5a:9d:b5:b3:05:14:c2:a6:b2 (RSA) + ssh-#{bold}#{red}rsa#{reset_color}#{reset_intensity} AAAAB3NzaC1yc2EAAAADAQABAAABAQC6afooTZ9mVUGFNEhkMoRR1Btzu64XXwElhCsHw/zVlIx/HXylNbb9+11dm2VgJQ21pxkWDs+L6+EbYyDnvRURTrMTgHL0xseB0EkNqexs9hYZSiqtMx4jtGNtHvsMxZnbxvVUk2dasWvtBkn8J5JagSbzWTQo4hjKMOI1SUlXtiKxAs2F8wiq2EdSuKw/KNk8GfIp1TA+8ccGeAtnsVptTJ4D/8MhAWsROkQzOowQvnBBz2/8ecEvoMScaf+kDfNQowK3gENtSSOqYw9JLOza6YJBPL/aYuQQ0nJ74Rr5vL44aNIlrGI9jJc2x0bV7BeNA5kVuXsmhyfWbbkB8yGd + 256 96:02:bb:5e:57:54:1c:4e:45:2f:56:4c:4a:24:b2:57 (ECDSA) + ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMD46g67x6yWNjjQJnXhiz/TskHrqQ0uPcOspFrIYW382uOGzmWDZCFV8FbFwQyH90u+j0Qr1SGNAxBZMhOQ8pc= + + ssh2-enum-algos: + kex_algorithms: (8) + curve25519-sha256@libssh.org + ecdh-sha2-nistp256 + ecdh-sha2-nistp384 + ecdh-sha2-nistp521 + diffie-hellman-group-exchange-sha256 + diffie-hellman-group-exchange-sha1 + diffie-hellman-group14-sha1 + diffie-hellman-group1-sha1 + server_host_key_algorithms: (4) + ssh-#{bold}#{red}rsa#{reset_color}#{reset_intensity} + ssh-dss + ecdsa-sha2-nistp256 + ssh-ed25519 + encryption_algorithms: (16) + aes128-ctr + aes192-ctr + aes256-ctr + arcfour256 + arcfour128 + aes128-gcm@openssh.com + aes256-gcm@openssh.com + chacha20-poly1305@openssh.com + aes128-cbc + 3des-cbc + blowfish-cbc + cast128-cbc + aes192-cbc + aes256-cbc + arcfour + rijndael-cbc@lysator.liu.se + mac_algorithms: (19) + hmac-md5-etm@openssh.com + hmac-sha1-etm@openssh.com + umac-64-etm@openssh.com + umac-128-etm@openssh.com + hmac-sha2-256-etm@openssh.com + hmac-sha2-512-etm@openssh.com + hmac-ripemd160-etm@openssh.com + hmac-sha1-96-etm@openssh.com + hmac-md5-96-etm@openssh.com + hmac-md5 + hmac-sha1 + umac-64@openssh.com + umac-128@openssh.com + hmac-sha2-256 + hmac-sha2-512 + hmac-ripemd160 + hmac-ripemd160@openssh.com + hmac-sha1-96 + hmac-md5-96 + compression_algorithms: (2) + none + zlib@openssh.com + + 80/tcp open Apache httpd 2.4.7 (Ubuntu) + 9929/tcp open nping-echo + 31337/tcp open ncat-chat users: nobody + 123/udp open NTP v4 + OUTPUT + ) + end + end + end + + describe "#highlight_port" do + context "when the Nmap::XML::Port object has a service" do + let(:port) do + host.open_ports.find { |port| port.number == 80 } + end + let(:pattern) { 'http' } + + it "must print the port number, protocol, state, and service name/version, with the pattern highlighted in bold red" do + subject.highlight_port(port,pattern) + + expect(stdout.string).to eq( + "80/tcp\topen\tApache #{bold}#{red}http#{reset_color}#{reset_intensity}d 2.4.7 (Ubuntu)#{$/}" + ) + end + + context "and when the Nmap::XML::Port object has scripts" do + let(:port) do + host.open_ports.find { |port| port.number == 22 } + end + let(:pattern) { 'rsa' } + + it "must print the port number, protocol, state, and service name/version, and script IDs and output, with the pattern highlighted in bold red" do + subject.highlight_port(port,pattern) + + expect(stdout.string).to eq( + <<~OUTPUT + 22/tcp open ssh protocol 2.0 + + ssh-hostkey: + 1024 ac:00:a0:1a:82:ff:cc:55:99:dc:67:2b:34:97:6b:75 (DSA) + ssh-dss AAAAB3NzaC1kc3MAAACBAOe8o59vFWZGaBmGPVeJBObEfi1AR8yEUYC/Ufkku3sKhGF7wM2m2ujIeZDK5vqeC0S5EN2xYo6FshCP4FQRYeTxD17nNO4PhwW65qAjDRRU0uHFfSAh5wk+vt4yQztOE++sTd1G9OBLzA8HO99qDmCAxb3zw+GQDEgPjzgyzGZ3AAAAFQCBmE1vROP8IaPkUmhM5xLFta/xHwAAAIEA3EwRfaeOPLL7TKDgGX67Lbkf9UtdlpCdC4doMjGgsznYMwWH6a7Lj3vi4/KmeZZdix6FMdFqq+2vrfT1DRqx0RS0XYdGxnkgS+2g333WYCrUkDCn6RPUWR/1TgGMPHCj7LWCa1ZwJwLWS2KX288Pa2gLOWuhZm2VYKSQx6NEDOIAAACBANxIfprSdBdbo4Ezrh6/X6HSvrhjtZ7MouStWaE714ByO5bS2coM9CyaCwYyrE5qzYiyIfb+1BG3O5nVdDuN95sQ/0bAdBKlkqLFvFqFjVbETF0ri3v97w6MpUawfF75ouDrQ4xdaUOLLEWTso6VFJcM6Jg9bDl0FA0uLZUSDEHL + 2048 20:3d:2d:44:62:2a:b0:5a:9d:b5:b3:05:14:c2:a6:b2 (RSA) + ssh-#{bold}#{red}rsa#{reset_color}#{reset_intensity} AAAAB3NzaC1yc2EAAAADAQABAAABAQC6afooTZ9mVUGFNEhkMoRR1Btzu64XXwElhCsHw/zVlIx/HXylNbb9+11dm2VgJQ21pxkWDs+L6+EbYyDnvRURTrMTgHL0xseB0EkNqexs9hYZSiqtMx4jtGNtHvsMxZnbxvVUk2dasWvtBkn8J5JagSbzWTQo4hjKMOI1SUlXtiKxAs2F8wiq2EdSuKw/KNk8GfIp1TA+8ccGeAtnsVptTJ4D/8MhAWsROkQzOowQvnBBz2/8ecEvoMScaf+kDfNQowK3gENtSSOqYw9JLOza6YJBPL/aYuQQ0nJ74Rr5vL44aNIlrGI9jJc2x0bV7BeNA5kVuXsmhyfWbbkB8yGd + 256 96:02:bb:5e:57:54:1c:4e:45:2f:56:4c:4a:24:b2:57 (ECDSA) + ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMD46g67x6yWNjjQJnXhiz/TskHrqQ0uPcOspFrIYW382uOGzmWDZCFV8FbFwQyH90u+j0Qr1SGNAxBZMhOQ8pc= + + ssh2-enum-algos: + kex_algorithms: (8) + curve25519-sha256@libssh.org + ecdh-sha2-nistp256 + ecdh-sha2-nistp384 + ecdh-sha2-nistp521 + diffie-hellman-group-exchange-sha256 + diffie-hellman-group-exchange-sha1 + diffie-hellman-group14-sha1 + diffie-hellman-group1-sha1 + server_host_key_algorithms: (4) + ssh-#{bold}#{red}rsa#{reset_color}#{reset_intensity} + ssh-dss + ecdsa-sha2-nistp256 + ssh-ed25519 + encryption_algorithms: (16) + aes128-ctr + aes192-ctr + aes256-ctr + arcfour256 + arcfour128 + aes128-gcm@openssh.com + aes256-gcm@openssh.com + chacha20-poly1305@openssh.com + aes128-cbc + 3des-cbc + blowfish-cbc + cast128-cbc + aes192-cbc + aes256-cbc + arcfour + rijndael-cbc@lysator.liu.se + mac_algorithms: (19) + hmac-md5-etm@openssh.com + hmac-sha1-etm@openssh.com + umac-64-etm@openssh.com + umac-128-etm@openssh.com + hmac-sha2-256-etm@openssh.com + hmac-sha2-512-etm@openssh.com + hmac-ripemd160-etm@openssh.com + hmac-sha1-96-etm@openssh.com + hmac-md5-96-etm@openssh.com + hmac-md5 + hmac-sha1 + umac-64@openssh.com + umac-128@openssh.com + hmac-sha2-256 + hmac-sha2-512 + hmac-ripemd160 + hmac-ripemd160@openssh.com + hmac-sha1-96 + hmac-md5-96 + compression_algorithms: (2) + none + zlib@openssh.com + + OUTPUT + ) + end + end + end + + context "when the Nmap::XML::Port object does not have a service" + + context "when the Nmap::XML::Port object has scripts" + end + + describe "#highlight_scripts" do + let(:has_scripts) { port } + let(:pattern) { 'dss' } + + it "must print the script IDs and script outputs of all scripts, highlighting the pattern in bold red" do + subject.highlight_scripts(has_scripts,pattern) + + expect(stdout.string).to eq( + <<~OUTPUT + ssh-hostkey: + 1024 ac:00:a0:1a:82:ff:cc:55:99:dc:67:2b:34:97:6b:75 (DSA) + ssh-#{bold}#{red}dss#{reset_color}#{reset_intensity} AAAAB3NzaC1kc3MAAACBAOe8o59vFWZGaBmGPVeJBObEfi1AR8yEUYC/Ufkku3sKhGF7wM2m2ujIeZDK5vqeC0S5EN2xYo6FshCP4FQRYeTxD17nNO4PhwW65qAjDRRU0uHFfSAh5wk+vt4yQztOE++sTd1G9OBLzA8HO99qDmCAxb3zw+GQDEgPjzgyzGZ3AAAAFQCBmE1vROP8IaPkUmhM5xLFta/xHwAAAIEA3EwRfaeOPLL7TKDgGX67Lbkf9UtdlpCdC4doMjGgsznYMwWH6a7Lj3vi4/KmeZZdix6FMdFqq+2vrfT1DRqx0RS0XYdGxnkgS+2g333WYCrUkDCn6RPUWR/1TgGMPHCj7LWCa1ZwJwLWS2KX288Pa2gLOWuhZm2VYKSQx6NEDOIAAACBANxIfprSdBdbo4Ezrh6/X6HSvrhjtZ7MouStWaE714ByO5bS2coM9CyaCwYyrE5qzYiyIfb+1BG3O5nVdDuN95sQ/0bAdBKlkqLFvFqFjVbETF0ri3v97w6MpUawfF75ouDrQ4xdaUOLLEWTso6VFJcM6Jg9bDl0FA0uLZUSDEHL + 2048 20:3d:2d:44:62:2a:b0:5a:9d:b5:b3:05:14:c2:a6:b2 (RSA) + ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC6afooTZ9mVUGFNEhkMoRR1Btzu64XXwElhCsHw/zVlIx/HXylNbb9+11dm2VgJQ21pxkWDs+L6+EbYyDnvRURTrMTgHL0xseB0EkNqexs9hYZSiqtMx4jtGNtHvsMxZnbxvVUk2dasWvtBkn8J5JagSbzWTQo4hjKMOI1SUlXtiKxAs2F8wiq2EdSuKw/KNk8GfIp1TA+8ccGeAtnsVptTJ4D/8MhAWsROkQzOowQvnBBz2/8ecEvoMScaf+kDfNQowK3gENtSSOqYw9JLOza6YJBPL/aYuQQ0nJ74Rr5vL44aNIlrGI9jJc2x0bV7BeNA5kVuXsmhyfWbbkB8yGd + 256 96:02:bb:5e:57:54:1c:4e:45:2f:56:4c:4a:24:b2:57 (ECDSA) + ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMD46g67x6yWNjjQJnXhiz/TskHrqQ0uPcOspFrIYW382uOGzmWDZCFV8FbFwQyH90u+j0Qr1SGNAxBZMhOQ8pc= + + ssh2-enum-algos: + kex_algorithms: (8) + curve25519-sha256@libssh.org + ecdh-sha2-nistp256 + ecdh-sha2-nistp384 + ecdh-sha2-nistp521 + diffie-hellman-group-exchange-sha256 + diffie-hellman-group-exchange-sha1 + diffie-hellman-group14-sha1 + diffie-hellman-group1-sha1 + server_host_key_algorithms: (4) + ssh-rsa + ssh-#{bold}#{red}dss#{reset_color}#{reset_intensity} + ecdsa-sha2-nistp256 + ssh-ed25519 + encryption_algorithms: (16) + aes128-ctr + aes192-ctr + aes256-ctr + arcfour256 + arcfour128 + aes128-gcm@openssh.com + aes256-gcm@openssh.com + chacha20-poly1305@openssh.com + aes128-cbc + 3des-cbc + blowfish-cbc + cast128-cbc + aes192-cbc + aes256-cbc + arcfour + rijndael-cbc@lysator.liu.se + mac_algorithms: (19) + hmac-md5-etm@openssh.com + hmac-sha1-etm@openssh.com + umac-64-etm@openssh.com + umac-128-etm@openssh.com + hmac-sha2-256-etm@openssh.com + hmac-sha2-512-etm@openssh.com + hmac-ripemd160-etm@openssh.com + hmac-sha1-96-etm@openssh.com + hmac-md5-96-etm@openssh.com + hmac-md5 + hmac-sha1 + umac-64@openssh.com + umac-128@openssh.com + hmac-sha2-256 + hmac-sha2-512 + hmac-ripemd160 + hmac-ripemd160@openssh.com + hmac-sha1-96 + hmac-md5-96 + compression_algorithms: (2) + none + zlib@openssh.com + + OUTPUT + ) + end + end + + describe "#highlight_script" do + context "when the pattern exists in the script ID name" do + let(:pattern) { 'ssh2' } + + it "must print the script ID with the pattern highlighted in the script ID" do + subject.highlight_script(script,pattern) + + expect(stdout.string).to eq( + <<~OUTPUT + #{bold}#{red}ssh2#{reset_color}#{reset_intensity}-enum-algos: + kex_algorithms: (8) + curve25519-sha256@libssh.org + ecdh-sha2-nistp256 + ecdh-sha2-nistp384 + ecdh-sha2-nistp521 + diffie-hellman-group-exchange-sha256 + diffie-hellman-group-exchange-sha1 + diffie-hellman-group14-sha1 + diffie-hellman-group1-sha1 + server_host_key_algorithms: (4) + ssh-rsa + ssh-dss + ecdsa-sha2-nistp256 + ssh-ed25519 + encryption_algorithms: (16) + aes128-ctr + aes192-ctr + aes256-ctr + arcfour256 + arcfour128 + aes128-gcm@openssh.com + aes256-gcm@openssh.com + chacha20-poly1305@openssh.com + aes128-cbc + 3des-cbc + blowfish-cbc + cast128-cbc + aes192-cbc + aes256-cbc + arcfour + rijndael-cbc@lysator.liu.se + mac_algorithms: (19) + hmac-md5-etm@openssh.com + hmac-sha1-etm@openssh.com + umac-64-etm@openssh.com + umac-128-etm@openssh.com + hmac-sha2-256-etm@openssh.com + hmac-sha2-512-etm@openssh.com + hmac-ripemd160-etm@openssh.com + hmac-sha1-96-etm@openssh.com + hmac-md5-96-etm@openssh.com + hmac-md5 + hmac-sha1 + umac-64@openssh.com + umac-128@openssh.com + hmac-sha2-256 + hmac-sha2-512 + hmac-ripemd160 + hmac-ripemd160@openssh.com + hmac-sha1-96 + hmac-md5-96 + compression_algorithms: (2) + none + zlib@openssh.com + OUTPUT + ) + end + end + + context "when the pattern exists in the script output" do + let(:pattern) { 'rsa' } + + it "must print the script output with the pattern highlighted in the script output" do + subject.highlight_script(script,pattern) + + expect(stdout.string).to eq( + <<~OUTPUT + ssh2-enum-algos: + kex_algorithms: (8) + curve25519-sha256@libssh.org + ecdh-sha2-nistp256 + ecdh-sha2-nistp384 + ecdh-sha2-nistp521 + diffie-hellman-group-exchange-sha256 + diffie-hellman-group-exchange-sha1 + diffie-hellman-group14-sha1 + diffie-hellman-group1-sha1 + server_host_key_algorithms: (4) + ssh-#{bold}#{red}rsa#{reset_color}#{reset_intensity} + ssh-dss + ecdsa-sha2-nistp256 + ssh-ed25519 + encryption_algorithms: (16) + aes128-ctr + aes192-ctr + aes256-ctr + arcfour256 + arcfour128 + aes128-gcm@openssh.com + aes256-gcm@openssh.com + chacha20-poly1305@openssh.com + aes128-cbc + 3des-cbc + blowfish-cbc + cast128-cbc + aes192-cbc + aes256-cbc + arcfour + rijndael-cbc@lysator.liu.se + mac_algorithms: (19) + hmac-md5-etm@openssh.com + hmac-sha1-etm@openssh.com + umac-64-etm@openssh.com + umac-128-etm@openssh.com + hmac-sha2-256-etm@openssh.com + hmac-sha2-512-etm@openssh.com + hmac-ripemd160-etm@openssh.com + hmac-sha1-96-etm@openssh.com + hmac-md5-96-etm@openssh.com + hmac-md5 + hmac-sha1 + umac-64@openssh.com + umac-128@openssh.com + hmac-sha2-256 + hmac-sha2-512 + hmac-ripemd160 + hmac-ripemd160@openssh.com + hmac-sha1-96 + hmac-md5-96 + compression_algorithms: (2) + none + zlib@openssh.com + OUTPUT + ) + end + end + end + + describe "#highlight" do + let(:text) { "The quick brown fox jumps over the lazy dog" } + let(:pattern) { "jumps" } + + it "must print the text with the pattern highlighted in red" do + expect(subject.highlight(text,pattern)).to eq( + "The quick brown fox #{bold}#{red}#{pattern}#{reset_color}#{reset_intensity} over the lazy dog" + ) + end + end +end