Skip to content

Commit

Permalink
Merge pull request nccgroup#824 from nccgroup/bugfix/5.9.1
Browse files Browse the repository at this point in the history
Bugfix/Improve Error & Exception Handling
  • Loading branch information
x4v13r64 committed Jul 28, 2020
2 parents 8291714 + e99ff37 commit e5dd01d
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 141 deletions.
55 changes: 55 additions & 0 deletions .github/workflows/testing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: CI Workflow

on:
push:
branches:
- develop
- master
pull_request:


jobs:
build:

runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.6, 3.7, 3.8]

steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Cache pip
uses: actions/cache@v2
with:
# This path is specific to Ubuntu
path: ~/.cache/pip
# Look to see if there is a cache hit for the corresponding requirements file
key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
${{ runner.os }}-
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -U setuptools
pip install -r dev-requirements.txt
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 .
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Autopep 8
run: |
autopep8 --diff --recursive --max-line-length=127 .
- name: Test with pytest and generate coverage report
run: |
python -m pytest --cov=./ --cov-report=xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
with:
file: ./coverage.xml
54 changes: 0 additions & 54 deletions .travis.yml

This file was deleted.

2 changes: 1 addition & 1 deletion ScoutSuite/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
__author__ = 'NCC Group'
__version__ = '5.9.0'
__version__ = '5.9.1'

ERRORS_LIST = []

