Skip to content

Commit

Permalink
Merge pull request #69 from Grunny/issue-29
Browse files Browse the repository at this point in the history
Change exit codes for alerts and quick-scan commands
  • Loading branch information
Grunny authored Dec 31, 2018
2 parents e35ec8e + e01dbee commit 0185d40
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 25 deletions.
52 changes: 35 additions & 17 deletions tests/cli_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def test_start_zap_daemon_exception(self, helper_mock):
helper_mock.side_effect = ZAPError('error')
result = self.runner.invoke(cli.cli, ['--boring', '--api-key', '', 'start'])
helper_mock.assert_called_with(options=None)
self.assertEqual(result.exit_code, 1)
self.assertEqual(result.exit_code, 2)

@patch('zapcli.zap_helper.ZAPHelper.shutdown')
def test_shutdown_zap_daemon(self, helper_mock):
Expand All @@ -59,7 +59,7 @@ def test_shutdown_zap_daemon_exception(self, helper_mock):
helper_mock.side_effect = ZAPError('error')
result = self.runner.invoke(cli.cli, ['--boring', '--api-key', '', 'shutdown'])
helper_mock.assert_called_with()
self.assertEqual(result.exit_code, 1)
self.assertEqual(result.exit_code, 2)

@patch('zapcli.zap_helper.ZAPHelper.is_running')
def test_check_status_running(self, helper_mock):
Expand All @@ -73,7 +73,7 @@ def test_check_status_not_running(self, helper_mock):
"""Test the status command when ZAP is not running."""
helper_mock.return_value = False
result = self.runner.invoke(cli.cli, ['--boring', '--api-key', '', 'status'])
self.assertEqual(result.exit_code, 1)
self.assertEqual(result.exit_code, 2)

@patch('zapcli.zap_helper.ZAPHelper.wait_for_zap')
@patch('zapcli.zap_helper.ZAPHelper.is_running')
Expand All @@ -82,7 +82,7 @@ def test_check_status_timeout(self, running_mock, wait_mock):
running_mock.return_value = False
wait_mock.side_effect = ZAPError('error')
result = self.runner.invoke(cli.cli, ['--boring', '--api-key', '', 'status', '-t', '0'])
self.assertEqual(result.exit_code, 1)
self.assertEqual(result.exit_code, 2)

@patch('zapcli.zap_helper.ZAPHelper.wait_for_zap')
@patch('zapcli.zap_helper.ZAPHelper.is_running')
Expand Down Expand Up @@ -147,14 +147,32 @@ def test_quick_scan(self, helper_mock):
'--spider', '--exclude', 'pattern'])
self.assertEqual(result.exit_code, 0)

@patch('zapcli.cli.ZAPHelper')
def test_quick_scan_issues_found(self, helper_mock):
"""Testing quick scan."""
instance = helper_mock.return_value
instance.scanner_groups = ['xss']
instance.scanner_group_map = {'xss': ['40012', '40014', '40016', '40017']}
instance.alerts.return_value = [{
'url': 'http://localhost/?test=%3C%2Fspan%3E%3Cscript%3Ealert%281%29%3B%3C%2Fscript%3E%3Cspan%3E',
'alert': 'Cross Site Scripting (Reflected)',
'cweid': '79',
'risk': 'High',
}]

result = self.runner.invoke(cli.cli, ['--boring', '--api-key', '', '--verbose', 'quick-scan',
'http://localhost/', '--self-contained', '--scanners', 'xss',
'--spider', '--exclude', 'pattern'])
self.assertEqual(result.exit_code, 1)

@patch('zapcli.zap_helper.ZAPHelper.start')
def test_quick_scan_start_error(self, helper_mock):
"""Testing quick scan."""
helper_mock.side_effect = ZAPError('error')

result = self.runner.invoke(cli.cli, ['--boring', '--api-key', '', '--verbose', 'quick-scan',
'http://localhost/', '--self-contained'])
self.assertEqual(result.exit_code, 1)
self.assertEqual(result.exit_code, 2)

@patch('zapcli.cli.ZAPHelper')
def test_quick_scan_shutdown_error(self, helper_mock):
Expand All @@ -165,7 +183,7 @@ def test_quick_scan_shutdown_error(self, helper_mock):

