ruby-nmap 0.10.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/.document +1 -0
  3. data/.editorconfig +11 -0
  4. data/.github/workflows/ruby.yml +31 -0
  5. data/ChangeLog.md +118 -71
  6. data/Gemfile +11 -5
  7. data/LICENSE.txt +1 -1
  8. data/README.md +88 -50
  9. data/Rakefile +5 -0
  10. data/UPGRADING.md +47 -0
  11. data/gemspec.yml +5 -5
  12. data/lib/nmap/command.rb +765 -0
  13. data/lib/nmap/version.rb +1 -1
  14. data/lib/nmap/xml/address.rb +38 -0
  15. data/lib/nmap/xml/cpe/url.rb +80 -0
  16. data/lib/nmap/xml/cpe.rb +47 -0
  17. data/lib/nmap/xml/hop.rb +22 -0
  18. data/lib/nmap/xml/host.rb +546 -0
  19. data/lib/nmap/xml/host_script.rb +26 -0
  20. data/lib/nmap/xml/hostname.rb +44 -0
  21. data/lib/nmap/xml/ip_id_sequence.rb +26 -0
  22. data/lib/nmap/xml/os.rb +131 -0
  23. data/lib/nmap/xml/os_class.rb +86 -0
  24. data/lib/nmap/xml/os_match.rb +22 -0
  25. data/lib/nmap/xml/port.rb +114 -0
  26. data/lib/nmap/xml/postscript.rb +26 -0
  27. data/lib/nmap/xml/prescript.rb +26 -0
  28. data/lib/nmap/xml/run_stat.rb +22 -0
  29. data/lib/nmap/xml/scan.rb +38 -0
  30. data/lib/nmap/xml/scan_task.rb +55 -0
  31. data/lib/nmap/xml/scanner.rb +22 -0
  32. data/lib/nmap/xml/script.rb +110 -0
  33. data/lib/nmap/xml/scripts.rb +33 -0
  34. data/lib/nmap/xml/sequence.rb +52 -0
  35. data/lib/nmap/xml/service.rb +172 -0
  36. data/lib/nmap/xml/status.rb +22 -0
  37. data/lib/nmap/xml/tcp_sequence.rb +48 -0
  38. data/lib/nmap/xml/tcp_ts_sequence.rb +26 -0
  39. data/lib/nmap/xml/traceroute.rb +73 -0
  40. data/lib/nmap/xml/uptime.rb +22 -0
  41. data/lib/nmap/xml.rb +31 -44
  42. data/spec/command_spec.rb +726 -0
  43. data/spec/fixtures/down_host_scan.xml +16 -0
  44. data/spec/{address_spec.rb → xml/address_spec.rb} +2 -2
  45. data/spec/{cpe → xml/cpe}/url_spec.rb +1 -1
  46. data/spec/{cpe_examples.rb → xml/cpe_examples.rb} +1 -1
  47. data/spec/{hop_spec.rb → xml/hop_spec.rb} +2 -2
  48. data/spec/{host_script_spec.rb → xml/host_script_spec.rb} +2 -2
  49. data/spec/{host_spec.rb → xml/host_spec.rb} +8 -8
  50. data/spec/{hostname_spec.rb → xml/hostname_spec.rb} +2 -2
  51. data/spec/{ip_id_sequence_spec.rb → xml/ip_id_sequence_spec.rb} +3 -3
  52. data/spec/{os_class_spec.rb → xml/os_class_spec.rb} +3 -3
  53. data/spec/{os_match_spec.rb → xml/os_match_spec.rb} +2 -2
  54. data/spec/{os_spec.rb → xml/os_spec.rb} +3 -3
  55. data/spec/{port_spec.rb → xml/port_spec.rb} +4 -5
  56. data/spec/{postscript_spec.rb → xml/postscript_spec.rb} +2 -2
  57. data/spec/{prescript_spec.rb → xml/prescript_spec.rb} +2 -2
  58. data/spec/{run_stat_spec.rb → xml/run_stat_spec.rb} +2 -2
  59. data/spec/{scan_spec.rb → xml/scan_spec.rb} +2 -2
  60. data/spec/{scan_task_spec.rb → xml/scan_task_spec.rb} +6 -6
  61. data/spec/{scanner_spec.rb → xml/scanner_spec.rb} +3 -3
  62. data/spec/xml/script_spec.rb +137 -0
  63. data/spec/xml/scripts_examples.rb +19 -0
  64. data/spec/{sequence_examples.rb → xml/sequence_examples.rb} +1 -0
  65. data/spec/{service_spec.rb → xml/service_spec.rb} +31 -5
  66. data/spec/{status_spec.rb → xml/status_spec.rb} +2 -2
  67. data/spec/{tcp_sequence_spec.rb → xml/tcp_sequence_spec.rb} +3 -3
  68. data/spec/{tcp_ts_sequence_spec.rb → xml/tcp_ts_sequence_spec.rb} +3 -3
  69. data/spec/{traceroute_spec.rb → xml/traceroute_spec.rb} +3 -3
  70. data/spec/{uptime_spec.rb → xml/uptime_spec.rb} +2 -2
  71. data/spec/xml_spec.rb +73 -44
  72. metadata +72 -66
  73. data/.travis.yml +0 -16
  74. data/lib/nmap/address.rb +0 -34
  75. data/lib/nmap/cpe/url.rb +0 -78
  76. data/lib/nmap/cpe.rb +0 -45
  77. data/lib/nmap/hop.rb +0 -20
  78. data/lib/nmap/host.rb +0 -587
  79. data/lib/nmap/host_script.rb +0 -18
  80. data/lib/nmap/hostname.rb +0 -42
  81. data/lib/nmap/ip_id_sequence.rb +0 -24
  82. data/lib/nmap/os.rb +0 -127
  83. data/lib/nmap/os_class.rb +0 -82
  84. data/lib/nmap/os_match.rb +0 -18
  85. data/lib/nmap/port.rb +0 -110
  86. data/lib/nmap/postscript.rb +0 -16
  87. data/lib/nmap/prescript.rb +0 -16
  88. data/lib/nmap/program.rb +0 -102
  89. data/lib/nmap/run_stat.rb +0 -20
  90. data/lib/nmap/scan.rb +0 -34
  91. data/lib/nmap/scan_task.rb +0 -53
  92. data/lib/nmap/scanner.rb +0 -18
  93. data/lib/nmap/scripts.rb +0 -71
  94. data/lib/nmap/sequence.rb +0 -50
  95. data/lib/nmap/service.rb +0 -170
  96. data/lib/nmap/status.rb +0 -18
  97. data/lib/nmap/task.rb +0 -387
  98. data/lib/nmap/tcp_sequence.rb +0 -46
  99. data/lib/nmap/tcp_ts_sequence.rb +0 -22
  100. data/lib/nmap/traceroute.rb +0 -71
  101. data/lib/nmap/uptime.rb +0 -20
  102. data/spec/scripts_examples.rb +0 -35
  103. data/spec/task_spec.rb +0 -150
