Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python 3.11 and return (un)suppressed findings #20

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.8.18
python-version: 3.11.10

- name: Install pipenv
run: pip install pipenv
Expand All @@ -46,7 +46,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.8.18
python-version: 3.11.10

- name: Install pipenv
run: pip install pipenv
Expand Down
2 changes: 1 addition & 1 deletion .python-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.8.18
3.11.10
6 changes: 3 additions & 3 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ sphinx = ">=7.0,<8.0"
sphinx-rtd-theme = ">=1.0,<2.0"
prospector = ">=1.8,<2.0"
coverage = ">=7,<8.0"
nose = ">=1.3,<2.0"
pynose = ">=1.5.3,<2.0"
nose-htmloutput = ">=0.1,<1.0"
tox = ">=4.0<5.0"
tox = ">=4.0,<5.0"
betamax = ">=0.8,<1.0"
betamax-serializers = "~=0.2,<1.0"
semver = ">=3.0,<4.0"
gitwrapperlib = ">=1.0,<2.0"
twine = ">=4.0,<5.0"
coloredlogs = ">=15.0,<16.0"
emoji = ">=2.0,<3.0"
emoji = ">=2.13.2,<3.0"
toml = ">=0.1,<1.0"
typing-extensions = ">=4.0,<5.0"

Expand Down
779 changes: 330 additions & 449 deletions Pipfile.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion _CI/files/environment_variables.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"PIPENV_VENV_IN_PROJECT": "true",
"PIPENV_DEFAULT_PYTHON_VERSION": "3.8",
"PIPENV_DEFAULT_PYTHON_VERSION": "3.11",
"PYPI_URL": "https://upload.pypi.org/legacy/",
"PROJECT_SLUG": "awsfindingsmanagerlib"
}
132 changes: 88 additions & 44 deletions awsfindingsmanagerlib/awsfindingsmanagerlib.py

Large diffs are not rendered by default.

16 changes: 8 additions & 8 deletions dev-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@
#
# Please use Pipfile to update the requirements.
#
sphinx>=7.1.2 ; python_version >= '3.8'
sphinx>=7.3.7 ; python_version >= '3.9'
sphinx-rtd-theme>=1.3.0 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'
prospector>=1.10.3 ; python_version < '4.0' and python_full_version >= '3.7.2'
coverage>=7.5.3 ; python_version >= '3.8'
nose>=1.3.7
prospector>=1.10.3 ; python_full_version >= '3.7.2' and python_version < '4.0'
coverage>=7.6.1 ; python_version >= '3.8'
pynose>=1.5.3 ; python_version >= '3.7'
nose-htmloutput>=0.6.0
tox>=4.15.1 ; python_version >= '3.8'
tox>=4.21.0 ; python_version >= '3.8'
betamax>=0.9.0 ; python_full_version >= '3.8.1'
betamax-serializers~=0.2.1
semver>=3.0.2 ; python_version >= '3.7'
gitwrapperlib>=1.0.4
twine>=4.0.2 ; python_version >= '3.7'
coloredlogs>=15.0.1 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
emoji>=2.12.1 ; python_version >= '3.7'
toml>=0.10.2 ; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'
typing-extensions>=4.12.2 ; python_version < '3.10'
emoji>=2.13.2 ; python_version >= '3.7'
toml>=0.10.2 ; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'
typing-extensions>=4.12.2 ; python_version >= '3.8'
6 changes: 3 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
#
# Please use Pipfile to update the requirements.
#
boto3>=1.34.129 ; python_version >= '3.8'
boto3>=1.35.31 ; python_version >= '3.8'
opnieuw>=1.2.1 ; python_version >= '3.7'
python-dateutil>=2.9.0.post0 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
python-dateutil>=2.9.0.post0 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'
schema~=0.7.7
requests~=2.32.3 ; python_version >= '3.8'
pyyaml~=6.0.1 ; python_version >= '3.6'
pyyaml~=6.0.2 ; python_version >= '3.8'
52 changes: 33 additions & 19 deletions tests/test_suppressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,64 +69,78 @@
# hence no dev for S3.14
for env in ['dev', 'acc', 'prd'] if security_control_id != 'S3.14' else ['acc', 'prd']:
with open(f'tests/fixtures/findings/full/{security_control_id}/{env}.json', encoding='utf-8') as findings_file:
findings_by_security_control_id_fixture[security_control_id].append(json.load(findings_file))
findings_by_security_control_id_fixture[security_control_id].append(
json.load(findings_file))

with open('tests/fixtures/matches.json', encoding='utf-8') as matches_file:
full_matches_fixture = json.load(matches_file)


def batch_update_findings_mock(_, payload):
return (True, payload)


class TestValidation(FindingsManagerTestCase):
backend_file = './tests/fixtures/suppressions/single.yaml'

def test_basic_run(self):
self.assertEqual(
[],
self.findings_manager._construct_findings_on_matching_rules(api_consolidated_findings_fixture['Findings'])
self.findings_manager._construct_findings_on_matching_rules(
api_consolidated_findings_fixture['Findings'])
)


class TestLegacyValidation(FindingsManagerTestCase):
backend_file = './tests/fixtures/suppressions/legacy.yaml'

def test_basic_run(self):
self.assertEqual(
[],
self.findings_manager._construct_findings_on_matching_rules(gui_legacy_findings_fixture)
self.findings_manager._construct_findings_on_matching_rules(
gui_legacy_findings_fixture)
)