result = self.runner.invoke(cli.cli, ['--boring', '--api-key', '', '--verbose', 'quick-scan',
'http://localhost/', '--self-contained'])
self.assertEqual(result.exit_code, 1)
self.assertEqual(result.exit_code, 2)

@patch('zapcli.cli.ZAPHelper')
def test_quick_scan_enable_scanners_error(self, helper_mock):
Expand All @@ -178,7 +196,7 @@ def test_quick_scan_enable_scanners_error(self, helper_mock):

result = self.runner.invoke(cli.cli, ['--boring', '--api-key', '', '--verbose', 'quick-scan',
'http://localhost/', '--scanners', 'xss'])
self.assertEqual(result.exit_code, 1)
self.assertEqual(result.exit_code, 2)

@patch('zapcli.cli.ZAPHelper')
def test_quick_scan_exclude_from_all_error(self, helper_mock):
Expand All @@ -189,7 +207,7 @@ def test_quick_scan_exclude_from_all_error(self, helper_mock):

result = self.runner.invoke(cli.cli, ['--boring', '--api-key', '', '--verbose', 'quick-scan',
'http://localhost/', '--exclude', 'pattern'])
self.assertEqual(result.exit_code, 1)
self.assertEqual(result.exit_code, 2)

@patch('zapv2.ascan')
def test_active_scanners_enable(self, ascan_mock):
Expand Down Expand Up @@ -240,7 +258,7 @@ def test_enable_script_error(self, enable_mock):
enable_mock.return_value = 'Does Not Exist'
result = self.runner.invoke(cli.cli, ['--boring', '--api-key', '', 'scripts', 'enable', 'Foo.js'])
enable_mock.assert_called_with('Foo.js')
self.assertEqual(result.exit_code, 1)
self.assertEqual(result.exit_code, 2)

@patch('zapv2.script.disable')
def test_disable_script(self, disable_mock):
Expand All @@ -256,7 +274,7 @@ def test_disable_script_error(self, disable_mock):
disable_mock.return_value = 'Does Not Exist'
result = self.runner.invoke(cli.cli, ['--boring', '--api-key', '', 'scripts', 'disable', 'Foo.js'])
disable_mock.assert_called_with('Foo.js')
self.assertEqual(result.exit_code, 1)
self.assertEqual(result.exit_code, 2)

@patch('zapv2.script.remove')
def test_remove_script(self, remove_mock):
Expand All @@ -272,7 +290,7 @@ def test_remove_script_error(self, remove_mock):
remove_mock.return_value = 'Does Not Exist'
result = self.runner.invoke(cli.cli, ['--boring', '--api-key', '', 'scripts', 'remove', 'Foo.js'])
remove_mock.assert_called_with('Foo.js')
self.assertEqual(result.exit_code, 1)
self.assertEqual(result.exit_code, 2)

@patch('zapv2.script')
@patch('os.path.isfile')
Expand Down Expand Up @@ -305,7 +323,7 @@ def test_load_script_file_error(self, isfile_mock, script_mock):
result = self.runner.invoke(cli.cli, ['--boring', '--api-key', '', 'scripts', 'load',
'--name', 'Foo.js', '--script-type', 'proxy',
'--engine', 'Oracle Nashorn', '--file-path', 'Foo.js'])
self.assertEqual(result.exit_code, 1)
self.assertEqual(result.exit_code, 2)
self.assertFalse(script_mock.return_value.load.called)

@patch('zapv2.script')
Expand All @@ -324,7 +342,7 @@ def test_load_script_engine_error(self, isfile_mock, script_mock):
result = self.runner.invoke(cli.cli, ['--boring', '--api-key', '', 'scripts', 'load',
'--name', 'Foo.js', '--script-type', 'proxy',
'--engine', 'Invalid Engine', '--file-path', 'Foo.js'])
self.assertEqual(result.exit_code, 1)
self.assertEqual(result.exit_code, 2)
self.assertFalse(class_mock.load.called)

@patch('zapv2.script')
Expand All @@ -347,7 +365,7 @@ def test_load_script_unknown_error(self, isfile_mock, script_mock):
result = self.runner.invoke(cli.cli, ['--boring', '--api-key', '', 'scripts', 'load',
'--name', script_name, '--script-type', script_type,
'--engine', engine, '--file-path', script_name])
self.assertEqual(result.exit_code, 1)
self.assertEqual(result.exit_code, 2)
class_mock.load.assert_called_with(script_name, script_type, engine, script_name, scriptdescription='')