@@ -0,0 +1,131 @@
1
+ require 'nmap/xml/os_class'
2
+ require 'nmap/xml/os_match'
3
+
4
+ module Nmap
5
+ class XML
6
+ #
7
+ # Wraps the `os` XML element.
8
+ #
9
+ # @since 1.0.0
10
+ #
11
+ class OS
12
+
13
+ include Enumerable
14
+
15
+ #
16
+ # Creates a new OS object.
17
+ #
18
+ # @param [Nokogiri::XML::Node] node
19
+ # The node that contains the OS guessing information.
20
+ #
21
+ def initialize(node)
22
+ @node = node
23
+ end
24
+
25
+ #
26
+ # Parses the OS class information.
27
+ #
28
+ # @yield [class]
29
+ # Passes each OS class to the given block.
30
+ #
31
+ # @yieldparam [OSClass] class
32
+ # The OS class information.
33
+ #
34
+ # @return [OS, Enumerator]
35
+ # The OS information. If no block was given, an enumerator object
36
+ # will be returned.
37
+ #
38
+ def each_class
39
+ return enum_for(__method__) unless block_given?
40
+
41
+ @node.xpath("osmatch/osclass").each do |osclass|
42
+ yield OSClass.new(osclass)
43
+ end
44
+
45
+ return self
46
+ end
47
+
48
+ #
49
+ # Parses the OS class information.
50
+ #
51
+ # @return [Array<OSClass>]
52
+ # The OS class information.
53
+ #
54
+ def classes
55
+ each_class.to_a
56
+ end
57
+
58
+ #
59
+ # Parses the OS match information.
60
+ #
61
+ # @yield [match]
62
+ # Passes each OS match to the given block.
63
+ #
64
+ # @yieldparam [OSMatch] class
65
+ # The OS match information.
66
+ #
67
+ # @return [OS, Enumerator]
68
+ # The OS information. If no block was given, an enumerator object
69
+ # will be returned.
70
+ #
71
+ def each_match
72
+ return enum_for(__method__) unless block_given?
73
+
74
+ @node.xpath("osmatch").map do |osclass|
75
+ os_match = OSMatch.new(
76
+ osclass['name'],
77
+ osclass['accuracy'].to_i
78
+ )
79
+
80
+ yield os_match
81
+ end
82
+
83
+ return self
84
+ end
85
+
86
+ #
87
+ # Parses the OS match information.
88
+ #
89
+ # @return [Array<OSMatch>]
90
+ # The OS match information.
91
+ #
92
+ def matches
93
+ each_match.to_a
94
+ end
95
+
96
+ #
97
+ # Parses the ports used for guessing the OS.
98
+ #
99
+ # @return [Array<Integer>]
100
+ # The ports used.
101
+ #
102
+ def ports_used
103
+ @ports_used ||= @node.xpath("portused/@portid").map do |port|
104
+ port.inner_text.to_i
105
+ end
106
+ end
107
+
108
+ #
109
+ # Parses the OS fingerprint used by Nmap.
110
+ #
111
+ # @return [String]
112
+ # The OS fingerprint.
113
+ #
114
+ def fingerprint
115
+ @fingerprint ||= if (fingerprint = @node.at_xpath("osfingerprint/@fingerprint"))
116
+ fingerprint.inner_text
117
+ end
118
+ end
119
+
120
+ #
121
+ # Parses the OS match information.
122
+ #
123
+ # @see #each_match
124
+ #
125
+ def each(&block)
126
+ each_match(&block)
127
+ end
128
+
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,86 @@
1
+ require 'nmap/xml/cpe'
2
+
3
+ module Nmap
4
+ class XML
5
+ #
6
+ # Represents an {OS} class.
7
+ #
8
+ # @since 1.0.0
9
+ #
10
+ class OSClass
11
+
12
+ include CPE
13
+
14
+ #
15
+ # Initializes the OS.
16
+ #
17
+ # @param [Nokogiri::XML::Node] node
18
+ # The node that contains the OS Class information.
19
+ #
20
+ def initialize(node)
21
+ @node = node
22
+ end
23
+
24
+ #
25
+ # The OS type.
26
+ #
27
+ # @return [String]
28
+ #
29
+ def type
30
+ @type ||= if @node['type']
31
+ @node['type'].to_sym
32
+ end
33
+ end
34
+
35
+ #
36
+ # The OS vendor.
37
+ #
38
+ # @return [String]
39
+ #
40
+ def vendor
41
+ @vendor ||= @node.get_attribute('vendor')
42
+ end
43
+
44
+ #
45
+ # The OS family.
46
+ #
47
+ # @return [Symbol, nil]
48
+ #
49
+ def family
50
+ @family ||= @node.get_attribute('osfamily').to_sym
51
+ end
52
+
53
+ #
54
+ # The OS generation.
55
+ #
56
+ # @return [Symbol, nil]
57
+ #
58
+ def gen
59
+ @gen ||= if @node['osgen']
60
+ @node['osgen'].to_sym
61
+ end
62
+ end
63
+
64
+ #
65
+ # The accuracy of the OS class information.
66
+ #
67
+ # @return [Integer]
68
+ # Returns a number between 0 and 10.
69
+ #
70
+ def accuracy
71
+ @accuracy ||= @node.get_attribute('accuracy').to_i
72
+ end
73
+
74
+ #
75
+ # Converts the OS class to a String.
76
+ #
77
+ # @return [String]
78
+ # The String form of the OS class.
79
+ #
80
+ def to_s
81
+ "#{self.type} #{self.vendor} (#{self.accuracy}%)"
82
+ end
83
+
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,22 @@
1
+ module Nmap
2
+ class XML
3
+ #
4
+ # Represents a match for a specific {OS}.
5
+ #
6
+ # @since 1.0.0
7
+ #
8
+ class OSMatch < Struct.new(:name, :accuracy)
9
+
10
+ #
11
+ # Converts the OS match to a String.
12
+ #
13
+ # @return [String]
14
+ # The String form of the OS match.
15
+ #
16
+ def to_s
17
+ "#{self.name} (#{self.accuracy}%)"
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,114 @@
1
+ require 'nmap/xml/service'
2
+ require 'nmap/xml/scripts'
3
+
4
+ module Nmap
5
+ class XML
6
+ #
7
+ # Wraps a `port` XML element.
8
+ #
9
+ # @since 1.0.0
10
+ #
11
+ class Port
12
+
13
+ include Scripts
14
+
15
+ #
16
+ # Creates a new Port object.
17
+ #
18
+ # @param [Nokogiri::XML::Element] node
19
+ # The XML `port` element.
20
+ #
21
+ def initialize(node)
22
+ @node = node
23
+ end
24
+
25
+ #
26
+ # The protocol the port runs on
27
+ #
28
+ # @return [Symbol]
29
+ # The protocol of the port.
30
+ #
31
+ def protocol
32
+ @protocol ||= @node['protocol'].to_sym
33
+ end
34
+
35
+ #
36
+ # The port number.
37
+ #
38
+ # @return [Integer]
39
+ # The number of the port.
40
+ #
41
+ def number
42
+ @number ||= @node['portid'].to_i
43
+ end
44
+
45
+ #
46
+ # The state of the port.
47
+ #
48
+ # @return [Symbol]
49
+ # The state of the port (`:open`, `:filtered` or `:closed`).
50
+ #
51
+ def state
52
+ @state ||= @node.at_xpath('state/@state').inner_text.to_sym
53
+ end
54
+
55
+ #
56
+ # The reason the port was discovered.
57
+ #
58
+ # @return [String]
59
+ # How the port was discovered.
60
+ #
61
+ def reason
62
+ @reason ||= @node.at_xpath('state/@reason').inner_text
63
+ end
64
+
65
+ #
66
+ # The reason TTL.
67
+ #
68
+ # @return [Integer]
69
+ #
70
+ # @since 0.10.0
71
+ #
72
+ def reason_ttl
73
+ @reason ||= @node.at_xpath('state/@reason_ttl').inner_text.to_i
74
+ end
75
+
76
+ #
77
+ # The fingerprinted service of the port.
78
+ #
79
+ # @return [Service]
80
+ # The service detected on the port.
81
+ #
82
+ # @since 0.6.0
83
+ #
84
+ def service
85
+ @service_info ||= if (service = @node.at_xpath('service'))
86
+ Service.new(service)
87
+ end
88
+ end
89
+
90
+ alias to_i number
91
+
92
+ #
93
+ # Converts the port to a String.
94
+ #
95
+ # @return [String]
96
+ # The port number.
97
+ #
98
+ def to_s
99
+ number.to_s
100
+ end
101
+
102
+ #
103
+ # Inspects the port.
104
+ #
105
+ # @return [String]
106
+ # The inspected port.
107
+ #
108
+ def inspect
109
+ "#<#{self.class}: #{self}>"
110
+ end
111
+
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,26 @@
1
+ require 'nmap/xml/scripts'
2
+
3
+ module Nmap
4
+ class XML
5
+ #
6
+ # Represents the `postscript` element.
7
+ #
8
+ # @since 1.0.0
9
+ #
10
+ class Postscript
11
+
12
+ include Scripts
13
+
14
+ #
15
+ # Initializes the Postscript object.
16
+ #
17
+ # @param [Nokogiri::XML::Node] node
18
+ # The XML node that contains the host information.
19
+ #
20
+ def initialize(node)
21
+ @node = node
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ require 'nmap/xml/scripts'
2
+
3
+ module Nmap
4
+ class XML
5
+ #
6
+ # Represents the `prescript` element.
7
+ #
8
+ # @since 1.0.0
9
+ #
10
+ class Prescript
11
+
12
+ include Scripts
13
+
14
+ #
15
+ # Initializes the Prescript object.
16
+ #
17
+ # @param [Nokogiri::XML::Node] node
18
+ # The XML node that contains the host information.
19
+ #
20
+ def initialize(node)
21
+ @node = node
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,22 @@
1
+ module Nmap
2
+ class XML
3
+ #
4
+ # Represents the runstats of a scan.
5
+ #
6
+ # @since 1.0.0
7
+ #
8
+ class RunStat < Struct.new(:end_time, :elapsed, :summary, :exit_status)
9
+
10
+ #
11
+ # Converts the stats to a String.
12
+ #
13
+ # @return [String]
14
+ # The String form of the scan.
15
+ #
16
+ def to_s
17
+ "#{self.end_time} #{self.elapsed} #{self.exit_status}"
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,38 @@
1
+ module Nmap
2
+ class XML
3
+ #
4
+ # Represents an Nmap scan.
5
+ #
6
+ # @since 1.0.0
7
+ #
8
+ class Scan < Struct.new(:type, :protocol, :services)
9
+
10
+ #
11
+ # Creates a new Scan object.
12
+ #
13
+ # @param [Symbol] type
14
+ # The type of the scan.
15
+ #
16
+ # @param [Symbol] protocol
17
+ # The protocol used for the scan.
18
+ #
19
+ # @param [Array<Integer, Rage>] services
20
+ # The port numbers scanned.
21
+ #
22
+ def initialize(type,protocol,services=[])
23
+ super(type,protocol,services)
24
+ end
25
+
26
+ #
27
+ # Converts the scan to a String.
28
+ #
29
+ # @return [String]
30
+ # The String form of the scan.
31
+ #
32
+ def to_s
33
+ "#{self.protocol} #{self.type}"
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,55 @@
1
+ module Nmap
2
+ class XML
3
+ #
4
+ # @since 1.0.0
5
+ #
6
+ class ScanTask < Struct.new(:name, :start_time, :end_time, :extra_info)
7
+
8
+ #
9
+ # Creates a new ScanTask object.
10
+ #
11
+ # @param [String] name
12
+ # The name of the scan task.
13
+ #
14
+ # @param [Time] start_time
15
+ # The time the scan task begun.
16
+ #
17
+ # @param [Time] end_time
18
+ # The time the scan task ended.
19
+ #
20
+ # @param [String] extra_info
21
+ # Any extra information relating to the scan task.
22
+ #
23
+ # @since 0.1.2
24
+ #
25
+ def initialize(name,start_time,end_time,extra_info=nil)
26
+ super
27
+ end
28
+
29
+ #
30
+ # The duration of the scan task.
31
+ #
32
+ # @return [Integer]
33
+ # The number of seconds it took the scan task to complete.
34
+ #
35
+ # @since 0.1.2
36
+ #
37
+ def duration
38
+ (self.end_time - self.start_time)
39
+ end
40
+
41
+ #
42
+ # Converts the scan task to a String.
43
+ #
44
+ # @return [String]
45
+ # The String form of the scan task.
46
+ #
47
+ # @since 0.1.2
48
+ #
49
+ def to_s
50
+ "#{self.start_time}: #{self.name} (#{self.extra_info})"
51
+ end
52
+
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,22 @@
1
+ module Nmap
2
+ class XML
3
+ #
4
+ # Describes the `nmap` command.
5
+ #
6
+ # @since 1.0.0
7
+ #
8
+ class Scanner < Struct.new(:name, :version, :arguments, :start_time)
9
+
10
+ #
11
+ # Converts the scanner to a String.
12
+ #
13
+ # @return [String]
14
+ # The scanner name and arguments.
15
+ #
16
+ def to_s
17
+ "#{self.name} #{self.arguments}"
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,110 @@
1
+ module Nmap
2
+ class XML
3
+ #
4
+ # Wraps a `script` XML element.
5
+ #
6
+ # @since 1.0.0
7
+ #
8
+ class Script
9
+
10
+ #
11
+ # Initializes a new Script object.
12
+ #
13
+ # @param [Nokogiri::XML::Node] node
14
+ # The XML node that contains the host information.
15
+ #
16
+ def initialize(node)
17
+ @node = node
18
+ end
19
+
20
+ #
21
+ # The ID of the NSE script.
22
+ #
23
+ # @return [String]
24
+ #
25
+ def id
26
+ @id ||= @node['id']
27
+ end
28
+
29
+ #
30
+ # The text output from the NSE script.
31
+ #
32
+ # @return [String]
33
+ #
34
+ def output
35
+ @output ||= @node['output']
36
+ end
37
+
38
+ #
39
+ # Parses the structured data within the `<script>` XML element.
40
+ #
41
+ # @return [Hash{String => Hash,Array,String}, Array<Hash>, Array<String>, nil]
42
+ # The parsed data.
43
+ #
44
+ def data
45
+ @data ||= parse_table(@node)
46
+ end
47
+
48
+ private
49
+
50
+ #
51
+ # Parses the `<table>` XML child elements within the given node.
52
+ #
53
+ # @param [Nokogiri::XML::Node] node
54
+ # The XML node that contains the host information.
55
+ #
56
+ # @return [Hash{String => Hash,Array,String}, Array<Hash>, Array<String>, nil]
57
+ # The parsed data.
58
+ #
59
+ def parse_tables(node)
60
+ if (tables = @node.xpath('table')).empty?
61
+ return
62
+ end
63
+
64
+ # check for named tables
65
+ if tables.all? { |table| table.has_attribute?('key') }
66
+ Hash[
67
+ tables.map { |table|
68
+ [table['key'], parse_table(table)]
69
+ }
70
+ ]
71
+ else
72
+ tables.map(&method(:parse_table))
73
+ end
74
+ end
75
+
76
+ #
77
+ # Parses the contents of a `<table>` XML element.
78
+ #
79
+ # @param [Nokogiri::XML::Node] node
80
+ # The XML node that contains the host information.
81
+ #
82
+ # @return [Hash{String => Hash,Array<String>,String}, Array<String>, nil]
83
+ # The parsed data.
84
+ #
85
+ def parse_table(node)
86
+ # check for nested tables
87
+ if node.xpath('count(table)') > 0
88
+ return parse_tables(node)
89
+ end
90
+
91
+ if (elems = node.xpath('elem')).empty?
92
+ # return nil if there are no elements
93
+ return
94
+ end
95
+
96
+ # check for named elements
97
+ if elems.all? { |elem| elem.has_attribute?('key') }
98
+ Hash[
99
+ elems.map { |elem|
100
+ [elem['key'], elem.inner_text]
101
+ }
102
+ ]
103
+ else
104
+ elems.map(&:inner_text)
105
+ end
106
+ end
107
+
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,33 @@
1
+ require 'nmap/xml/script'
2
+
3
+ module Nmap
4
+ class XML
5
+ #
6
+ # Mixin that adds methods for parsing `<script>` XML elements.
7
+ #
8
+ # @since 1.0.0
9
+ #
10
+ module Scripts
11
+ #
12
+ # The output from the NSE script's output and structured data.
13
+ #
14
+ # @return [Hash{String => Script}]
15
+ # The NSE script names and output.
16
+ #
17
+ # @since 0.3.0
18
+ #
19
+ def scripts
20
+ unless @scripts
21
+ @scripts = {}
22
+
23
+ @node.xpath('script').each do |script|
24
+ @scripts[script['id']] = Script.new(script)
25
+ end
26
+ end
27
+
28
+ return @scripts
29
+ end
30
+
31
+ end
32
+ end
33
+ end