Expand Down
133 changes: 84 additions & 49 deletions ScoutSuite/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ async def _run(provider,

print_info('Authenticating to cloud provider')
auth_strategy = get_authentication_strategy(provider)

try:
credentials = auth_strategy.authenticate(profile=profile,
aws_access_key_id=aws_access_key_id,
Expand All @@ -205,6 +206,7 @@ async def _run(provider,
except Exception as e:
print_exception('Authentication failure: {}'.format(e))
return 101

# Create a cloud provider object
try:
cloud_provider = get_provider(provider=provider,
Expand All @@ -230,17 +232,21 @@ async def _run(provider,
return 102

# Create a new report
report_name = report_name if report_name else cloud_provider.get_report_name()
report = ScoutReport(cloud_provider.provider_code,
report_name,
report_dir,
timestamp,
result_format=result_format)

if database_name:
database_file, _ = get_filename('RESULTS', report_name, report_dir, file_extension="db")
Server.init(database_file, host_ip, host_port)
return
try:
report_name = report_name if report_name else cloud_provider.get_report_name()
report = ScoutReport(cloud_provider.provider_code,
report_name,
report_dir,
timestamp,
result_format=result_format)

if database_name:
database_file, _ = get_filename('RESULTS', report_name, report_dir, file_extension="db")
Server.init(database_file, host_ip, host_port)
return
except Exception as e:
print_exception('Report initialization failure: {}'.format(e))
return 103

# If this command, run and exit
if list_services:
Expand All @@ -259,47 +265,68 @@ async def _run(provider,
except KeyboardInterrupt:
print_info('\nCancelled by user')
return 130
except Exception as e:
print_exception('Unhandled exception thrown while gathering data: {}'.format(e))
return 104

# Update means we reload the whole config and overwrite part of it
if update:
print_info('Updating existing data')
current_run_services = copy.deepcopy(cloud_provider.services)
last_run_dict = report.encoder.load_from_file('RESULTS')
cloud_provider.services = last_run_dict['services']
for service in cloud_provider.service_list:
cloud_provider.services[service] = current_run_services[service]
try:
print_info('Updating existing data')
current_run_services = copy.deepcopy(cloud_provider.services)
last_run_dict = report.encoder.load_from_file('RESULTS')
cloud_provider.services = last_run_dict['services']
for service in cloud_provider.service_list:
cloud_provider.services[service] = current_run_services[service]
except Exception as e:
print_exception('Failure while updating report: {}'.format(e))

# Partial run, using pre-pulled data
else:
print_info('Using local data')
# Reload to flatten everything into a python dictionary
last_run_dict = report.encoder.load_from_file('RESULTS')
for key in last_run_dict:
setattr(cloud_provider, key, last_run_dict[key])
try:
print_info('Using local data')
# Reload to flatten everything into a python dictionary
last_run_dict = report.encoder.load_from_file('RESULTS')
for key in last_run_dict:
setattr(cloud_provider, key, last_run_dict[key])
except Exception as e:
print_exception('Failure while updating report: {}'.format(e))

# Pre processing
cloud_provider.preprocessing(
ip_ranges, ip_ranges_name_key)
try:
print_info('Running pre-processing engine')
cloud_provider.preprocessing(ip_ranges, ip_ranges_name_key)
except Exception as e:
print_exception('Failure while running pre-processing engine: {}'.format(e))
return 105

# Analyze config
print_info('Running rule engine')
finding_rules = Ruleset(cloud_provider=cloud_provider.provider_code,
environment_name=cloud_provider.environment,
filename=ruleset,
ip_ranges=ip_ranges,
account_id=cloud_provider.account_id)
processing_engine = ProcessingEngine(finding_rules)
processing_engine.run(cloud_provider)
try:
print_info('Running rule engine')
finding_rules = Ruleset(cloud_provider=cloud_provider.provider_code,
environment_name=cloud_provider.environment,
filename=ruleset,
ip_ranges=ip_ranges,
account_id=cloud_provider.account_id)
processing_engine = ProcessingEngine(finding_rules)
processing_engine.run(cloud_provider)
except Exception as e:
print_exception('Failure while running rule engine: {}'.format(e))
return 106

# Create display filters
print_info('Applying display filters')
filter_rules = Ruleset(cloud_provider=cloud_provider.provider_code,
environment_name=cloud_provider.environment,
filename='filters.json',
rule_type='filters',
account_id=cloud_provider.account_id)
processing_engine = ProcessingEngine(filter_rules)
processing_engine.run(cloud_provider)
try:
print_info('Applying display filters')
filter_rules = Ruleset(cloud_provider=cloud_provider.provider_code,
environment_name=cloud_provider.environment,
filename='filters.json',
rule_type='filters',
account_id=cloud_provider.account_id)
processing_engine = ProcessingEngine(filter_rules)
processing_engine.run(cloud_provider)
except Exception as e:
print_exception('Failure while applying display filters: {}'.format(e))
return 107

# Handle exceptions
if exceptions:
Expand All @@ -314,18 +341,26 @@ async def _run(provider,
else:
exceptions = {}

run_parameters = {
'services': services,
'skipped_services': skipped_services,
'regions': regions,
'excluded_regions': excluded_regions,
}
# Finalize
cloud_provider.postprocessing(report.current_time, finding_rules, run_parameters)
try:
print_info('Running post-processing engine')
run_parameters = {
'services': services,
'skipped_services': skipped_services,
'regions': regions,
'excluded_regions': excluded_regions,
}
cloud_provider.postprocessing(report.current_time, finding_rules, run_parameters)
except Exception as e:
print_exception('Failure while running post-processing engine: {}'.format(e))
return 108

# Save config and create HTML report
html_report_path = report.save(
cloud_provider, exceptions, force_write, debug)
try:
html_report_path = report.save(cloud_provider, exceptions, force_write, debug)
except Exception as e:
print_exception('Failure while generating HTML report: {}'.format(e))
return 109

# Open the report by default
if not no_browser:
Expand Down
2 changes: 1 addition & 1 deletion ScoutSuite/providers/azure/facade/aad.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ async def get_user(self, user_id):
return await run_concurrently(lambda: self.get_client().users.get(user_id))
except Exception as e:
print_exception('Failed to retrieve user {}: {}'.format(user_id, e))
return []
return None

async def get_groups(self):
try:
Expand Down
47 changes: 25 additions & 22 deletions ScoutSuite/providers/azure/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,25 +81,28 @@ def _match_rbac_roles_and_principals(self):
"""
Matches ARM role assignments to AAD service principals
"""
if 'rbac' in self.service_list and 'aad' in self.service_list:
for subscription in self.services['rbac']['subscriptions']:
for assignment in self.services['rbac']['subscriptions'][subscription]['role_assignments'].values():
role_id = assignment['role_definition_id'].split('/')[-1]
for group in self.services['aad']['groups']:
if group == assignment['principal_id']:
self.services['aad']['groups'][group]['roles'].append({'subscription_id': subscription,
'role_id': role_id})
self.services['rbac']['subscriptions'][subscription]['roles'][role_id]['assignments']['groups'].append(group)
self.services['rbac']['subscriptions'][subscription]['roles'][role_id]['assignments_count'] += 1
for user in self.services['aad']['users']:
if user == assignment['principal_id']:
self.services['aad']['users'][user]['roles'].append({'subscription_id': subscription,
'role_id': role_id})
self.services['rbac']['subscriptions'][subscription]['roles'][role_id]['assignments']['users'].append(user)
self.services['rbac']['subscriptions'][subscription]['roles'][role_id]['assignments_count'] += 1
for service_principal in self.services['aad']['service_principals']:
if service_principal == assignment['principal_id']:
self.services['aad']['service_principals'][service_principal]['roles'].append({'subscription_id': subscription,
'role_id': role_id})
self.services['rbac']['subscriptions'][subscription]['roles'][role_id]['assignments']['service_principals'].append(service_principal)
self.services['rbac']['subscriptions'][subscription]['roles'][role_id]['assignments_count'] += 1
try:
if 'rbac' in self.service_list and 'aad' in self.service_list:
for subscription in self.services['rbac']['subscriptions']:
for assignment in self.services['rbac']['subscriptions'][subscription]['role_assignments'].values():
role_id = assignment['role_definition_id'].split('/')[-1]
for group in self.services['aad']['groups']:
if group == assignment['principal_id']:
self.services['aad']['groups'][group]['roles'].append({'subscription_id': subscription,
'role_id': role_id})
self.services['rbac']['subscriptions'][subscription]['roles'][role_id]['assignments']['groups'].append(group)
self.services['rbac']['subscriptions'][subscription]['roles'][role_id]['assignments_count'] += 1
for user in self.services['aad']['users']:
if user == assignment['principal_id']:
self.services['aad']['users'][user]['roles'].append({'subscription_id': subscription,
'role_id': role_id})
self.services['rbac']['subscriptions'][subscription]['roles'][role_id]['assignments']['users'].append(user)
self.services['rbac']['subscriptions'][subscription]['roles'][role_id]['assignments_count'] += 1
for service_principal in self.services['aad']['service_principals']:
if service_principal == assignment['principal_id']:
self.services['aad']['service_principals'][service_principal]['roles'].append({'subscription_id': subscription,
'role_id': role_id})
self.services['rbac']['subscriptions'][subscription]['roles'][role_id]['assignments']['service_principals'].append(service_principal)
self.services['rbac']['subscriptions'][subscription]['roles'][role_id]['assignments_count'] += 1
except Exception as e:
print_exception('Unable to match RBAC roles and principals: {}'.format(e))
Loading

0 comments on commit e5dd01d

Please sign in to comment.