@patch('zapcli.zap_helper.ZAPHelper.xml_report')
Expand Down Expand Up @@ -390,7 +408,7 @@ def test_context_include_error(self, context_mock):
result = self.runner.invoke(cli.cli, ['--boring', '--api-key', '', '--verbose', 'context',
'include', '--name', 'Test', '--pattern', 'zap-cli'])
context_mock.assert_called_with(contextname='Test', regex='zap-cli')
self.assertEqual(result.exit_code, 1)
self.assertEqual(result.exit_code, 2)

def test_context_include_regex_error(self):
"""Testing that an error is reported when providing an invalid regex."""
Expand All @@ -414,7 +432,7 @@ def test_context_exclude_error(self, context_mock):
result = self.runner.invoke(cli.cli, ['--boring', '--api-key', '', '--verbose', 'context',
'exclude', '--name', 'Test', '--pattern', 'zap-cli'])
context_mock.assert_called_with(contextname='Test', regex='zap-cli')
self.assertEqual(result.exit_code, 1)
self.assertEqual(result.exit_code, 2)

def test_context_exclude_regex_error(self):
"""Testing that an error is reported when providing an invalid regex."""
Expand Down Expand Up @@ -443,7 +461,7 @@ def test_load_session_error(self, isfile_mock, session_mock):

result = self.runner.invoke(cli.cli, ['--boring', '--api-key', '', '--verbose', 'session',
'load', file_path])
self.assertEqual(result.exit_code, 1)
self.assertEqual(result.exit_code, 2)
self.assertFalse(session_mock.called)


Expand Down
17 changes: 10 additions & 7 deletions zapcli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def check_status(zap_helper, timeout):
console.info('ZAP is running')
else:
console.error('ZAP is not running')
sys.exit(1)
sys.exit(2)


@cli.command('open-url')
Expand Down Expand Up @@ -162,7 +162,8 @@ def active_scan(zap_helper, url, scanners, recursive, context_name, user_name):
@click.option('--output-format', '-f', default='table', type=click.Choice(['table', 'json']),
help='Output format to print the alerts.')
@click.option('--exit-code', default=True, type=bool,
help='Whether to set the exit code to the number of alerts (default: True).')
help='Whether to set a non-zero exit code when there are any alerts of the specified ' +
'level (default: True).')
@click.pass_obj
def show_alerts(zap_helper, alert_level, output_format, exit_code):
"""Show alerts at the given alert level."""
Expand All @@ -171,8 +172,8 @@ def show_alerts(zap_helper, alert_level, output_format, exit_code):
helpers.report_alerts(alerts, output_format)

if exit_code:
num_alerts = len(alerts)
sys.exit(num_alerts)
code = 1 if len(alerts) > 0 else 0
sys.exit(code)


@cli.command('quick-scan', short_help='Run a quick scan.')
Expand Down Expand Up @@ -208,6 +209,9 @@ def quick_scan(zap_helper, url, **options):
This command contains most scan options as parameters, so you can do
everything in one go.
If any alerts are found for the given alert level, this command will exit
with a status code of 1.
"""
if options['self_contained']:
console.info('Starting ZAP daemon')
Expand Down Expand Up @@ -235,16 +239,15 @@ def quick_scan(zap_helper, url, **options):

alerts = zap_helper.alerts(options['alert_level'])

num_alerts = len(alerts)

helpers.report_alerts(alerts, options['output_format'])

if options['self_contained']:
console.info('Shutting down ZAP daemon')
with helpers.zap_error_handler():
zap_helper.shutdown()

sys.exit(num_alerts)
exit_code = 1 if len(alerts) > 0 else 0
sys.exit(exit_code)


@cli.command('exclude', short_help='Exclude a pattern from all scanners.')
Expand Down
2 changes: 1 addition & 1 deletion zapcli/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def zap_error_handler():
yield
except ZAPError as ex:
console.error(str(ex))
sys.exit(1)
sys.exit(2)


def report_alerts(alerts, output_format='table'):
Expand Down

0 comments on commit 0185d40

Please sign in to comment.