class TestBasicRun(FindingsManagerTestCase):
@patch(
'awsfindingsmanagerlib.FindingsManager._get_security_hub_paginator_iterator',
lambda *_, **__: [api_consolidated_findings_fixture],
)
@patch('awsfindingsmanagerlib.FindingsManager._batch_update_findings')
@patch('awsfindingsmanagerlib.FindingsManager._batch_update_findings', side_effect=batch_update_findings_mock)
def test_basic_run(self, _batch_update_findings_mocked: MagicMock):
self.assertTrue(self.findings_manager.suppress_matching_findings())
self.assert_batch_update_findings_called_with(
[batch_update_findings_fixture], _batch_update_findings_mocked
)
success, payloads = self.findings_manager.suppress_matching_findings()
self.assertTrue(success)
self.assert_batch_update_findings(
[batch_update_findings_fixture], payloads)


class TestFullSuppressions(FindingsManagerTestCase):
backend_file = './tests/fixtures/suppressions/full.yaml'

def test_validation(self):
self.assertEqual(full_matches_fixture,
[dict(finding._data, matched_rule=finding._matched_rule._data)
for finding in self.findings_manager._construct_findings_on_matching_rules(full_findings_fixture)]
)
[dict(finding._data, matched_rule=finding._matched_rule._data)
for finding in self.findings_manager._construct_findings_on_matching_rules(full_findings_fixture)]
)

@patch('awsfindingsmanagerlib.FindingsManager._batch_update_findings')
@patch('awsfindingsmanagerlib.FindingsManager._batch_update_findings', side_effect=batch_update_findings_mock)
def test_payload_construction(self, _batch_update_findings_mocked: MagicMock):
self.assertTrue(self.findings_manager.suppress_findings_on_matching_rules(full_findings_fixture))
self.assert_batch_update_findings_called_with(batch_update_findings_full_fixture, _batch_update_findings_mocked)
success, payloads = self.findings_manager.suppress_findings_on_matching_rules(
full_findings_fixture)
self.assertTrue(success)
self.assert_batch_update_findings(
batch_update_findings_full_fixture, payloads)

@patch(
'awsfindingsmanagerlib.FindingsManager._get_security_hub_paginator_iterator',
lambda *_, **kwargs: [{
'Findings': findings_by_security_control_id_fixture[kwargs['query_filter']['ComplianceSecurityControlId'][0]['Value']]
}],
)
@patch('awsfindingsmanagerlib.FindingsManager._batch_update_findings')
@patch('awsfindingsmanagerlib.FindingsManager._batch_update_findings', side_effect=batch_update_findings_mock)
def test_from_query(self, _batch_update_findings_mocked: MagicMock):
self.assertTrue(self.findings_manager.suppress_matching_findings())
self.assert_batch_update_findings_called_with(
batch_update_findings_full_fixture, _batch_update_findings_mocked
)
success, payloads = self.findings_manager.suppress_matching_findings()
self.assertTrue(success)
self.assert_batch_update_findings(
batch_update_findings_full_fixture, payloads)
29 changes: 13 additions & 16 deletions tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,28 +69,25 @@ def setUp(self) -> None:
self.findings_manager = FindingsManager()
self.findings_manager.register_rules(rules)

def assert_batch_update_findings_called_with(self, batch_update_findings_expected: List[dict], _batch_update_findings_mocked: MagicMock):
def assert_batch_update_findings(self, batch_update_findings_expected: List[dict], batch_update_findings: List[dict]):
"""
Compare expected to actual (=mocked) api call payload.

Sadly, something like this does not work: _batch_update_findings_mocked.assert_called_once_with(ANY, batch_update_findings),
because FindingIdentifiers is a randomly ordered collection.
Compare expected to actual api call payload.
"""
self.assertEqual(
len(batch_update_findings_expected),
_batch_update_findings_mocked.call_count
)
self.assertEqual(len(batch_update_findings_expected),
len(batch_update_findings))

for expected in batch_update_findings_expected:
for call in _batch_update_findings_mocked.call_args_list:
for finding in batch_update_findings:
try:
received_args = call.args[1]
self.assertEqual(expected.keys(), received_args.keys())
self.assertEqual(expected['Note'], received_args['Note'])
self.assertEqual(expected['Workflow'], received_args['Workflow'])
self.assertEqual(len(expected['FindingIdentifiers']), len(received_args['FindingIdentifiers']))
self.assertTrue(
set(expected.keys()).issubset(set(finding.keys())))
self.assertEqual(expected['Note'], finding['Note'])
self.assertEqual(
expected['Workflow'], finding['Workflow'])
self.assertEqual(len(expected['FindingIdentifiers']), len(
finding['FindingIdentifiers']))
for item in expected['FindingIdentifiers']:
self.assertIn(item, received_args['FindingIdentifiers'])
self.assertIn(item, finding['FindingIdentifiers'])
break
except:
continue
Expand Down
4 changes: 2 additions & 2 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
# and then run "tox" from this directory.

[tox]
envlist = py38,
envlist = py311,

[testenv]
allowlist_externals = *
commands = ./setup.py nosetests --with-coverage --cover-tests --cover-html --cover-html-dir=test-output/coverage --with-html --html-file test-output/nosetests.html
commands = nosetests --with-coverage --cover-tests --cover-html --cover-html-dir=test-output/coverage --with-html --html-file test-output/nosetests.html
deps =
-rrequirements.txt
-rdev-requirements.txt
Expand Down
Loading