ruby-nmap 0.9.3 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. checksums.yaml +5 -5
  2. data/.document +1 -0
  3. data/.editorconfig +11 -0
  4. data/.github/workflows/ruby.yml +31 -0
  5. data/ChangeLog.md +122 -67
  6. data/Gemfile +11 -5
  7. data/LICENSE.txt +1 -1
  8. data/README.md +88 -50
  9. data/Rakefile +8 -3
  10. data/UPGRADING.md +47 -0
  11. data/gemspec.yml +6 -6
  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 +46 -44
  42. data/ruby-nmap.gemspec +38 -83
  43. data/spec/command_spec.rb +726 -0
  44. data/spec/fixtures/down_host_scan.xml +16 -0
  45. data/spec/{local_scan.xml → fixtures/local_scan.xml} +1 -1
  46. data/spec/{scan.xml → fixtures/scan.xml} +1 -1
  47. data/spec/spec_helper.rb +2 -2
  48. data/spec/{address_spec.rb → xml/address_spec.rb} +2 -2
  49. data/spec/{cpe → xml/cpe}/url_spec.rb +1 -1
  50. data/spec/{cpe_examples.rb → xml/cpe_examples.rb} +1 -1
  51. data/spec/{hop_spec.rb → xml/hop_spec.rb} +2 -2
  52. data/spec/{host_script_spec.rb → xml/host_script_spec.rb} +2 -2
  53. data/spec/{host_spec.rb → xml/host_spec.rb} +12 -8
  54. data/spec/{hostname_spec.rb → xml/hostname_spec.rb} +2 -2
  55. data/spec/{ip_id_sequence_spec.rb → xml/ip_id_sequence_spec.rb} +3 -3
  56. data/spec/{os_class_spec.rb → xml/os_class_spec.rb} +3 -3
  57. data/spec/{os_match_spec.rb → xml/os_match_spec.rb} +2 -2
  58. data/spec/{os_spec.rb → xml/os_spec.rb} +3 -3
  59. data/spec/{port_spec.rb → xml/port_spec.rb} +10 -5
  60. data/spec/{postscript_spec.rb → xml/postscript_spec.rb} +2 -2
  61. data/spec/{prescript_spec.rb → xml/prescript_spec.rb} +2 -2
  62. data/spec/{run_stat_spec.rb → xml/run_stat_spec.rb} +2 -2
  63. data/spec/{scan_spec.rb → xml/scan_spec.rb} +2 -2
  64. data/spec/{scan_task_spec.rb → xml/scan_task_spec.rb} +6 -6
  65. data/spec/{scanner_spec.rb → xml/scanner_spec.rb} +3 -3
  66. data/spec/xml/script_spec.rb +137 -0
  67. data/spec/xml/scripts_examples.rb +19 -0
  68. data/spec/{sequence_examples.rb → xml/sequence_examples.rb} +1 -0
  69. data/spec/{service_spec.rb → xml/service_spec.rb} +31 -5
  70. data/spec/{status_spec.rb → xml/status_spec.rb} +4 -3
  71. data/spec/{tcp_sequence_spec.rb → xml/tcp_sequence_spec.rb} +3 -3
  72. data/spec/{tcp_ts_sequence_spec.rb → xml/tcp_ts_sequence_spec.rb} +3 -3
  73. data/spec/{traceroute_spec.rb → xml/traceroute_spec.rb} +3 -3
  74. data/spec/{uptime_spec.rb → xml/uptime_spec.rb} +2 -2
  75. data/spec/xml_spec.rb +93 -45
  76. metadata +78 -99
  77. data/.travis.yml +0 -14
  78. data/lib/nmap/address.rb +0 -34
  79. data/lib/nmap/cpe/url.rb +0 -78
  80. data/lib/nmap/cpe.rb +0 -45
  81. data/lib/nmap/hop.rb +0 -20
  82. data/lib/nmap/host.rb +0 -586
  83. data/lib/nmap/host_script.rb +0 -18
  84. data/lib/nmap/hostname.rb +0 -42
  85. data/lib/nmap/ip_id_sequence.rb +0 -24
  86. data/lib/nmap/os.rb +0 -127
  87. data/lib/nmap/os_class.rb +0 -82
  88. data/lib/nmap/os_match.rb +0 -18
  89. data/lib/nmap/port.rb +0 -99
  90. data/lib/nmap/postscript.rb +0 -16
  91. data/lib/nmap/prescript.rb +0 -16
  92. data/lib/nmap/program.rb +0 -102
  93. data/lib/nmap/run_stat.rb +0 -20
  94. data/lib/nmap/scan.rb +0 -34
  95. data/lib/nmap/scan_task.rb +0 -50
  96. data/lib/nmap/scanner.rb +0 -18
  97. data/lib/nmap/scripts.rb +0 -71
  98. data/lib/nmap/sequence.rb +0 -50
  99. data/lib/nmap/service.rb +0 -170
  100. data/lib/nmap/status.rb +0 -18
  101. data/lib/nmap/task.rb +0 -381
  102. data/lib/nmap/tcp_sequence.rb +0 -46
  103. data/lib/nmap/tcp_ts_sequence.rb +0 -22
  104. data/lib/nmap/traceroute.rb +0 -71
  105. data/lib/nmap/uptime.rb +0 -20
  106. data/spec/scripts_examples.rb +0 -35
  107. 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