ruby-nikto 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4fcc497451a7d1d6bd7ed21bd253f113afd7907e7846d78cd78fdacfa5b05038
4
+ data.tar.gz: 4e3cf53c1f2a0354720e1ebe13e4e3f2fd8da7a82bf21f9687bfdfa9a758a9da
5
+ SHA512:
6
+ metadata.gz: 388a8492bb9faa2a4ee6c3235ffd5119511b4993cac47e984f45f1498c4d12a00e6c354b1255fa9caeeb38d767cf826c91acbf60af1dfa0174ba094e28c53e16
7
+ data.tar.gz: 9ac99ff184756ae910eccc8b555393ac99f7e13fe01f16bf105c24645b805d09da9d4ddf407b2fc794373882b78cf01a3a387b6ad94e450a4e947d4d6f62defc
data/.editorconfig ADDED
@@ -0,0 +1,11 @@
1
+ root = true
2
+
3
+ [*]
4
+ end_of_line = lf
5
+ insert_final_newline = true
6
+ tab_width = 8
7
+ trim_trailing_whitespace = true
8
+
9
+ [{Gemfile,Rakefile,*.rb,*.gemspec,*.yml}]
10
+ indent_style = space
11
+ indent_size = 2
@@ -0,0 +1,31 @@
1
+ name: CI
2
+
3
+ on: [ push, pull_request ]
4
+
5
+ jobs:
6
+ tests:
7
+ runs-on: ubuntu-latest
8
+ strategy:
9
+ fail-fast: false
10
+ matrix:
11
+ ruby:
12
+ - 2.6
13
+ - 2.7
14
+ - 3.0
15
+ - jruby
16
+ - truffleruby
17
+ name: Ruby ${{ matrix.ruby }}
18
+ steps:
19
+ - uses: actions/checkout@v2
20
+ - name: Set up Ruby
21
+ uses: ruby/setup-ruby@v1
22
+ with:
23
+ ruby-version: ${{ matrix.ruby }}
24
+ - name: Install libxml2-dev and libxslt1-dev
25
+ run: |
26
+ sudo apt update -y && \
27
+ sudo apt install -y --no-install-recommends --no-install-suggests libxml2-dev libxslt1-dev
28
+ - name: Install dependencies
29
+ run: bundle install --jobs 4 --retry 3
30
+ - name: Run tests
31
+ run: bundle exec rake test
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /Gemfile.lock
2
+ /doc
3
+ /pkg
4
+ .DS_Store
5
+ .yardoc
6
+ *.db
7
+ *.log
8
+ *.swp
9
+ *~
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour --format documentation
data/.specopts ADDED
@@ -0,0 +1 @@
1
+ --colour --format specdoc
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --markup markdown --title 'Ruby Nikto Documentation' --protected --files ChangeLog.md,LICENSE.txt
data/ChangeLog.md ADDED
@@ -0,0 +1,9 @@
1
+ ### 0.1.0 / 2021-11-30
2
+
3
+ * Initial release:
4
+ * Added {Nikto::Command}.
5
+ * Added {Nikto::XML}.
6
+ * Added {Nikto::XML::ScanDetails}.
7
+ * Added {Nikto::XML::Item}.
8
+ * Added {Nikto::XML::Statistics}.
9
+
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ # gem 'command_mapper', '~> 0.1.1', github: 'postmodern/command_mapper.rb'
6
+
7
+ group :development do
8
+ gem 'rake'
9
+ gem 'rubygems-tasks', '~> 0.2'
10
+ gem 'rspec', '~> 3.0'
11
+ gem 'simplecov', '~> 0.7'
12
+
13
+ gem 'kramdown'
14
+ gem 'yard', '~> 0.9'
15
+ gem 'yard-spellcheck', require: false
16
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ (The MIT License)
2
+
3
+ Copyright (c) 2009-2021 Hal Brodigan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ 'Software'), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # ruby-nikto
2
+
3
+ [![CI](https://github.com/postmodern/ruby-nikto/actions/workflows/ruby.yml/badge.svg)](https://github.com/postmodern/ruby-nikto/actions/workflows/ruby.yml)
4
+ [![Gem Version](https://badge.fury.io/rb/ruby-nikto.svg)](https://badge.fury.io/rb/ruby-nikto)
5
+
6
+ * [Source](https://github.com/sophsec/ruby-nikto)
7
+ * [Issues](https://github.com/sophsec/ruby-nikto/issues)
8
+
9
+ ## Description
10
+
11
+ A Ruby interface to [nikto].
12
+
13
+ ## Features
14
+
15
+ * Provides a [Ruby interface][Nikto::Command] for running the `nikto` utility.
16
+ * Provides a [parser][Nikto::XML] for enumerating Nikto XML scan files.
17
+
18
+ [Nikto::Command]: https://rubydoc.info/gems/ruby-nikto/Nikto/Command
19
+ [Nikto::XML]: https://rubydoc.info/gems/ruby-nikto/Nikto/XML
20
+
21
+ ## Examples
22
+
23
+ Run Nikto from Ruby:
24
+
25
+ ```ruby
26
+ require 'nikto/command'
27
+
28
+ Nikto::Command.run(host: 'example.com', output: 'nikto.xml')
29
+ ```
30
+
31
+ Parse Nikto XML scan files:
32
+
33
+ ```ruby
34
+ require 'nikto/xml'
35
+
36
+ Nikto::XML.open('nikto.xml') do |xml|
37
+ xml.each_scan_details do |scan_details|
38
+ puts "#{scan_details.site_name}"
39
+
40
+ scan_details.each_item do |item|
41
+ puts " #{item.uri}"
42
+ puts
43
+ puts " #{item.description}"
44
+ puts
45
+ end
46
+ end
47
+ end
48
+ ```
49
+
50
+ ## Requirements
51
+
52
+ * [nikto] >= 2.1.0
53
+ * [command_mapper](http://github.com/postmodern/command_mapper.rb#readme) ~> 0.1
54
+ * [nokogiri](https://github.com/sparklemotion/nokogiri#readme) ~> 1.0
55
+
56
+ ## Install
57
+
58
+ ```shell
59
+ $ gem install ruby-nikto
60
+ ```
61
+
62
+ ### gemspec
63
+
64
+ ```ruby
65
+ gemspec.add_dependency 'ruby-nikto', '~> 0.1'
66
+ ```
67
+
68
+ ### Gemfile
69
+
70
+ ```ruby
71
+ gem 'ruby-nikto', '~> 0.1'
72
+ ```
73
+
74
+ ## License
75
+
76
+ Copyright (c) 2009-2021 Hal Brodigan
77
+
78
+ See {file:LICENSE.txt} for license information.
79
+
80
+ [nikto]: https://github.com/sullo/nikto#readme
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'rubygems/tasks'
2
+ Gem::Tasks.new
3
+
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new
6
+ task :test => :spec
7
+ task :default => :spec
8
+
9
+ require 'yard'
10
+ YARD::Rake::YardocTask.new
data/gemspec.yml ADDED
@@ -0,0 +1,24 @@
1
+ name: ruby-nikto
2
+ summary: A Ruby interface to Nikto.
3
+ description: A Ruby interface to Nikto, the Web Server scanner.
4
+ license: MIT
5
+ authors: Postmodern
6
+ email: postmodern.mod3@gmail.com
7
+ homepage: http://github.com/sophsec/ruby-nikto
8
+ has_yard: true
9
+
10
+ metadata:
11
+ documentation_uri: https://rubydoc.info/gems/ruby-nikto
12
+ source_code_uri: https://github.com/postmodern/ruby-nikto
13
+ bug_tracker_uri: https://github.com/postmodern/ruby-nikto/issues
14
+ changelog_uri: https://github.com/postmodern/ruby-nikto/blob/master/ChangeLog.md
15
+ rubygems_mfa_required: 'true'
16
+
17
+ requirements: nikto >= 2.1.0
18
+
19
+ dependencies:
20
+ command_mapper: ~> 0.1
21
+ nokogiri: ~> 1.0
22
+
23
+ development_dependencies:
24
+ bundler: ~> 2.0
@@ -0,0 +1,188 @@
1
+ require 'command_mapper/command'
2
+
3
+ module Nikto
4
+ #
5
+ # Provides an interface for invoking the `nikto` utility.
6
+ #
7
+ # ## Example
8
+ #
9
+ # require 'nikto/command'
10
+ #
11
+ # Nikto::Command.run(host: 'example.com', output: 'nikto.xml')
12
+ #
13
+ # ## `nikto` options:
14
+ #
15
+ # * `-Cgidirs` - `nikto.cgi_dirs`
16
+ # * `-config` - `nikto.config`
17
+ # * `-dbcheck` - `nikto.dbcheck`
18
+ # * `-Display` - `nikto.display`
19
+ # * `-evasion` - `nikto.evasion`
20
+ # * `-findonly` - `nikto.find_only`
21
+ # * `-Format` - `nikto.format`
22
+ # * `-host` - `nikto.host`
23
+ # * `-Help` - `nikto.help`
24
+ # * `-id` - `nikto.id`
25
+ # * `-list-plugins` - `nikto.list_plugins`
26
+ # * `-mutate` - `nikto.mutate`
27
+ # * `-mutate-options` - `nikto.mutate_options`
28
+ # * `-nolookup` - `nikto.no_lookup`
29
+ # * `-nossl` - `nikto.no_ssl`
30
+ # * `-no404` - `nikto.no_404`
31
+ # * `-output` - `nikto.output`
32
+ # * `-plugins` - `nikto.plugins`
33
+ # * `-port` - `nikto.port`
34
+ # * `-Pause` - `nikto.pause`
35
+ # * `-root` - `nikto.root`
36
+ # * `-ssl` - `nikto.ssl`
37
+ # * `-Single` - `nikto.single`
38
+ # * `-timeout` - `nikto.timeout`
39
+ # * `-Tuning` - `nikto.tuning`
40
+ # * `-useproxy` - `nikto.use_proxy`
41
+ # * `-update` - `nikto.update`
42
+ # * `-Version` - `nikto.version`
43
+ # * `-vhost` - `nikto.vhost`
44
+ #
45
+ # @see http://linux.die.net/man/1/nikto
46
+ #
47
+ class Command < CommandMapper::Command
48
+
49
+ class OptionString < CommandMapper::Types::List
50
+
51
+ #
52
+ # @param [Hash{Symbol => String}] options
53
+ #
54
+ def initialize(options)
55
+ super(type: CommandMapper::Types::Map.new(options), separator: '')
56
+ end
57
+
58
+ end
59
+
60
+ class PortList < CommandMapper::Types::Num
61
+
62
+ def validate(value)
63
+ case value
64
+ when Array
65
+ value.each do |element|
66
+ valid, message = validate(element)
67
+
68
+ unless valid
69
+ return [valid, message]
70
+ end
71
+ end
72
+
73
+ return true
74
+ when Range
75
+ valid, message = super(value.begin)
76
+
77
+ unless valid
78
+ return [valid, message]
79
+ end
80
+
81
+ valid, message = super(value.end)
82
+
83
+ unless valid
84
+ return [valid, message]
85
+ end
86
+
87
+ return true
88
+ else
89
+ super(value)
90
+ end
91
+ end
92
+
93
+ def format(value)
94
+ case value
95
+ when Array
96
+ value.map(&method(:format)).join(',')
97
+ when Range
98
+ "#{value.begin}-#{value.end}"
99
+ else
100
+ super(value)
101
+ end
102
+ end
103
+
104
+ end
105
+
106
+ command 'nikto' do
107
+ option '-Cgidirs', name: :cgi_dirs, value: true
108
+ option '-config', value: {type: InputFile.new}
109
+ option '-dbcheck'
110
+ option '-Display', value: {
111
+ type: OptionString.new(
112
+ :show_redirects => '1',
113
+ :show_cookies => '2',
114
+ :show_200_responses => '3',
115
+ :show_auth_urls => '4',
116
+ :debug => 'D',
117
+ :verbose => 'V'
118
+ )
119
+ }
120
+
121
+ option '-evasion', value: {
122
+ type: OptionString.new(
123
+ :random_uri_encoding => '1',
124
+ :directory_self_reference => '2',
125
+ :premature_url_ending => '3',
126
+ :prepend_random_strings => '4',
127
+ :fake_parameter => '5',
128
+ :tab_request_spacer => '6',
129
+ :random_url_case => '7',
130
+ :windows_directory_separator => '8'
131
+ )
132
+ }
133
+ option '-findonly', name: :find_only
134
+ option '-Format', name: :format, value: {
135
+ type: Enum[:csv, :htm, :txt, :xml]
136
+ }
137
+ option '-host', value: true
138
+ option '-Help', name: :help
139
+ option '-id', value: {type: KeyValue.new(separator: ':')}
140
+ option '-list-plugins'
141
+ option '-mutate', value: {
142
+ type: OptionString.new(
143
+ :test_all_files => '1',
144
+ :enum_password_file_names => '2',
145
+ :enum_user_dirs => '3',
146
+ :enum_cgi_users => '4',
147
+ :bruteforce_subcomdains => '5',
148
+ :bruteforce_directories => '6'
149
+ )
150
+ }
151
+ option '-mutate-options', value: true
152
+ option '-nolookup', name: :no_lookup
153
+ option '-nossl', name: :no_ssl
154
+ option '-no404', name: :no_404
155
+ option '-output', value: true
156
+ option '-plugins', value: {type: List.new}
157
+ option '-port', value: {type: PortList.new}
158
+ option '-Pause', name: :pause, value: {type: Num.new}
159
+ option '-root', value: true
160
+ option '-ssl', value: {type: PortList.new}
161
+ option '-Single', name: :single
162
+ option '-timeout', value: {type: Num.new}
163
+ option '-Tuning', name: :tuning, value: {
164
+ type: OptionString.new(
165
+ :file_upload => '0',
166
+ :interesting_files => '1',
167
+ :default_files => '2',
168
+ :information_disclosure => '3',
169
+ :injection => '4',
170
+ :remote_file_retriveal_inside_web_root => '5',
171
+ :denail_of_service => '6',
172
+ :remote_file_retriveal_server_wide => '7',
173
+ :command_execution => '8',
174
+ :sql_injection => '9',
175
+ :authentication_bypass => 'a',
176
+ :software_identification => 'b',
177
+ :remote_source_inclusion => 'c',
178
+ :reverse_tuning_options => 'x'
179
+ )
180
+ }
181
+ option '-useproxy', name: :use_proxy
182
+ option '-update'
183
+ option '-Version', name: :version
184
+ option '-vhost'
185
+ end
186
+
187
+ end
188
+ end
@@ -0,0 +1,4 @@
1
+ module Nikto
2
+ # ruby-nikto version
3
+ VERSION = '0.1.0'
4
+ end
@@ -0,0 +1,55 @@
1
+ module Nikto
2
+ class XML
3
+ class Item
4
+
5
+ #
6
+ # Initializes the item object.
7
+ #
8
+ # @param [Nokogiri::XML::Node] node
9
+ # The XML node for the `item` XML element.
10
+ #
11
+ # @api private
12
+ #
13
+ def initialize(node)
14
+ @node = node
15
+ end
16
+
17
+ #
18
+ # The text of the `description` child element.
19
+ #
20
+ # @return [String]
21
+ #
22
+ def description
23
+ @description ||= @node.at_xpath('description').inner_text
24
+ end
25
+
26
+ #
27
+ # The text of the `uri` child element.
28
+ #
29
+ # @return [String]
30
+ #
31
+ def uri
32
+ @uri ||= @node.at_xpath('uri').inner_text
33
+ end
34
+
35
+ #
36
+ # The text of the `namelink` child element.
37
+ #
38
+ # @return [String]
39
+ #
40
+ def name_link
41
+ @name_link ||= @node.at_xpath('namelink').inner_text
42
+ end
43
+
44
+ #
45
+ # The text of the `iplink` child element.
46
+ #
47
+ # @return [String]
48
+ #
49
+ def ip_link
50
+ @ip_link ||= @node.at_xpath('iplink').inner_text
51
+ end
52
+
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,175 @@
1
+ require 'nikto/xml/item'
2
+ require 'nikto/xml/statistics'
3
+
4
+ require 'time'
5
+
6
+ module Nikto
7
+ class XML
8
+ #
9
+ # Represents a `scandetails` XML element.
10
+ #
11
+ class ScanDetails
12
+
13
+ #
14
+ # Initializes the scan details object.
15
+ #
16
+ # @param [Nokogiri::XML::Node] node
17
+ # The XML node for the `scandetails` XML element.
18
+ #
19
+ # @api private
20
+ #
21
+ def initialize(node)
22
+ @node = node
23
+ end
24
+
25
+ #
26
+ # The target's IP address.
27
+ #
28
+ # @return [String]
29
+ # The value of the `targetip` attribute.
30
+ #
31
+ def target_ip
32
+ @node['targetip']
33
+ end
34
+
35
+ #
36
+ # The target's hostname.
37
+ #
38
+ # @return [String]
39
+ # The value of the `targethostname` attribute.
40
+ #
41
+ def target_hostname
42
+ @node['targethostname']
43
+ end
44
+
45
+ #
46
+ # The target's port number.
47
+ #
48
+ # @return [Integer]
49
+ # The parsed value of the `targetport` attribute.
50
+ #
51
+ def target_port
52
+ @target_port ||= @node['targetport'].to_i
53
+ end
54
+
55
+ #
56
+ # The target's banner value.
57
+ #
58
+ # @return [String]
59
+ # The value of the `targetbanner` attribute.
60
+ #
61
+ def target_banner
62
+ @node['targetbanner']
63
+ end
64
+
65
+ #
66
+ # When the target started being scanned.
67
+ #
68
+ # @return [Time]
69
+ # The parsed value `starttime` attribute.
70
+ #
71
+ def start_time
72
+ @start_time ||= Time.parse(@node['starttime'])
73
+ end
74
+
75
+ #
76
+ # The site name.
77
+ #
78
+ # @return [String]
79
+ # The value of the `sitename` attribute.
80
+ #
81
+ def site_name
82
+ @node['sitename']
83
+ end
84
+
85
+ #
86
+ # The site's IP address.
87
+ #
88
+ # @return [String]
89
+ # The value of the `siteip` attribute.
90
+ #
91
+ def site_ip
92
+ @node['siteip']
93
+ end
94
+
95
+ #
96
+ # The `Host` header.
97
+ #
98
+ # @return [String]
99
+ # The value of the `hostheader` attribute.
100
+ #
101
+ def host_header
102
+ @node['hostheader']
103
+ end
104
+
105
+ #
106
+ # How many errors occurred.
107
+ #
108
+ # @return [Integer]
109
+ # The parsed value of the `errors` attribute.
110
+ #
111
+ def errors
112
+ @errors ||= @node['errors'].to_i
113
+ end
114
+
115
+ #
116
+ # Determines if any errors occurred.
117
+ #
118
+ # @return [Boolean]
119
+ #
120
+ def errors?
121
+ errors > 0
122
+ end
123
+
124
+ #
125
+ # How many checks were performed on the target.
126
+ #
127
+ # @return [Integer]
128
+ # The parsed value of the `checks` attribute.
129
+ #
130
+ def checks
131
+ @checks ||= @node['checks'].to_i
132
+ end
133
+
134
+ #
135
+ # Enumerates over the found items.
136
+ #
137
+ # @yield [item]
138
+ # If a block is given, it will be passed each item object.
139
+ #
140
+ # @yieldparam [Item] item
141
+ # An item object.
142
+ #
143
+ # @return [Enumerator]
144
+ # If no block is given, an Enumerator object will be returned.
145
+ #
146
+ def each_item
147
+ return enum_for(__method__) unless block_given?
148
+
149
+ @node.xpath('item').each do |node|
150
+ yield Item.new(node)
151
+ end
152
+ end
153
+
154
+ #
155
+ # The found items for the target.
156
+ #
157
+ # @return [Array<Item>]
158
+ #
159
+ def items
160
+ each_item.to_a
161
+ end
162
+
163
+ #
164
+ # The statistics associated with the scan.
165
+ #
166
+ # @return [Statistics]
167
+ # Represents the `statistics` XML element.
168
+ #
169
+ def statistics
170
+ @statistics ||= Statistics.new(@node.at_xpath('statistics'))
171
+ end
172
+
173
+ end
174
+ end
175
+ end