ruby-feroxbuster 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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