From 40d2b19b9cd56d8ce881e49bb318472383c96143 Mon Sep 17 00:00:00 2001 From: Chris Lenk Date: Thu, 20 Apr 2023 11:15:10 -0400 Subject: [PATCH 1/9] Test Python 3.11 in test harness --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 5be2b45fe..a69c3dd79 100644 --- a/tox.ini +++ b/tox.ini @@ -6,7 +6,7 @@ [tox] skipsdist = True envlist = - py{37,38,39,310} + py{37,38,39,310,311} style coverage safety @@ -25,7 +25,7 @@ deps = coverage commands = py37: coverage run -p -m pytest --tb=short -Werror --asyncio-mode=auto tests - py{38,39,310}: coverage run -p -m pytest --tb=short --asyncio-mode=auto tests + py{38,39,310,311}: coverage run -p -m pytest --tb=short --asyncio-mode=auto tests [testenv:style] deps = pre-commit From f32a96f887a9e1f689b6e4d908ddd8b79dce6a0b Mon Sep 17 00:00:00 2001 From: Chris Lenk Date: Thu, 20 Apr 2023 11:15:46 -0400 Subject: [PATCH 2/9] Update requirements for Python 3.11 --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index da1719a79..06a553b03 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ aiohttp-jinja2==1.5.0 -aiohttp==3.8.1 +aiohttp==3.8.4 aiohttp_session==2.9.0 aiohttp-security==0.4.0 aiohttp-apispec==2.2.3 @@ -19,7 +19,7 @@ donut-shellcode==0.9.2 marshmallow-enum==1.5.1 ldap3==2.8.1 lxml~=4.9.1 # debrief -reportlab==3.5.67 # debrief +reportlab==3.6.12 # debrief svglib==1.0.1 # debrief Markdown==3.3.3 # training dnspython==2.1.0 From 48ed801c011117a1a916e2bb35e9d88d080fd5fd Mon Sep 17 00:00:00 2001 From: Chris Lenk Date: Thu, 20 Apr 2023 23:00:12 -0400 Subject: [PATCH 3/9] Fix failing operation test (writing log to disk) --- tests/objects/test_operation.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/objects/test_operation.py b/tests/objects/test_operation.py index 0db4454bc..ec5b3de49 100644 --- a/tests/objects/test_operation.py +++ b/tests/objects/test_operation.py @@ -17,6 +17,7 @@ from app.objects.secondclass.c_result import Result from app.objects.secondclass.c_fact import Fact from app.utility.base_object import BaseObject +from app.utility.base_world import BaseWorld LINK1_DECIDE_TIME = MOCK_LINK_FINISH_TIME = '2021-01-01T08:00:00Z' LINK1_COLLECT_TIME = '2021-01-01T08:01:00Z' @@ -74,6 +75,12 @@ def _encode_command(command_str): return _encode_command +@pytest.fixture +def setup_op_config(): + BaseWorld.apply_config(name='main', config={'exfil_dir': '/tmp/caldera', + 'reports_dir': '/tmp'}) + + @pytest.fixture def op_for_event_logs(operation_agent, operation_adversary, executor, ability, operation_link, encoded_command, parse_datestring): @@ -263,6 +270,9 @@ def test_event_logs(self, event_loop, op_for_event_logs, operation_agent, file_s event_logs = event_loop.run_until_complete(op_for_event_logs.event_logs(file_svc, data_svc)) assert event_logs == want + @pytest.mark.usefixtures( + "setup_op_config" + ) def test_writing_event_logs_to_disk(self, event_loop, op_for_event_logs, operation_agent, file_svc, data_svc, event_log_op_start_time, op_agent_creation_time, fire_event_mock): event_loop.run_until_complete(data_svc.remove('agents', match=dict(unique=operation_agent.unique))) From 933bae04541cae82d9aa256d9271957a790555e2 Mon Sep 17 00:00:00 2001 From: Chris Lenk Date: Fri, 21 Apr 2023 10:18:21 -0400 Subject: [PATCH 4/9] Increase stacklevel of warnings to appease flake8 B028 --- app/service/data_svc.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/service/data_svc.py b/app/service/data_svc.py index 499203a01..f71af6e87 100644 --- a/app/service/data_svc.py +++ b/app/service/data_svc.py @@ -240,15 +240,15 @@ async def load_requirements_from_list(self, requirements: list): return [RequirementSchema().load(entry) for entry in requirements] async def load_adversary_file(self, filename, access): - warnings.warn("Function deprecated and will be removed in a future update. Use load_yaml_file", DeprecationWarning) + warnings.warn("Function deprecated and will be removed in a future update. Use load_yaml_file", DeprecationWarning, stacklevel=2) await self.load_yaml_file(Adversary, filename, access) async def load_source_file(self, filename, access): - warnings.warn("Function deprecated and will be removed in a future update. Use load_yaml_file", DeprecationWarning) + warnings.warn("Function deprecated and will be removed in a future update. Use load_yaml_file", DeprecationWarning, stacklevel=2) await self.load_yaml_file(Source, filename, access) async def load_objective_file(self, filename, access): - warnings.warn("Function deprecated and will be removed in a future update. Use load_yaml_file", DeprecationWarning) + warnings.warn("Function deprecated and will be removed in a future update. Use load_yaml_file", DeprecationWarning, stacklevel=2) await self.load_yaml_file(Objective, filename, access) async def load_yaml_file(self, object_class, filename, access): From c2d4e69c353ddb65d7e060d350ffeb56060bb68a Mon Sep 17 00:00:00 2001 From: Chris Lenk Date: Fri, 21 Apr 2023 12:01:45 -0400 Subject: [PATCH 5/9] Use our own AsyncMock The one from unittest doesn't exist in Python 3.7. Also consolidate a second async mock wrapper for code reuse. --- tests/conftest.py | 2 +- tests/services/test_planning_svc.py | 12 +++--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 6630c8a21..c968b8e91 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,7 +2,6 @@ import os.path import pytest -from unittest.mock import AsyncMock import random import string import uuid @@ -63,6 +62,7 @@ from app.api.rest_api import RestApi from app import version +from tests import AsyncMock DIR = os.path.dirname(os.path.abspath(__file__)) CONFIG_DIR = os.path.join(DIR, '..', 'conf') diff --git a/tests/services/test_planning_svc.py b/tests/services/test_planning_svc.py index d9369c5aa..655b3de16 100644 --- a/tests/services/test_planning_svc.py +++ b/tests/services/test_planning_svc.py @@ -11,6 +11,7 @@ from app.objects.secondclass.c_fact import Fact from app.objects.secondclass.c_requirement import Requirement from app.utility.base_world import BaseWorld +from tests import AsyncMock stop_bucket_exhaustion_params = [ @@ -69,13 +70,6 @@ def __init__(self, **kwargs): return PlannerStub(**kwargs) -def async_wrapper(return_value): - """Creates an async method that returns a constant value for mocking purposes.""" - async def wrap(*args, **kwargs): - return return_value - return wrap - - @pytest.fixture async def setup_planning_test(executor, ability, agent, operation, data_svc, event_svc, init_base_world): texecutor = executor(name='sh', platform='darwin', command='mkdir test', cleanup='rm -rf test') @@ -355,9 +349,9 @@ async def test_trim_links(self, setup_planning_test, planning_svc): Fact(trait='a.b.e', value='3'), ] - operation.all_facts = async_wrapper(facts) + operation.all_facts = AsyncMock(return_value=facts) operation.planner = MagicMock() - planning_svc.load_module = async_wrapper(RequirementFake()) + planning_svc.load_module = AsyncMock(return_value=RequirementFake()) link.ability.requirements = [Requirement('fake_requirement', [{'fake': 'relationship'}])] trimmed_links = await planning_svc.trim_links(operation, [link], agent) From 8c7d05ac625d0079afeb0b7d2b9bc9c8e12fb28e Mon Sep 17 00:00:00 2001 From: Chris Lenk Date: Fri, 21 Apr 2023 13:50:44 -0400 Subject: [PATCH 6/9] Update SonarCloud GitHub action version --- .github/workflows/quality.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index ecba2de4e..fb105c67c 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -29,6 +29,8 @@ jobs: toxenv: py39,style,coverage-ci - python-version: 3.10.9 toxenv: py310,style,coverage-ci + - python-version: 3.11 + toxenv: py311,style,coverage-ci steps: - uses: actions/checkout@7884fcad6b5d53d10323aee724dc68d8b9096a2e @@ -50,7 +52,7 @@ jobs: - name: Override Coverage Source Path for Sonar run: sed -i "s/\/home\/runner\/work\/caldera\/caldera/\/github\/workspace/g" /home/runner/work/caldera/caldera/coverage.xml - name: SonarCloud Scan - uses: SonarSource/sonarcloud-github-action@156db6fef3e168e4972abb76de0b32bbce8ec77a + uses: SonarSource/sonarcloud-github-action@5875562561d22a34be0c657405578705a169af6c env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} From 52cde70206a9c0cc5fce7e718b4f984d9f07a15b Mon Sep 17 00:00:00 2001 From: Chris Lenk Date: Wed, 26 Apr 2023 09:18:20 -0400 Subject: [PATCH 7/9] Refactor duplicate string to appease sonarcloud --- app/service/data_svc.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/service/data_svc.py b/app/service/data_svc.py index f71af6e87..97d75232d 100644 --- a/app/service/data_svc.py +++ b/app/service/data_svc.py @@ -41,6 +41,8 @@ PAYLOADS_CONFIG_SPECIAL_KEY = 'special_payloads' PAYLOADS_CONFIG_EXTENSIONS_KEY = 'extensions' +DEPRECATION_WARNING_LOAD = "Function deprecated and will be removed in a future update. Use load_yaml_file" + class DataService(DataServiceInterface, BaseService): @@ -240,15 +242,15 @@ async def load_requirements_from_list(self, requirements: list): return [RequirementSchema().load(entry) for entry in requirements] async def load_adversary_file(self, filename, access): - warnings.warn("Function deprecated and will be removed in a future update. Use load_yaml_file", DeprecationWarning, stacklevel=2) + warnings.warn(DEPRECATION_WARNING_LOAD) await self.load_yaml_file(Adversary, filename, access) async def load_source_file(self, filename, access): - warnings.warn("Function deprecated and will be removed in a future update. Use load_yaml_file", DeprecationWarning, stacklevel=2) + warnings.warn(DEPRECATION_WARNING_LOAD) await self.load_yaml_file(Source, filename, access) async def load_objective_file(self, filename, access): - warnings.warn("Function deprecated and will be removed in a future update. Use load_yaml_file", DeprecationWarning, stacklevel=2) + warnings.warn(DEPRECATION_WARNING_LOAD) await self.load_yaml_file(Objective, filename, access) async def load_yaml_file(self, object_class, filename, access): From 955bb2a26d157b33021042bc62d08a4ada9fd278 Mon Sep 17 00:00:00 2001 From: Chris Lenk Date: Wed, 26 Apr 2023 09:37:37 -0400 Subject: [PATCH 8/9] Update GitHub Actions versions --- .github/workflows/publish_docker_image.yml | 6 +++--- .github/workflows/quality.yml | 4 ++-- .github/workflows/security.yml | 4 ++-- .github/workflows/stale.yml | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/publish_docker_image.yml b/.github/workflows/publish_docker_image.yml index 772c09b17..54db57f4b 100644 --- a/.github/workflows/publish_docker_image.yml +++ b/.github/workflows/publish_docker_image.yml @@ -21,10 +21,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@7884fcad6b5d53d10323aee724dc68d8b9096a2e + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab - name: Log in to the Container registry - uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 + uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} @@ -32,7 +32,7 @@ jobs: - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 + uses: docker/metadata-action@c4ee3adeed93b1fa6a762f209fb01608c1a22f1e with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index fb105c67c..5fba75867 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -33,12 +33,12 @@ jobs: toxenv: py311,style,coverage-ci steps: - - uses: actions/checkout@7884fcad6b5d53d10323aee724dc68d8b9096a2e + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab with: submodules: recursive fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: Setup python - uses: actions/setup-python@b55428b1882923874294fa556849718a1d7f2ca5 + uses: actions/setup-python@57ded4d7d5e986d7296eab16560982c6dd7c923b with: python-version: ${{ matrix.python-version }} - name: Install dependencies diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index b41fa21e6..fa10419d0 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -22,11 +22,11 @@ jobs: toxenv: safety steps: - - uses: actions/checkout@7884fcad6b5d53d10323aee724dc68d8b9096a2e + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab with: submodules: recursive - name: Setup python - uses: actions/setup-python@b55428b1882923874294fa556849718a1d7f2ca5 + uses: actions/setup-python@57ded4d7d5e986d7296eab16560982c6dd7c923b with: python-version: ${{ matrix.python-version }} - name: Install dependencies diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 2d6ad2be7..4e694d21d 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -17,7 +17,7 @@ jobs: pull-requests: write steps: - - uses: actions/stale@9c1b1c6e115ca2af09755448e0dbba24e5061cc8 + - uses: actions/stale@a20b814fb01b71def3bd6f56e7494d667ddf28da with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-issue-label: 'no-issue-activity' From 3c5b8e5a064be240dcb94b428f637f49bd6a153b Mon Sep 17 00:00:00 2001 From: Chris Lenk Date: Wed, 26 Apr 2023 10:28:57 -0400 Subject: [PATCH 9/9] Fix warning stacklevel --- app/service/data_svc.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/service/data_svc.py b/app/service/data_svc.py index 97d75232d..5f23a0d58 100644 --- a/app/service/data_svc.py +++ b/app/service/data_svc.py @@ -242,15 +242,15 @@ async def load_requirements_from_list(self, requirements: list): return [RequirementSchema().load(entry) for entry in requirements] async def load_adversary_file(self, filename, access): - warnings.warn(DEPRECATION_WARNING_LOAD) + warnings.warn(DEPRECATION_WARNING_LOAD, DeprecationWarning, stacklevel=2) await self.load_yaml_file(Adversary, filename, access) async def load_source_file(self, filename, access): - warnings.warn(DEPRECATION_WARNING_LOAD) + warnings.warn(DEPRECATION_WARNING_LOAD, DeprecationWarning, stacklevel=2) await self.load_yaml_file(Source, filename, access) async def load_objective_file(self, filename, access): - warnings.warn(DEPRECATION_WARNING_LOAD) + warnings.warn(DEPRECATION_WARNING_LOAD, DeprecationWarning, stacklevel=2) await self.load_yaml_file(Objective, filename, access) async def load_yaml_file(self, object_class, filename, access):