ruby-feroxbuster 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: 93982d828d9029176f21dc60dc00bcc6d58ee980a4c52d37afa18cea4fd2fe3e
4
+ data.tar.gz: b1225c989137297136368816c9547dca26a38412e166d7f675794ccf91475c3a
5
+ SHA512:
6
+ metadata.gz: d7d98099731b1aa773f378f676bd24a5b09618d6cdd30cbbf324ea9641d04b866e418ab3d1c1daed00208f24341e15b3e3ffb204c63b820955cb2077c877085d
7
+ data.tar.gz: febaa1f1c1011743478b626e4395a3f6a212185f4e6712ab053f06834a8b86eb04a316d1832393d162088779c4bd84614e19fb07c2a4783b62c22a8c73deffb2
data/.document ADDED
@@ -0,0 +1,3 @@
1
+ -
2
+ ChangeLog.md
3
+ LICENSE.txt
@@ -0,0 +1,27 @@
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 dependencies
25
+ run: bundle install --jobs 4 --retry 3
26
+ - name: Run tests
27
+ run: bundle exec rake test
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /Gemfile.lock
2
+ /coverage
3
+ /doc
4
+ /pkg
5
+ /.yardoc
6
+ .DS_Store
7
+ *.db
8
+ *.log
9
+ *.swp
10
+ *~
11
+ *.gem
12
+ *.state
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour --format documentation
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --markup markdown --title 'Ruby Feroxbuster Documentation' --protected
data/ChangeLog.md ADDED
@@ -0,0 +1,10 @@
1
+ ### 0.1.0 / 2021-04-22
2
+
3
+ * Initial release:
4
+ * Added {Feroxbuster::Command}.
5
+ * Added {Feroxbuster::OutputFile}.
6
+ * Added {Feroxbuster::Parsers::JSON}.
7
+ * Added {Feroxbuster::Parsers::TXT}.
8
+ * Added {Feroxbuster::Response}.
9
+ * Added {Feroxbuster::Statistics}.
10
+
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ # gem 'command_mapper', '~> 0.2', 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 'redcarpet', platform: :mri
15
+ gem 'yard', '~> 0.9'
16
+ gem 'yard-spellcheck', require: false
17
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2022 Hal Brodigan
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ 'Software'), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # ruby-feroxbuster
2
+
3
+ [![CI](https://github.com/postmodern/ruby-feroxbuster/actions/workflows/ruby.yml/badge.svg)](https://github.com/postmodern/ruby-gobuster/actions/workflows/ruby.yml)
4
+ [![Gem Version](https://badge.fury.io/rb/ruby-feroxbuster.svg)](https://badge.fury.io/rb/ruby-gobuster)
5
+
6
+ * [Source](https://github.com/postmodern/ruby-feroxbuster/)
7
+ * [Issues](https://github.com/postmodern/ruby-feroxbuster/issues)
8
+ * [Documentation](http://rubydoc.info/gems/ruby-feroxbuster/frames)
9
+
10
+ ## Description
11
+
12
+ A Ruby interface to [feroxbuster], a simple, fast, recursive content discovery
13
+ tool written in Rust.
14
+
15
+ ## Features
16
+
17
+ * Provides a [Ruby interface][Feroxbuster::Command] for running the
18
+ `feroxbuster` command.
19
+ * Supports [parsing][Feroxbuster::OutputFile] `feroxbuster` `.txt` and `.json`
20
+ output files.
21
+
22
+ [Feroxbuster::Command]: https://rubydoc.info/gems/ruby-feroxbuster/Feroxbuster/Command
23
+ [Feroxbuster::OutputFile]: https://rubydoc.info/gems/ruby-amass/Feroxbuster/OutputFile
24
+
25
+ ## Examples
26
+
27
+ Run `feroxbuster --wordlist /path/to/wordlist.txt --url https://example.com` from Ruby:
28
+
29
+ ```ruby
30
+ require 'feroxbuster/command'
31
+
32
+ Feroxbuster::Command.run(wordlist: '/path/to/wordlist.txt', url: 'https://example.com')
33
+ ```
34
+
35
+ Parser an `feroxbuster` JSON file:
36
+
37
+ ```ruby
38
+ require 'feroxbuster/output_file'
39
+
40
+ output_file = Feroxbuster::OutputFile.new('/path/to/feroxbuster.json')
41
+ output_file.each do |object|
42
+ p object
43
+ end
44
+ ```
45
+
46
+ ```
47
+ #<Feroxbuster::Response:0x000056472fab3390 @url="https://github.com/test", @original_url="https://github.com", @path="/test", @wildcard=false, @status=200, @method="GET", @content_length=0, @line_count=2010, @word_count=11253, @headers={"permissions-policy"=>"interest-cohort=()", "accept-ranges"=>"bytes", "referrer-policy"=>"origin-when-cross-origin, strict-origin-when-cross-origin", "x-content-type-options"=>"nosniff", "cache-control"=>"max-age=0, private, must-revalidate", "expect-ct"=>"max-age=2592000, report-uri=\"https://api.github.com/_private/browser/errors\"", "server"=>"GitHub.com", "x-github-request-id"=>"85A8:0E10:20B03E:27719A:6260B1D5", "transfer-encoding"=>"chunked", "date"=>"Thu, 21 Apr 2022 01:22:16 GMT", "strict-transport-security"=>"max-age=31536000; includeSubdomains; preload", "etag"=>"W/\"7c98cb0440eb94eddcfd360497fae419\"", "x-frame-options"=>"deny", "content-security-policy"=>"default-src 'none'; base-uri 'self'; block-all-mixed-content; child-src github.com/assets-cdn/worker/ gist.github.com/assets-cdn/worker/; connect-src 'self' uploads.github.com objects-origin.githubusercontent.com www.githubstatus.com collector.github.com raw.githubusercontent.com api.github.com github-cloud.s3.amazonaws.com github-production-repository-file-5c1aeb.s3.amazonaws.com github-production-upload-manifest-file-7fdce7.s3.amazonaws.com github-production-user-asset-6210df.s3.amazonaws.com cdn.optimizely.com logx.optimizely.com/v1/events translator.github.com *.actions.githubusercontent.com wss://*.actions.githubusercontent.com online.visualstudio.com/api/v1/locations github-production-repository-image-32fea6.s3.amazonaws.com github-production-release-asset-2e65be.s3.amazonaws.com insights.github.com wss://alive.github.com; font-src github.githubassets.com; form-action 'self' github.com gist.github.com objects-origin.githubusercontent.com; frame-ancestors 'none'; frame-src render.githubusercontent.com viewscreen.githubusercontent.com notebooks.githubusercontent.com; img-src 'self' data: github.githubassets.com identicons.github.com github-cloud.s3.amazonaws.com secured-user-images.githubusercontent.com/ *.githubusercontent.com; manifest-src 'self'; media-src github.com user-images.githubusercontent.com/; script-src github.githubassets.com; style-src 'unsafe-inline' github.githubassets.com; worker-src github.com/assets-cdn/worker/ gist.github.com/assets-cdn/worker/", "x-xss-protection"=>"0", "set-cookie"=>"logged_in=no; Path=/; Domain=github.com; Expires=Fri, 21 Apr 2023 01:22:29 GMT; HttpOnly; Secure; SameSite=Lax", "vary"=>"X-Requested-With, X-PJAX-Container, Accept-Encoding, Accept, X-Requested-With", "content-type"=>"text/html; charset=utf-8"}, @extension="">
48
+ #<Feroxbuster::Response:0x000056472fab20f8 @url="https://github.com/dev", @original_url="https://github.com", @path="/dev", @wildcard=false, @status=200, @method="GET", @content_length=0, @line_count=2067, @word_count=11308, @headers={"date"=>"Thu, 21 Apr 2022 01:22:17 GMT", "cache-control"=>"max-age=0, private, must-revalidate", "content-security-policy"=>"default-src 'none'; base-uri 'self'; block-all-mixed-content; child-src github.com/assets-cdn/worker/ gist.github.com/assets-cdn/worker/; connect-src 'self' uploads.github.com objects-origin.githubusercontent.com www.githubstatus.com collector.github.com raw.githubusercontent.com api.github.com github-cloud.s3.amazonaws.com github-production-repository-file-5c1aeb.s3.amazonaws.com github-production-upload-manifest-file-7fdce7.s3.amazonaws.com github-production-user-asset-6210df.s3.amazonaws.com cdn.optimizely.com logx.optimizely.com/v1/events translator.github.com *.actions.githubusercontent.com wss://*.actions.githubusercontent.com online.visualstudio.com/api/v1/locations github-production-repository-image-32fea6.s3.amazonaws.com github-production-release-asset-2e65be.s3.amazonaws.com insights.github.com wss://alive.github.com; font-src github.githubassets.com; form-action 'self' github.com gist.github.com objects-origin.githubusercontent.com; frame-ancestors 'none'; frame-src render.githubusercontent.com viewscreen.githubusercontent.com notebooks.githubusercontent.com; img-src 'self' data: github.githubassets.com identicons.github.com github-cloud.s3.amazonaws.com secured-user-images.githubusercontent.com/ *.githubusercontent.com; manifest-src 'self'; media-src github.com user-images.githubusercontent.com/; script-src github.githubassets.com; style-src 'unsafe-inline' github.githubassets.com; worker-src github.com/assets-cdn/worker/ gist.github.com/assets-cdn/worker/", "set-cookie"=>"logged_in=no; Path=/; Domain=github.com; Expires=Fri, 21 Apr 2023 01:22:29 GMT; HttpOnly; Secure; SameSite=Lax", "permissions-policy"=>"interest-cohort=()", "x-frame-options"=>"deny", "server"=>"GitHub.com", "accept-ranges"=>"bytes", "etag"=>"W/\"3ca853f280bb864544f7842cdaa124e5\"", "vary"=>"X-Requested-With, X-PJAX-Container, Accept-Encoding, Accept, X-Requested-With", "referrer-policy"=>"origin-when-cross-origin, strict-origin-when-cross-origin", "x-github-request-id"=>"85AC:2947:2319C4:29E4C8:6260B1D5", "transfer-encoding"=>"chunked", "expect-ct"=>"max-age=2592000, report-uri=\"https://api.github.com/_private/browser/errors\"", "x-content-type-options"=>"nosniff", "content-type"=>"text/html; charset=utf-8", "x-xss-protection"=>"0", "strict-transport-security"=>"max-age=31536000; includeSubdomains; preload"}, @extension="">
49
+ #<Feroxbuster::Response:0x000056472fab0f28 @url="https://github.com/www", @original_url="https://github.com", @path="/www", @wildcard=false, @status=200, @method="GET", @content_length=0, @line_count=1813, @word_count=10399, @headers={"content-type"=>"text/html; charset=utf-8", "accept-ranges"=>"bytes", "date"=>"Thu, 21 Apr 2022 01:22:16 GMT", "transfer-encoding"=>"chunked", "vary"=>"X-Requested-With, X-PJAX-Container, Accept-Encoding, Accept, X-Requested-With", "x-xss-protection"=>"0", "x-github-request-id"=>"85B4:4678:28A012:2F9830:6260B1D5", "content-security-policy"=>"default-src 'none'; base-uri 'self'; block-all-mixed-content; child-src github.com/assets-cdn/worker/ gist.github.com/assets-cdn/worker/; connect-src 'self' uploads.github.com objects-origin.githubusercontent.com www.githubstatus.com collector.github.com raw.githubusercontent.com api.github.com github-cloud.s3.amazonaws.com github-production-repository-file-5c1aeb.s3.amazonaws.com github-production-upload-manifest-file-7fdce7.s3.amazonaws.com github-production-user-asset-6210df.s3.amazonaws.com cdn.optimizely.com logx.optimizely.com/v1/events translator.github.com *.actions.githubusercontent.com wss://*.actions.githubusercontent.com online.visualstudio.com/api/v1/locations github-production-repository-image-32fea6.s3.amazonaws.com github-production-release-asset-2e65be.s3.amazonaws.com insights.github.com wss://alive.github.com; font-src github.githubassets.com; form-action 'self' github.com gist.github.com objects-origin.githubusercontent.com; frame-ancestors 'none'; frame-src render.githubusercontent.com viewscreen.githubusercontent.com notebooks.githubusercontent.com; img-src 'self' data: github.githubassets.com identicons.github.com github-cloud.s3.amazonaws.com secured-user-images.githubusercontent.com/ *.githubusercontent.com; manifest-src 'self'; media-src github.com user-images.githubusercontent.com/; script-src github.githubassets.com; style-src 'unsafe-inline' github.githubassets.com; worker-src github.com/assets-cdn/worker/ gist.github.com/assets-cdn/worker/", "referrer-policy"=>"origin-when-cross-origin, strict-origin-when-cross-origin", "etag"=>"W/\"e03d055bf6b701eb6ecf1fa7c1a137f0\"", "permissions-policy"=>"interest-cohort=()", "strict-transport-security"=>"max-age=31536000; includeSubdomains; preload", "x-frame-options"=>"deny", "set-cookie"=>"logged_in=no; Path=/; Domain=github.com; Expires=Fri, 21 Apr 2023 01:22:29 GMT; HttpOnly; Secure; SameSite=Lax", "expect-ct"=>"max-age=2592000, report-uri=\"https://api.github.com/_private/browser/errors\"", "server"=>"GitHub.com", "cache-control"=>"max-age=0, private, must-revalidate", "x-content-type-options"=>"nosniff"}, @extension="">
50
+ #<Feroxbuster::Response:0x000056472fabbcc0 @url="https://github.com/", @original_url="https://github.com", @path="/", @wildcard=false, @status=200, @method="GET", @content_length=0, @line_count=2596, @word_count=13002, @headers={"cache-control"=>"max-age=0, private, must-revalidate", "server"=>"GitHub.com", "set-cookie"=>"logged_in=no; Path=/; Domain=github.com; Expires=Fri, 21 Apr 2023 01:22:29 GMT; HttpOnly; Secure; SameSite=Lax", "permissions-policy"=>"interest-cohort=()", "vary"=>"X-PJAX, X-PJAX-Container, Accept-Language, Accept-Encoding, Accept, X-Requested-With", "content-security-policy"=>"default-src 'none'; base-uri 'self'; block-all-mixed-content; child-src github.com/assets-cdn/worker/ gist.github.com/assets-cdn/worker/; connect-src 'self' uploads.github.com objects-origin.githubusercontent.com www.githubstatus.com collector.github.com raw.githubusercontent.com api.github.com github-cloud.s3.amazonaws.com github-production-repository-file-5c1aeb.s3.amazonaws.com github-production-upload-manifest-file-7fdce7.s3.amazonaws.com github-production-user-asset-6210df.s3.amazonaws.com cdn.optimizely.com logx.optimizely.com/v1/events translator.github.com *.actions.githubusercontent.com wss://*.actions.githubusercontent.com online.visualstudio.com/api/v1/locations github-production-repository-image-32fea6.s3.amazonaws.com github-production-release-asset-2e65be.s3.amazonaws.com insights.github.com wss://alive.github.com github.githubassets.com; font-src github.githubassets.com; form-action 'self' github.com gist.github.com objects-origin.githubusercontent.com; frame-ancestors 'none'; frame-src render.githubusercontent.com viewscreen.githubusercontent.com notebooks.githubusercontent.com; img-src 'self' data: github.githubassets.com identicons.github.com github-cloud.s3.amazonaws.com secured-user-images.githubusercontent.com/ *.githubusercontent.com customer-stories-feed.github.com spotlights-feed.github.com; manifest-src 'self'; media-src github.com user-images.githubusercontent.com/ github.githubassets.com; script-src github.githubassets.com; style-src 'unsafe-inline' github.githubassets.com; worker-src github.com/assets-cdn/worker/ gist.github.com/assets-cdn/worker/", "x-xss-protection"=>"0", "expect-ct"=>"max-age=2592000, report-uri=\"https://api.github.com/_private/browser/errors\"", "x-github-request-id"=>"85B2:4670:5B88C:BE8AA:6260B1D5", "date"=>"Thu, 21 Apr 2022 01:22:22 GMT", "strict-transport-security"=>"max-age=31536000; includeSubdomains; preload", "transfer-encoding"=>"chunked", "x-content-type-options"=>"nosniff", "etag"=>"W/\"b1bb181559a6360603ad7f0ed22c198c\"", "accept-ranges"=>"bytes", "x-frame-options"=>"deny", "referrer-policy"=>"origin-when-cross-origin, strict-origin-when-cross-origin", "content-type"=>"text/html; charset=utf-8", "content-language"=>"en-US"}, @extension="">
51
+ #<Feroxbuster::Statistics:0x000056472fabacd0 @timeouts=0, @requests=18, @expected_per_scan=6, @total_expected=12, @errors=0, @successes=12, @redirects=0, @client_errors=6, @server_errors=0, @total_scans=2, @initial_targets=0, @links_extracted=0, @extensions_collected=0, @status_200s=12, @status_301s=0, @status_302s=0, @status_401s=0, @status_403s=0, @status_429s=0, @status_500s=0, @status_503s=0, @status_504s=0, @status_508s=0, @wildcards_filtered=0, @responses_filtered=0, @resources_discovered=4, @url_format_errors=0, @redirection_errors=0, @connection_errors=0, @request_errors=0, @directory_scan_times=[0.434531853, 0.434228035], @total_runtime=[1.6527268240000001]>
52
+ ```
53
+
54
+ ## Requirements
55
+
56
+ * [ruby] >= 2.0.0
57
+ * [feroxbuster] >= 2.7.0
58
+ * [command_mapper] ~> 0.2, >= 0.2.1
59
+
60
+ [ruby]: https://www.ruby-lang.org/
61
+ [command_mapper]: https://github.com/postmodern/command_mapper.rb#readme
62
+
63
+ ## Install
64
+
65
+ ```shell
66
+ $ gem install ruby-feroxbuster
67
+ ```
68
+
69
+ ### gemspec
70
+
71
+ ```ruby
72
+ gemspec.add_dependency 'ruby-feroxbuster', '~> 0.1'
73
+ ```
74
+
75
+ ### Gemfile
76
+
77
+ ```ruby
78
+ gem 'ruby-feroxbuster', '~> 0.1'
79
+ ```
80
+
81
+ ## License
82
+
83
+ Copyright (c) 2022 Hal Brodigan
84
+
85
+ See {file:LICENSE.txt} for license information.
86
+
87
+ [feroxbuster]: https://github.com/epi052/feroxbuster#readme
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+
5
+ begin
6
+ require 'bundler/setup'
7
+ rescue LoadError => e
8
+ warn e.message
9
+ warn "Run `gem install bundler` to install Bundler"
10
+ exit -1
11
+ end
12
+
13
+ require 'rubygems/tasks'
14
+ Gem::Tasks.new
15
+
16
+ require 'rspec/core/rake_task'
17
+ RSpec::Core::RakeTask.new
18
+ task :test => :spec
19
+ task :default => :spec
20
+
21
+ require 'yard'
22
+ YARD::Rake::YardocTask.new
23
+ task :doc => :yard
data/gemspec.yml ADDED
@@ -0,0 +1,28 @@
1
+ name: ruby-feroxbuster
2
+ summary: A Ruby interface to feroxbuster.
3
+ description:
4
+ A Ruby interface to feroxbuster, a simple, fast, recursive content discovery
5
+ tool written in Rust.
6
+
7
+ license: MIT
8
+ authors: Postmodern
9
+ email: postmodern.mod3@gmail.com
10
+ homepage: https://github.com/postmodern/ruby-feroxbuster#readme
11
+ has_yard: true
12
+
13
+ metadata:
14
+ documentation_uri: https://rubydoc.info/gems/ruby-feroxbuster
15
+ source_code_uri: https://github.com/postmodern/ruby-feroxbuster
16
+ bug_tracker_uri: https://github.com/postmodern/ruby-feroxbuster/issues
17
+ changelog_uri: https://github.com/postmodern/ruby-feroxbuster/blob/master/ChangeLog.md
18
+ rubygems_mfa_required: 'true'
19
+
20
+ required_ruby_version: ">= 2.0.0"
21
+
22
+ requirements: feroxbuster >= 2.7.0
23
+
24
+ dependencies:
25
+ command_mapper: ~> 0.2, >= 0.2.1
26
+
27
+ development_dependencies:
28
+ bundler: ~> 2.0
@@ -0,0 +1,134 @@
1
+ require 'command_mapper/command'
2
+
3
+ module Feroxbuster
4
+ class Command < CommandMapper::Command
5
+
6
+ #
7
+ # Represents the type for the `--time-limit` option.
8
+ #
9
+ # @api private
10
+ #
11
+ class TimeSpec < CommandMapper::Types::Str
12
+
13
+ #
14
+ # Validates the given value.
15
+ #
16
+ # @param [Object] value
17
+ # The given value to validate.
18
+ #
19
+ # @return [true, (false, String)]
20
+ # Returns true if the value is considered valid, or false and a
21
+ # validation message if the value is not valid.
22
+ #
23
+ def validate(value)
24
+ valid, message = super(value)
25
+
26
+ unless valid
27
+ return [valid, message]
28
+ end
29
+
30
+ value = value.to_s
31
+
32
+ unless value =~ /\A\d+(?:m|s|ms|ns)\z/
33
+ return [false, "must be a number and end with 'm', 's', 'ms', or 'ns'"]
34
+ end
35
+
36
+ return true
37
+ end
38
+
39
+ end
40
+
41
+ #
42
+ # Represents an HTTP status code (100 - 599).
43
+ #
44
+ # @api private
45
+ #
46
+ class StatusCode < CommandMapper::Types::Num
47
+
48
+ def initialize
49
+ super(range: 100..599)
50
+ end
51
+
52
+ end
53
+
54
+ command 'feroxbuster' do
55
+ option '--help'
56
+ option '--version'
57
+
58
+ # Target selection:
59
+ option '--resume-from', value: {type: InputFile.new}
60
+ option '--stdin'
61
+ option '--url', value: true
62
+
63
+ # Composite settings:
64
+ option '--burp'
65
+ option '--burp-replay'
66
+ option '--smart'
67
+ option '--thorough'
68
+
69
+ # Proxy settings:
70
+ option '--proxy', value: true
71
+ option '--replay-proxy', value: true
72
+ option '--replay-codes', value: {type: StatusCode.new}, repeats: true
73
+
74
+ # Request settings:
75
+ option '--user-agent', value: true
76
+ option '--random-agent'
77
+ option '--cookies', value: true, repeats: true
78
+ option '--data', value: true
79
+ option '--add-slash'
80
+ option '--headers', value: true, repeats: true
81
+ option '--methods', name: :http_methods, value: true, repeats: true
82
+ option '--query', value: true, repeats: true
83
+
84
+ # Request filters:
85
+ option '--dont-scan', value: true, repeats: true
86
+
87
+ # Response filters:
88
+ option '--filter-status', value: {type: StatusCode.new}, repeats: true
89
+ option '--filter-similar-to', value: true, repeats: true
90
+ option '--filter-lines', value: true, repeats: true
91
+ option '--status-codes', value: {type: StatusCode.new},
92
+ repeats: true
93
+ option '--filter-size', value: {type: Num.new}, repeats: true
94
+ option '--filter-words', value: {type: Num.new}, repeats: true
95
+ option '--filter-regex', value: true, repeats: true
96
+
97
+ # Client settings:
98
+ option '--insecure'
99
+ option '--redirects'
100
+ option '--timeout', value: {type: Num.new}
101
+
102
+ # Scan settings:
103
+ option '--auto-bail'
104
+ option '--auto-tune'
105
+ option '--depth', value: {type: Num.new}
106
+ option '--dont-filter'
107
+ option '--extract-links'
108
+ option '--force-recursion'
109
+ option '--scan-list', value: {type: Num.new}
110
+ option '--no-recursion'
111
+ option '--parallel', value: {type: Num.new}
112
+ option '--rate-limit', value: {type: Num.new}
113
+ option '--threads', value: {type: Num.new}
114
+ option '--time-limit', value: {type: TimeSpec.new}
115
+ option '--wordlist', value: {type: InputFile.new}
116
+
117
+ # Dynamic collection settings:
118
+ option '--collect-backups'
119
+ option '--collect-extensions'
120
+ option '--collect-words'
121
+ option '--dont-collect', value: true, repeats: true
122
+
123
+ # Output settings:
124
+ option '--debug-log', value: true
125
+ option '--json'
126
+ option '--no-state'
127
+ option '--output', value: true
128
+ option '--quiet'
129
+ option '--silent'
130
+ option '--verbosity'
131
+ end
132
+
133
+ end
134
+ end
@@ -0,0 +1,124 @@
1
+ require 'feroxbuster/parsers/json'
2
+ require 'feroxbuster/parsers/txt'
3
+
4
+ module Feroxbuster
5
+ #
6
+ # Represents either a `.json` or `.txt` output file.
7
+ #
8
+ # ## Example
9
+ #
10
+ # require 'feroxbuster/output_file'
11
+ #
12
+ # output_file = Feroxbuster::OutputFile.new('/path/to/feroxbuster.json')
13
+ # output_file.each do |response|
14
+ # p response
15
+ # end
16
+ #
17
+ # @api public
18
+ #
19
+ class OutputFile
20
+
21
+ # Mapping of formats to parsers.
22
+ #
23
+ # @api semipublic
24
+ PARSERS = {
25
+ json: Parsers::JSON,
26
+ txt: Parsers::TXT
27
+ }
28
+
29
+ # The path to the output file.
30
+ #
31
+ # @return [String]
32
+ attr_reader :path
33
+
34
+ # The format of the output file.
35
+ #
36
+ # @return [:json, :txt]
37
+ attr_reader :format
38
+
39
+ # The parser for the output file format.
40
+ #
41
+ # @return [Parsers::JSON, Parsers::TXT]
42
+ #
43
+ # @api private
44
+ attr_reader :parser
45
+
46
+ #
47
+ # Initializes the output file.
48
+ #
49
+ # @param [String] path
50
+ # The path to the output file.
51
+ #
52
+ # @param [:json, :txt] format
53
+ # The optional format of the output file. If not given, it will be
54
+ # inferred by the path's file extension.
55
+ #
56
+ def initialize(path, format: self.class.infer_format(path))
57
+ @path = File.expand_path(path)
58
+ @format = format
59
+
60
+ @parser = PARSERS.fetch(format) do
61
+ raise(ArgumentError,"unrecognized file type: #{@path.inspect}")
62
+ end
63
+ end
64
+
65
+ # Mapping of file extensions to formats
66
+ #
67
+ # @api semipublic
68
+ FILE_FORMATS = {
69
+ '.json' => :json,
70
+ '.txt' => :txt
71
+ }
72
+
73
+ #
74
+ # Infers the format from the output file's extension name.
75
+ #
76
+ # @param [String] path
77
+ # The path to the output file.
78
+ #
79
+ # @return [:json, :txt]
80
+ # The output format inferred from the file's extension name.
81
+ #
82
+ # @raise [ArgumentError]
83
+ # The output format could not be inferred from the file's name.
84
+ #
85
+ # @api semipublic
86
+ #
87
+ def self.infer_format(path)
88
+ FILE_FORMATS.fetch(File.extname(path)) do
89
+ raise(ArgumentError,"could not infer format of #{path}")
90
+ end
91
+ end
92
+
93
+ #
94
+ # Parses the contents of the output file.
95
+ #
96
+ # @yield [hostname]
97
+ # The given block will be passed each parsed hostname.
98
+ #
99
+ # @yieldparam [Hostname] hostname
100
+ # A parsed hostname from the output file.
101
+ #
102
+ # @return [Enumerator]
103
+ # If no block is given, an Enumerator object will be returned.
104
+ #
105
+ def each(&block)
106
+ return enum_for(__method__) unless block
107
+
108
+ File.open(@path) do |file|
109
+ @parser.parse(file,&block)
110
+ end
111
+ end
112
+
113
+ #
114
+ # Converts the output file to a String.
115
+ #
116
+ # @return [String]
117
+ # The path to the output file.
118
+ #
119
+ def to_s
120
+ @path
121
+ end
122
+
123
+ end
124
+ end
@@ -0,0 +1,121 @@
1
+ require 'feroxbuster/response'
2
+ require 'feroxbuster/statistics'
3
+
4
+ require 'json'
5
+
6
+ module Feroxbuster
7
+ module Parsers
8
+ #
9
+ # Parses single-line JSON hashes.
10
+ #
11
+ # @api semipublic
12
+ #
13
+ module JSON
14
+ #
15
+ # Parses a single-line of JSON.
16
+ #
17
+ # @param [IO] io
18
+ # The IO stream to parse.
19
+ #
20
+ # @yield [data]
21
+ # The given block will be passed each parsed data.
22
+ #
23
+ # @yieldparam [Response, Statistics] data
24
+ # The parsed data. {Statistics} will be yielded last.
25
+ #
26
+ # @return [Enumerator]
27
+ # If no block is given, an Enumerator will be returned.
28
+ #
29
+ def self.parse(io)
30
+ return enum_for(__method__,io) unless block_given?
31
+
32
+ io.each_line do |line|
33
+ line.chomp!
34
+ json = ::JSON.parse(line)
35
+
36
+ case json["type"]
37
+ when "response"
38
+ yield map_response(json)
39
+ when "statistics"
40
+ yield map_statistics(json)
41
+ else
42
+ raise(NotImplementedError,"unsupported JSON type: #{json['type']}")
43
+ end
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ #
50
+ # Maps a response type JSON Hash to a {Response} object.
51
+ #
52
+ # @param [Hash{Symbol => Object}] json
53
+ # The parsed JSON Hash.
54
+ #
55
+ # @return [Respnse]
56
+ # The resulting response object.
57
+ #
58
+ def self.map_response(json)
59
+ Response.new(
60
+ url: json['url'],
61
+ original_url: json['original_url'],
62
+ path: json['path'],
63
+ wildcard: json['wildcard'],
64
+ status: json['status'],
65
+ method: json['method'],
66
+ content_length: json['content_length'],
67
+ line_count: json['line_count'],
68
+ word_count: json['word_count'],
69
+ headers: json['headers'],
70
+ extension: json['extension']
71
+ )
72
+ end
73
+
74
+ #
75
+ # Maps a statistics type JSON Hash to a {Statistics} object.
76
+ #
77
+ # @param [Hash{Symbol => Object}] json
78
+ # The parsed JSON Hash.
79
+ #
80
+ # @return [Statistics]
81
+ # The resulting statistics object.
82
+ #
83
+ def self.map_statistics(json)
84
+ Statistics.new(
85
+ timeouts: json['timeouts'],
86
+ requests: json['requests'],
87
+ expected_per_scan: json['expected_per_scan'],
88
+ total_expected: json['total_expected'],
89
+ errors: json['errors'],
90
+ successes: json['successes'],
91
+ redirects: json['redirects'],
92
+ client_errors: json['client_errors'],
93
+ server_errors: json['server_errors'],
94
+ total_scans: json['total_scans'],
95
+ initial_targets: json['initial_targets'],
96
+ links_extracted: json['links_extracted'],
97
+ extensions_collected: json['extensions_collected'],
98
+ status_200s: json['status_200s'],
99
+ status_301s: json['status_301s'],
100
+ status_302s: json['status_302s'],
101
+ status_401s: json['status_401s'],
102
+ status_403s: json['status_403s'],
103
+ status_429s: json['status_429s'],
104
+ status_500s: json['status_500s'],
105
+ status_503s: json['status_503s'],
106
+ status_504s: json['status_504s'],
107
+ status_508s: json['status_508s'],
108
+ wildcards_filtered: json['wildcards_filtered'],
109
+ responses_filtered: json['responses_filtered'],
110
+ resources_discovered: json['resources_discovered'],
111
+ url_format_errors: json['url_format_errors'],
112
+ redirection_errors: json['redirection_errors'],
113
+ connection_errors: json['connection_errors'],
114
+ request_errors: json['request_errors'],
115
+ directory_scan_times: json['directory_scan_times'],
116
+ total_runtime: json['total_runtime']
117
+ )
118
+ end
119
+ end
120
+ end
121
+ end