Skip to content

Commit

Permalink
Fixes #79
Browse files Browse the repository at this point in the history
  • Loading branch information
erwanlr committed Mar 27, 2019
1 parent 70c5cc6 commit 02e7707
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 174 deletions.
84 changes: 1 addition & 83 deletions lib/cms_scanner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
require 'cms_scanner/public_suffix/domain' # Adds a Domain#match method and logic, used in scope stuff
require 'cms_scanner/numeric' # Adds a Numeric#bytes_to_human
# Custom Libs
require 'cms_scanner/scan'
require 'cms_scanner/helper'
require 'cms_scanner/exit_code'
require 'cms_scanner/errors'
Expand Down Expand Up @@ -122,89 +123,6 @@ def self.included(base)
base.extend(ClassMethods)
super(base)
end

# 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

require "#{CMSScanner::APP_DIR}/app"
86 changes: 86 additions & 0 deletions lib/cms_scanner/scan.rb
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
91 changes: 0 additions & 91 deletions spec/lib/cms_scanner_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,94 +56,3 @@
end
end
end

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
92 changes: 92 additions & 0 deletions spec/lib/scan_spec.rb
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

0 comments on commit 02e7707

Please sign in to comment.