-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
179 additions
and
174 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
# frozen_string_literal: true | ||
|
||
module CMSScanner | ||
# Scan | ||
class Scan | ||
attr_reader :run_error | ||
|
||
def initialize | ||
controllers << NS::Controller::Core.new | ||
|
||
exit_hook | ||
|
||
yield self if block_given? | ||
end | ||
|
||
# @return [ Controllers ] | ||
def controllers | ||
@controllers ||= NS::Controllers.new | ||
end | ||
|
||
def run | ||
controllers.run | ||
rescue OptParseValidator::NoRequiredOption => e | ||
@run_error = e | ||
|
||
formatter.output('@usage', msg: e.message) | ||
rescue NoMemoryError, ScriptError, SecurityError, SignalException, StandardError, SystemStackError => e | ||
@run_error = e | ||
|
||
formatter.output('@scan_aborted', | ||
reason: e.is_a?(Interrupt) ? 'Canceled by User' : e.message, | ||
trace: e.backtrace, | ||
verbose: controllers.first.parsed_options[:verbose] || | ||
run_error_exit_code == NS::ExitCode::EXCEPTION) | ||
ensure | ||
Browser.instance.hydra.abort | ||
|
||
formatter.beautify | ||
end | ||
|
||
# Used for convenience | ||
# @See Formatter | ||
def formatter | ||
controllers.first.formatter | ||
end | ||
|
||
# @return [ Hash ] | ||
def datastore | ||
controllers.first.datastore | ||
end | ||
|
||
# Hook to be able to have an exit code returned | ||
# depending on the findings / errors | ||
# :nocov: | ||
def exit_hook | ||
# Avoid hooking the exit when rspec is running, otherwise it will always return 0 | ||
# and Travis won't detect failed builds. Couldn't find a better way, even though | ||
# some people managed to https://github.com/rspec/rspec-core/pull/410 | ||
return if defined?(RSpec) | ||
|
||
at_exit do | ||
exit(run_error_exit_code) if run_error | ||
|
||
controller = controllers.first | ||
|
||
# The parsed_option[:url] must be checked to avoid raising erros when only -h/-v are given | ||
exit(NS::ExitCode::VULNERABLE) if controller.parsed_options[:url] && controller.target.vulnerable? | ||
exit(NS::ExitCode::OK) | ||
end | ||
end | ||
# :nocov: | ||
|
||
# @return [ Integer ] The exit code related to the run_error | ||
def run_error_exit_code | ||
return NS::ExitCode::CLI_OPTION_ERROR if run_error.is_a?(OptParseValidator::Error) || | ||
run_error.is_a?(OptionParser::ParseError) | ||
|
||
return NS::ExitCode::INTERRUPTED if run_error.is_a?(Interrupt) | ||
|
||
return NS::ExitCode::ERROR if run_error.is_a?(NS::Error::Standard) || | ||
run_error.is_a?(CMSScanner::Error::Standard) | ||
|
||
NS::ExitCode::EXCEPTION | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
# frozen_string_literal: true | ||
|
||
describe CMSScanner::Scan do | ||
subject(:scanner) { described_class.new } | ||
let(:controller) { CMSScanner::Controller } | ||
|
||
before do | ||
Object.send(:remove_const, :ARGV) | ||
Object.const_set(:ARGV, []) | ||
end | ||
|
||
describe '#new, #controllers' do | ||
its(:controllers) { should eq([controller::Core.new]) } | ||
end | ||
|
||
describe '#run' do | ||
after do | ||
scanner.run | ||
|
||
if defined?(run_error) | ||
expect(scanner.run_error).to be_a run_error.class | ||
expect(scanner.run_error.message).to eql run_error.message | ||
end | ||
end | ||
|
||
it 'runs the controlllers and calls the formatter#beautify' do | ||
hydra = CMSScanner::Browser.instance.hydra | ||
|
||
expect(scanner.controllers).to receive(:run).ordered | ||
expect(hydra).to receive(:abort).ordered | ||
expect(scanner.formatter).to receive(:beautify).ordered | ||
end | ||
|
||
context 'when no required option supplied' do | ||
it 'calls the formatter to display the usage view' do | ||
expect(scanner.formatter).to receive(:output) | ||
.with('@usage', msg: 'One of the following options is required: url, help, hh, version') | ||
end | ||
end | ||
|
||
context 'when an Interrupt is raised during the scan' do | ||
it 'aborts the scan with the correct output' do | ||
expect(scanner.controllers.option_parser).to receive(:results).and_return({}) | ||
|
||
expect(scanner.controllers.first) | ||
.to receive(:before_scan) | ||
.and_raise(Interrupt) | ||
|
||
expect(scanner.formatter).to receive(:output) | ||
.with('@scan_aborted', hash_including(reason: 'Canceled by User', trace: anything, verbose: false)) | ||
end | ||
end | ||
|
||
{ | ||
NoMemoryError.new => true, | ||
ScriptError.new => true, | ||
SecurityError.new => true, | ||
SignalException.new('SIGTERM') => true, | ||
Interrupt.new('Canceled by User') => false, | ||
RuntimeError.new('error spotted') => true, | ||
CMSScanner::Error::Standard.new('aa') => false, | ||
CMSScanner::Error::MaxScanDurationReached.new => false, | ||
SystemStackError.new => true | ||
}.each do |error, expected_verbose| | ||
context "when an/a #{error.class} is raised during the scan" do | ||
let(:run_error) { error } | ||
|
||
it 'aborts the scan with the associated output' do | ||
expect(scanner.controllers.option_parser).to receive(:results).and_return({}) | ||
|
||
expect(scanner.controllers.first) | ||
.to receive(:before_scan) | ||
.and_raise(run_error.class, run_error.message) | ||
|
||
expect(scanner.formatter).to receive(:output) | ||
.with('@scan_aborted', hash_including(reason: run_error.message, | ||
trace: anything, | ||
verbose: expected_verbose)) | ||
end | ||
end | ||
end | ||
end | ||
|
||
describe '#datastore' do | ||
its(:datastore) { should eq({}) } | ||
end | ||
|
||
describe '#exit_hook' do | ||
# No idea how to test that, maybe with another at_exit hook ? oO | ||
xit | ||
end | ||
end |