From 6f22bc158eb53dbdcf576fcf9070ab14b83c6ae4 Mon Sep 17 00:00:00 2001 From: bleepbop Date: Wed, 29 Jun 2022 14:21:46 -0400 Subject: [PATCH 01/65] update reason enum --- app/objects/c_operation.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/objects/c_operation.py b/app/objects/c_operation.py index a4beb9bac..b701a9453 100644 --- a/app/objects/c_operation.py +++ b/app/objects/c_operation.py @@ -517,10 +517,12 @@ async def _get_agent_info_for_event_log(agent_paw, data_svc): class Reason(Enum): PLATFORM = 0 EXECUTOR = 1 - FACT_DEPENDENCY = 2 - PRIVILEGE = 3 - OP_RUNNING = 4 + PRIVILEGE = 2 + FACT_DEPENDENCY = 3 + LINK_IGNORED = 4 UNTRUSTED = 5 + OP_RUNNING = 6 + OTHER = 7 class States(Enum): RUNNING = 'running' From e5340060b75e2fa7adb70b8a21b69945e145b517 Mon Sep 17 00:00:00 2001 From: bleepbop Date: Thu, 30 Jun 2022 10:26:46 -0400 Subject: [PATCH 02/65] add additional checks to _check_reason_skipped --- app/objects/c_operation.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/app/objects/c_operation.py b/app/objects/c_operation.py index b701a9453..7304688f2 100644 --- a/app/objects/c_operation.py +++ b/app/objects/c_operation.py @@ -445,21 +445,35 @@ def _check_reason_skipped(self, agent, ability, op_facts, state, agent_executors facts = re.findall(BasePlanningService.re_variable, executor.test) if executor.command else [] if not facts or all(fact in op_facts for fact in facts): fact_dependency_fulfilled = True + untrusted_agent = agent.paw in self.untrusted_agents - if not agent.trusted: - return dict(reason='Agent untrusted', reason_id=self.Reason.UNTRUSTED.value, + if agent.platform == 'unknown': + reason_description = 'Untrusted' if untrusted_agent else 'No platform specified' + return dict(reason=reason_description, reason_id=self.Reason.PLATFORM.value, ability_id=ability.ability_id, ability_name=ability.name) elif not valid_executors: - return dict(reason='Executor not available', reason_id=self.Reason.EXECUTOR.value, + reason_description = 'Untrusted' if untrusted_agent else 'Executor not available' + return dict(reason=reason_description, reason_id=self.Reason.EXECUTOR.value, + ability_id=ability.ability_id, ability_name=ability.name) + elif not agent.privileged_to_run(ability): + reason_description = 'Untrusted' if untrusted_agent else 'Ability privilege not fulfilled' + return dict(reason=reason_description, reason_id=self.Reason.PRIVILEGE.value, ability_id=ability.ability_id, ability_name=ability.name) elif not fact_dependency_fulfilled: - return dict(reason='Fact dependency not fulfilled', reason_id=self.Reason.FACT_DEPENDENCY.value, + reason_description = 'Fact dependency not fulfilled' + return dict(reason=reason_description, reason_id=self.Reason.FACT_DEPENDENCY.value, ability_id=ability.ability_id, ability_name=ability.name) - elif not agent.privileged_to_run(ability): - return dict(reason='Ability privilege not fulfilled', reason_id=self.Reason.PRIVILEGE.value, + elif not agent.trusted: + reason_description = 'Untrusted' if untrusted_agent else 'Agent untrusted' + return dict(reason=reason_description, reason_id=self.Reason.UNTRUSTED.value, ability_id=ability.ability_id, ability_name=ability.name) elif state != 'finished': - return dict(reason='Operation not completed', reason_id=self.Reason.OP_RUNNING.value, + reason_description = 'Untrusted' if untrusted_agent else 'Operation not completed' + return dict(reason=reason_description, reason_id=self.Reason.OP_RUNNING.value, + ability_id=ability.ability_id, ability_name=ability.name) + else: + reason_description = 'Untrusted' if untrusted_agent else 'Other' + return dict(reason=reason_description, reason_id=self.Reason.OTHER.value, ability_id=ability.ability_id, ability_name=ability.name) def _get_operation_metadata_for_event_log(self): From 30dd8c016909d864b0f154c02dfeca13275ef33f Mon Sep 17 00:00:00 2001 From: bleepbop Date: Thu, 30 Jun 2022 15:40:54 -0400 Subject: [PATCH 03/65] work on checking if link was ignored --- app/objects/c_operation.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/objects/c_operation.py b/app/objects/c_operation.py index 180a1603a..85a24c3c2 100644 --- a/app/objects/c_operation.py +++ b/app/objects/c_operation.py @@ -449,6 +449,7 @@ def _check_reason_skipped(self, agent, ability, op_facts, state, agent_executors if not facts or all(fact in op_facts for fact in facts): fact_dependency_fulfilled = True untrusted_agent = agent.paw in self.untrusted_agents + # links = set([link for link in self.chain if link.paw == agent.paw and link.ability == ability]) if agent.platform == 'unknown': reason_description = 'Untrusted' if untrusted_agent else 'No platform specified' @@ -478,6 +479,12 @@ def _check_reason_skipped(self, agent, ability, op_facts, state, agent_executors reason_description = 'Untrusted' if untrusted_agent else 'Other' return dict(reason=reason_description, reason_id=self.Reason.OTHER.value, ability_id=ability.ability_id, ability_name=ability.name) + ''' + elif link_ignored: # link.can_ignore() or if link.id in ignored_links + reason_description = 'Untrusted' if untrusted_agent else 'Link was ignored' + return dict(reason=reason_description, reason_id=self.Reason.UNTRUSTED.value, + ability_id=ability.ability_id, ability_name=ability.name) + ''' def _get_operation_metadata_for_event_log(self): return dict(operation_name=self.name, From ae7c2dc3b720a76a9dbb269e9a777a4913b4abdf Mon Sep 17 00:00:00 2001 From: bleepbop Date: Wed, 13 Jul 2022 09:13:46 -0400 Subject: [PATCH 04/65] add check for ignored link --- app/objects/c_operation.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/app/objects/c_operation.py b/app/objects/c_operation.py index 85a24c3c2..caea64a5a 100644 --- a/app/objects/c_operation.py +++ b/app/objects/c_operation.py @@ -271,7 +271,7 @@ async def get_skipped_abilities_by_agent(self, data_svc): for agent in self.agents: agent_skipped = defaultdict(dict) agent_executors = agent.executors - agent_ran = set([link.ability.ability_id for link in self.chain if link.paw == agent.paw]) + agent_ran = {link.ability.ability_id: link for link in self.chain if link.paw == agent.paw} for ab in abilities_by_agent[agent.paw]['all_abilities']: skipped = self._check_reason_skipped(agent=agent, ability=ab, agent_executors=agent_executors, op_facts=[f.trait for f in await self.all_facts()], @@ -449,7 +449,7 @@ def _check_reason_skipped(self, agent, ability, op_facts, state, agent_executors if not facts or all(fact in op_facts for fact in facts): fact_dependency_fulfilled = True untrusted_agent = agent.paw in self.untrusted_agents - # links = set([link for link in self.chain if link.paw == agent.paw and link.ability == ability]) + associated_link = agent_ran[ability.ability_id] if agent.platform == 'unknown': reason_description = 'Untrusted' if untrusted_agent else 'No platform specified' @@ -467,6 +467,10 @@ def _check_reason_skipped(self, agent, ability, op_facts, state, agent_executors reason_description = 'Fact dependency not fulfilled' return dict(reason=reason_description, reason_id=self.Reason.FACT_DEPENDENCY.value, ability_id=ability.ability_id, ability_name=ability.name) + elif associated_link.id in self.ignored_links: + reason_description = 'Untrusted' if untrusted_agent else 'Link was ignored' + return dict(reason=reason_description, reason_id=self.Reason.UNTRUSTED.value, + ability_id=ability.ability_id, ability_name=ability.name) elif not agent.trusted: reason_description = 'Untrusted' if untrusted_agent else 'Agent untrusted' return dict(reason=reason_description, reason_id=self.Reason.UNTRUSTED.value, @@ -479,12 +483,6 @@ def _check_reason_skipped(self, agent, ability, op_facts, state, agent_executors reason_description = 'Untrusted' if untrusted_agent else 'Other' return dict(reason=reason_description, reason_id=self.Reason.OTHER.value, ability_id=ability.ability_id, ability_name=ability.name) - ''' - elif link_ignored: # link.can_ignore() or if link.id in ignored_links - reason_description = 'Untrusted' if untrusted_agent else 'Link was ignored' - return dict(reason=reason_description, reason_id=self.Reason.UNTRUSTED.value, - ability_id=ability.ability_id, ability_name=ability.name) - ''' def _get_operation_metadata_for_event_log(self): return dict(operation_name=self.name, From 3b1a3fbad48a15fcf56ddb7512b2e1a0e1d6c9ab Mon Sep 17 00:00:00 2001 From: bleepbop Date: Wed, 13 Jul 2022 17:07:51 -0400 Subject: [PATCH 05/65] small bug fixes and begin unit tests: --- app/objects/c_operation.py | 4 +-- tests/objects/test_operation.py | 47 +++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/app/objects/c_operation.py b/app/objects/c_operation.py index caea64a5a..e8e76909a 100644 --- a/app/objects/c_operation.py +++ b/app/objects/c_operation.py @@ -449,14 +449,14 @@ def _check_reason_skipped(self, agent, ability, op_facts, state, agent_executors if not facts or all(fact in op_facts for fact in facts): fact_dependency_fulfilled = True untrusted_agent = agent.paw in self.untrusted_agents - associated_link = agent_ran[ability.ability_id] + associated_link = agent_ran.get(ability.ability_id) if agent.platform == 'unknown': reason_description = 'Untrusted' if untrusted_agent else 'No platform specified' return dict(reason=reason_description, reason_id=self.Reason.PLATFORM.value, ability_id=ability.ability_id, ability_name=ability.name) elif not valid_executors: - reason_description = 'Untrusted' if untrusted_agent else 'Executor not available' + reason_description = 'Untrusted' if untrusted_agent else 'Executor is unavailable' return dict(reason=reason_description, reason_id=self.Reason.EXECUTOR.value, ability_id=ability.ability_id, ability_name=ability.name) elif not agent.privileged_to_run(ability): diff --git a/tests/objects/test_operation.py b/tests/objects/test_operation.py index 100a1e7b0..8080405b3 100644 --- a/tests/objects/test_operation.py +++ b/tests/objects/test_operation.py @@ -472,3 +472,50 @@ async def test_wait_for_links_completion_non_ignorable_link(self, make_test_link await op.wait_for_links_completion([test_link.id]) assert not op.ignored_links assert test_link in op.chain + + def test_check_reason_skipped_platform(self, test_agent, test_ability): + test_agent.platform = 'unknown' + op = Operation(name='test', agents=[test_agent], state='running') + reason = op._check_reason_skipped(agent=test_agent, ability=test_ability, op_facts=[], state=op.state, + agent_executors=test_agent.executors, agent_ran={}) + assert reason['reason'] == 'No platform specified' + assert reason['reason_id'] == Operation.Reason.PLATFORM.value + assert reason['ability_id'] == test_ability.ability_id + assert reason['ability_name'] == test_ability.name + + async def test_check_reason_skipped_executor(self, test_agent, test_ability): + test_agent.platform = 'darwin' + op = Operation(name='test', agents=[test_agent], state='running') + reason = op._check_reason_skipped(agent=test_agent, ability=test_ability, op_facts=[], state=op.state, + agent_executors=[], agent_ran={}) + assert reason['reason'] == 'Executor is unavailable' + assert reason['reason_id'] == Operation.Reason.EXECUTOR.value + assert reason['ability_id'] == test_ability.ability_id + assert reason['ability_name'] == test_ability.name + + async def test_check_reason_skipped_privilege(self, test_agent, test_ability, mocker, test_executor): + test_agent.platform = 'linux' + test_agent.executors = [test_executor.name] + test_ability.privilege = True + op = Operation(name='test', agents=[test_agent], state='running') + reason = op._check_reason_skipped(agent=test_agent, ability=test_ability, op_facts=[], state=op.state, + agent_executors=test_agent.executors, agent_ran={}) + assert reason['reason'] == 'Ability privilege not fulfilled' + assert reason['reason_id'] == Operation.Reason.PRIVILEGE.value + assert reason['ability_id'] == test_ability.ability_id + assert reason['ability_name'] == test_ability.name + + async def test_check_reason_skipped_fact_dependency(self): + pass + + async def test_check_reason_skipped_link_ignored(self): + pass + + async def test_check_reason_skipped_untrusted(self): + pass + + async def test_check_reason_skipped_op_running(self): + pass + + async def test_check_reason_skipped_other(self): + pass From 8084a978c9a78ed33ec18235126accd3b9f92de5 Mon Sep 17 00:00:00 2001 From: bleepbop Date: Thu, 14 Jul 2022 09:14:50 -0400 Subject: [PATCH 06/65] reduce code duplication --- app/objects/c_operation.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/app/objects/c_operation.py b/app/objects/c_operation.py index e8e76909a..b23167f63 100644 --- a/app/objects/c_operation.py +++ b/app/objects/c_operation.py @@ -450,25 +450,31 @@ def _check_reason_skipped(self, agent, ability, op_facts, state, agent_executors fact_dependency_fulfilled = True untrusted_agent = agent.paw in self.untrusted_agents associated_link = agent_ran.get(ability.ability_id) + reason_description = 'Untrusted' if agent.platform == 'unknown': - reason_description = 'Untrusted' if untrusted_agent else 'No platform specified' + if not untrusted_agent: + reason_description = 'No platform specified' return dict(reason=reason_description, reason_id=self.Reason.PLATFORM.value, ability_id=ability.ability_id, ability_name=ability.name) elif not valid_executors: - reason_description = 'Untrusted' if untrusted_agent else 'Executor is unavailable' + if not untrusted_agent: + reason_description = 'Executor is unavailable' return dict(reason=reason_description, reason_id=self.Reason.EXECUTOR.value, ability_id=ability.ability_id, ability_name=ability.name) elif not agent.privileged_to_run(ability): - reason_description = 'Untrusted' if untrusted_agent else 'Ability privilege not fulfilled' + if not untrusted_agent: + reason_description = 'Ability privilege not fulfilled' return dict(reason=reason_description, reason_id=self.Reason.PRIVILEGE.value, ability_id=ability.ability_id, ability_name=ability.name) elif not fact_dependency_fulfilled: - reason_description = 'Fact dependency not fulfilled' + if not untrusted_agent: + reason_description = 'Fact dependency not fulfilled' return dict(reason=reason_description, reason_id=self.Reason.FACT_DEPENDENCY.value, ability_id=ability.ability_id, ability_name=ability.name) elif associated_link.id in self.ignored_links: - reason_description = 'Untrusted' if untrusted_agent else 'Link was ignored' + if not untrusted_agent: + reason_description = 'Link was ignored' return dict(reason=reason_description, reason_id=self.Reason.UNTRUSTED.value, ability_id=ability.ability_id, ability_name=ability.name) elif not agent.trusted: @@ -476,11 +482,13 @@ def _check_reason_skipped(self, agent, ability, op_facts, state, agent_executors return dict(reason=reason_description, reason_id=self.Reason.UNTRUSTED.value, ability_id=ability.ability_id, ability_name=ability.name) elif state != 'finished': - reason_description = 'Untrusted' if untrusted_agent else 'Operation not completed' + if not untrusted_agent: + reason_description = 'Operation not completed' return dict(reason=reason_description, reason_id=self.Reason.OP_RUNNING.value, ability_id=ability.ability_id, ability_name=ability.name) else: - reason_description = 'Untrusted' if untrusted_agent else 'Other' + if not untrusted_agent: + reason_description = 'Other' return dict(reason=reason_description, reason_id=self.Reason.OTHER.value, ability_id=ability.ability_id, ability_name=ability.name) From d98efd300d5bb9ca5becad65d8e75a8f6ab903d8 Mon Sep 17 00:00:00 2001 From: bleepbop Date: Wed, 20 Jul 2022 13:36:47 -0400 Subject: [PATCH 07/65] convert agent_ran back into set; check disjoint sets --- app/objects/c_operation.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/objects/c_operation.py b/app/objects/c_operation.py index b23167f63..7533b2083 100644 --- a/app/objects/c_operation.py +++ b/app/objects/c_operation.py @@ -271,14 +271,14 @@ async def get_skipped_abilities_by_agent(self, data_svc): for agent in self.agents: agent_skipped = defaultdict(dict) agent_executors = agent.executors - agent_ran = {link.ability.ability_id: link for link in self.chain if link.paw == agent.paw} + agent_ran = set([link.ability.ability_id for link in self.chain if link.paw == agent.paw]) for ab in abilities_by_agent[agent.paw]['all_abilities']: skipped = self._check_reason_skipped(agent=agent, ability=ab, agent_executors=agent_executors, op_facts=[f.trait for f in await self.all_facts()], state=self.state, agent_ran=agent_ran) if skipped: if agent_skipped[skipped['ability_id']]: - if agent_skipped[skipped['ability_id']]['reason_id'] < skipped['reason_id']: + if agent_skipped[skipped['ability_id']]['reason_id'] > skipped['reason_id']: agent_skipped[skipped['ability_id']] = skipped else: agent_skipped[skipped['ability_id']] = skipped @@ -449,7 +449,8 @@ def _check_reason_skipped(self, agent, ability, op_facts, state, agent_executors if not facts or all(fact in op_facts for fact in facts): fact_dependency_fulfilled = True untrusted_agent = agent.paw in self.untrusted_agents - associated_link = agent_ran.get(ability.ability_id) + associated_links = set([link.id for link in self.chain if link.paw == agent.paw + and link.ability.ability_id == ability.ability_id]) reason_description = 'Untrusted' if agent.platform == 'unknown': @@ -472,7 +473,7 @@ def _check_reason_skipped(self, agent, ability, op_facts, state, agent_executors reason_description = 'Fact dependency not fulfilled' return dict(reason=reason_description, reason_id=self.Reason.FACT_DEPENDENCY.value, ability_id=ability.ability_id, ability_name=ability.name) - elif associated_link.id in self.ignored_links: + elif not set(associated_links).isdisjoint(self.ignored_links): if not untrusted_agent: reason_description = 'Link was ignored' return dict(reason=reason_description, reason_id=self.Reason.UNTRUSTED.value, From b44c1e98ab664ac558c7bf5a6cd54b822859ebeb Mon Sep 17 00:00:00 2001 From: bleepbop Date: Wed, 20 Jul 2022 14:20:48 -0400 Subject: [PATCH 08/65] bug fixes in returned dictionaries --- app/objects/c_operation.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/objects/c_operation.py b/app/objects/c_operation.py index 7533b2083..84e6445d6 100644 --- a/app/objects/c_operation.py +++ b/app/objects/c_operation.py @@ -476,10 +476,11 @@ def _check_reason_skipped(self, agent, ability, op_facts, state, agent_executors elif not set(associated_links).isdisjoint(self.ignored_links): if not untrusted_agent: reason_description = 'Link was ignored' - return dict(reason=reason_description, reason_id=self.Reason.UNTRUSTED.value, + return dict(reason=reason_description, reason_id=self.Reason.LINK_IGNORED.value, ability_id=ability.ability_id, ability_name=ability.name) elif not agent.trusted: - reason_description = 'Untrusted' if untrusted_agent else 'Agent untrusted' + if not untrusted_agent: + reason_description = 'Agent untrusted' return dict(reason=reason_description, reason_id=self.Reason.UNTRUSTED.value, ability_id=ability.ability_id, ability_name=ability.name) elif state != 'finished': From 8b75fdb2319d315daedc5b5f17177f6c943f75ad Mon Sep 17 00:00:00 2001 From: bleepbop Date: Wed, 20 Jul 2022 15:21:40 -0400 Subject: [PATCH 09/65] tests passing --- tests/conftest.py | 2 +- tests/objects/test_operation.py | 82 +++++++++++++++++++++++++++------ 2 files changed, 70 insertions(+), 14 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 418d02298..a12450842 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -500,7 +500,7 @@ def test_agent(event_loop): @pytest.fixture def test_executor(test_agent): - return ExecutorSchema().load(dict(timeout=60, platform=test_agent.platform, name='linux', command='ls')) + return ExecutorSchema().load(dict(timeout=60, platform=test_agent.platform, name='sh', command='ls')) @pytest.fixture diff --git a/tests/objects/test_operation.py b/tests/objects/test_operation.py index 8080405b3..a3a7bb110 100644 --- a/tests/objects/test_operation.py +++ b/tests/objects/test_operation.py @@ -483,7 +483,7 @@ def test_check_reason_skipped_platform(self, test_agent, test_ability): assert reason['ability_id'] == test_ability.ability_id assert reason['ability_name'] == test_ability.name - async def test_check_reason_skipped_executor(self, test_agent, test_ability): + async def test_check_reason_skipped_valid_executor(self, test_agent, test_ability): test_agent.platform = 'darwin' op = Operation(name='test', agents=[test_agent], state='running') reason = op._check_reason_skipped(agent=test_agent, ability=test_ability, op_facts=[], state=op.state, @@ -494,9 +494,10 @@ async def test_check_reason_skipped_executor(self, test_agent, test_ability): assert reason['ability_name'] == test_ability.name async def test_check_reason_skipped_privilege(self, test_agent, test_ability, mocker, test_executor): - test_agent.platform = 'linux' + test_agent.platform = 'windows' + test_executor.name = 'psh' test_agent.executors = [test_executor.name] - test_ability.privilege = True + test_ability.privilege = 'Elevated' op = Operation(name='test', agents=[test_agent], state='running') reason = op._check_reason_skipped(agent=test_agent, ability=test_ability, op_facts=[], state=op.state, agent_executors=test_agent.executors, agent_ran={}) @@ -505,17 +506,72 @@ async def test_check_reason_skipped_privilege(self, test_agent, test_ability, mo assert reason['ability_id'] == test_ability.ability_id assert reason['ability_name'] == test_ability.name - async def test_check_reason_skipped_fact_dependency(self): - pass + async def test_check_reason_skipped_fact_dependency(self, test_agent, test_ability, mocker, test_executor, fact): + test_agent.platform = 'windows' + test_executor.name = 'psh' + test_executor.command = 'whoami' + test_agent.executors = [test_executor.name] + op = Operation(name='test', agents=[test_agent], state='running') + with mocker.patch('app.objects.c_ability.Ability.find_executors') as mock_find_executors: + mock_find_executors.return_value = [test_executor] + with mocker.patch('re.findall') as mock_findall: + mock_findall.return_value = [fact('test.fact.attribute')] + reason = op._check_reason_skipped(agent=test_agent, ability=test_ability, op_facts=[], state=op.state, + agent_executors=test_agent.executors, agent_ran={}) + assert reason['reason'] == 'Fact dependency not fulfilled' + assert reason['reason_id'] == Operation.Reason.FACT_DEPENDENCY.value + assert reason['ability_id'] == test_ability.ability_id + assert reason['ability_name'] == test_ability.name - async def test_check_reason_skipped_link_ignored(self): - pass + async def test_check_reason_skipped_link_ignored(self, test_agent, test_ability, mocker, test_executor, + active_link): + test_agent.platform = 'windows' + test_executor.name = 'psh' + test_agent.executors = [test_executor.name] + op = Operation(name='test', agents=[test_agent], state='running') + test_link = Link.load(active_link) + op.chain = [test_link] + op.ignored_links = [test_link.id] + reason = op._check_reason_skipped(agent=test_agent, ability=test_ability, op_facts=[], state=op.state, + agent_executors=test_agent.executors, agent_ran={}) + assert reason['reason'] == 'Link was ignored' + assert reason['reason_id'] == Operation.Reason.LINK_IGNORED.value + assert reason['ability_id'] == test_ability.ability_id + assert reason['ability_name'] == test_ability.name - async def test_check_reason_skipped_untrusted(self): - pass + async def test_check_reason_skipped_untrusted(self, test_agent, test_ability, mocker, test_executor): + test_agent.platform = 'windows' + test_executor.name = 'psh' + test_agent.executors = [test_executor.name] + test_agent.trusted = False + op = Operation(name='test', agents=[test_agent], state='running') + reason = op._check_reason_skipped(agent=test_agent, ability=test_ability, op_facts=[], state=op.state, + agent_executors=test_agent.executors, agent_ran={}) + assert reason['reason'] == 'Agent untrusted' + assert reason['reason_id'] == Operation.Reason.UNTRUSTED.value + assert reason['ability_id'] == test_ability.ability_id + assert reason['ability_name'] == test_ability.name - async def test_check_reason_skipped_op_running(self): - pass + async def test_check_reason_skipped_op_running(self, test_agent, test_ability, mocker, test_executor): + test_agent.platform = 'windows' + test_executor.name = 'psh' + test_agent.executors = [test_executor.name] + op = Operation(name='test', agents=[test_agent], state='running') + reason = op._check_reason_skipped(agent=test_agent, ability=test_ability, op_facts=[], state=op.state, + agent_executors=test_agent.executors, agent_ran={}) + assert reason['reason'] == 'Operation not completed' + assert reason['reason_id'] == Operation.Reason.OP_RUNNING.value + assert reason['ability_id'] == test_ability.ability_id + assert reason['ability_name'] == test_ability.name - async def test_check_reason_skipped_other(self): - pass + async def test_check_reason_skipped_other(self, test_agent, test_ability, mocker, test_executor): + test_agent.platform = 'windows' + test_executor.name = 'psh' + test_agent.executors = [test_executor.name] + op = Operation(name='test', agents=[test_agent], state='finished') + reason = op._check_reason_skipped(agent=test_agent, ability=test_ability, op_facts=[], state=op.state, + agent_executors=test_agent.executors, agent_ran={}) + assert reason['reason'] == 'Other' + assert reason['reason_id'] == Operation.Reason.OTHER.value + assert reason['ability_id'] == test_ability.ability_id + assert reason['ability_name'] == test_ability.name From 2c8b165052d5443f6d15ed87c9efcdefa490bc89 Mon Sep 17 00:00:00 2001 From: bleepbop Date: Wed, 20 Jul 2022 15:57:56 -0400 Subject: [PATCH 10/65] refactor test_agent --- tests/objects/test_operation.py | 77 +++++++++++++++++---------------- 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/tests/objects/test_operation.py b/tests/objects/test_operation.py index a3a7bb110..8f9ba73d9 100644 --- a/tests/objects/test_operation.py +++ b/tests/objects/test_operation.py @@ -170,6 +170,16 @@ def op_without_learning_parser(ability, adversary): return op +@pytest.fixture +def custom_agent(test_agent): + def _make_agent(executors, platform='windows', trusted=True): + test_agent.platform = platform + test_agent.executors = executors + test_agent.trusted = trusted + return test_agent + return _make_agent + + @pytest.fixture def op_with_learning_and_seeded(ability, adversary, operation_agent, parse_datestring): sc = Source(id='3124', name='test', facts=[Fact(trait='domain.user.name', value='bob')]) @@ -493,84 +503,77 @@ async def test_check_reason_skipped_valid_executor(self, test_agent, test_abilit assert reason['ability_id'] == test_ability.ability_id assert reason['ability_name'] == test_ability.name - async def test_check_reason_skipped_privilege(self, test_agent, test_ability, mocker, test_executor): - test_agent.platform = 'windows' + async def test_check_reason_skipped_privilege(self, custom_agent, test_ability, mocker, test_executor): test_executor.name = 'psh' - test_agent.executors = [test_executor.name] + agent = custom_agent(executors=[test_executor.name]) test_ability.privilege = 'Elevated' - op = Operation(name='test', agents=[test_agent], state='running') - reason = op._check_reason_skipped(agent=test_agent, ability=test_ability, op_facts=[], state=op.state, - agent_executors=test_agent.executors, agent_ran={}) + op = Operation(name='test', agents=[agent], state='running') + reason = op._check_reason_skipped(agent=agent, ability=test_ability, op_facts=[], state=op.state, + agent_executors=agent.executors, agent_ran={}) assert reason['reason'] == 'Ability privilege not fulfilled' assert reason['reason_id'] == Operation.Reason.PRIVILEGE.value assert reason['ability_id'] == test_ability.ability_id assert reason['ability_name'] == test_ability.name - async def test_check_reason_skipped_fact_dependency(self, test_agent, test_ability, mocker, test_executor, fact): - test_agent.platform = 'windows' + async def test_check_reason_skipped_fact_dependency(self, custom_agent, test_ability, mocker, test_executor, fact): test_executor.name = 'psh' test_executor.command = 'whoami' - test_agent.executors = [test_executor.name] - op = Operation(name='test', agents=[test_agent], state='running') + agent = custom_agent(executors=[test_executor.name]) + op = Operation(name='test', agents=[agent], state='running') with mocker.patch('app.objects.c_ability.Ability.find_executors') as mock_find_executors: mock_find_executors.return_value = [test_executor] with mocker.patch('re.findall') as mock_findall: mock_findall.return_value = [fact('test.fact.attribute')] - reason = op._check_reason_skipped(agent=test_agent, ability=test_ability, op_facts=[], state=op.state, - agent_executors=test_agent.executors, agent_ran={}) + reason = op._check_reason_skipped(agent=agent, ability=test_ability, op_facts=[], state=op.state, + agent_executors=agent.executors, agent_ran={}) assert reason['reason'] == 'Fact dependency not fulfilled' assert reason['reason_id'] == Operation.Reason.FACT_DEPENDENCY.value assert reason['ability_id'] == test_ability.ability_id assert reason['ability_name'] == test_ability.name - async def test_check_reason_skipped_link_ignored(self, test_agent, test_ability, mocker, test_executor, + async def test_check_reason_skipped_link_ignored(self, custom_agent, test_ability, mocker, test_executor, active_link): - test_agent.platform = 'windows' test_executor.name = 'psh' - test_agent.executors = [test_executor.name] - op = Operation(name='test', agents=[test_agent], state='running') + agent = custom_agent(executors=[test_executor.name]) + op = Operation(name='test', agents=[agent], state='running') test_link = Link.load(active_link) op.chain = [test_link] op.ignored_links = [test_link.id] - reason = op._check_reason_skipped(agent=test_agent, ability=test_ability, op_facts=[], state=op.state, - agent_executors=test_agent.executors, agent_ran={}) + reason = op._check_reason_skipped(agent=agent, ability=test_ability, op_facts=[], state=op.state, + agent_executors=agent.executors, agent_ran={}) assert reason['reason'] == 'Link was ignored' assert reason['reason_id'] == Operation.Reason.LINK_IGNORED.value assert reason['ability_id'] == test_ability.ability_id assert reason['ability_name'] == test_ability.name - async def test_check_reason_skipped_untrusted(self, test_agent, test_ability, mocker, test_executor): - test_agent.platform = 'windows' + async def test_check_reason_skipped_untrusted(self, custom_agent, test_ability, mocker, test_executor): test_executor.name = 'psh' - test_agent.executors = [test_executor.name] - test_agent.trusted = False - op = Operation(name='test', agents=[test_agent], state='running') - reason = op._check_reason_skipped(agent=test_agent, ability=test_ability, op_facts=[], state=op.state, - agent_executors=test_agent.executors, agent_ran={}) + agent = custom_agent(executors=[test_executor.name], trusted=False) + op = Operation(name='test', agents=[agent], state='running') + reason = op._check_reason_skipped(agent=agent, ability=test_ability, op_facts=[], state=op.state, + agent_executors=agent.executors, agent_ran={}) assert reason['reason'] == 'Agent untrusted' assert reason['reason_id'] == Operation.Reason.UNTRUSTED.value assert reason['ability_id'] == test_ability.ability_id assert reason['ability_name'] == test_ability.name - async def test_check_reason_skipped_op_running(self, test_agent, test_ability, mocker, test_executor): - test_agent.platform = 'windows' + async def test_check_reason_skipped_op_running(self, custom_agent, test_ability, mocker, test_executor): test_executor.name = 'psh' - test_agent.executors = [test_executor.name] - op = Operation(name='test', agents=[test_agent], state='running') - reason = op._check_reason_skipped(agent=test_agent, ability=test_ability, op_facts=[], state=op.state, - agent_executors=test_agent.executors, agent_ran={}) + agent = custom_agent(executors=[test_executor.name]) + op = Operation(name='test', agents=[agent], state='running') + reason = op._check_reason_skipped(agent=agent, ability=test_ability, op_facts=[], state=op.state, + agent_executors=agent.executors, agent_ran={}) assert reason['reason'] == 'Operation not completed' assert reason['reason_id'] == Operation.Reason.OP_RUNNING.value assert reason['ability_id'] == test_ability.ability_id assert reason['ability_name'] == test_ability.name - async def test_check_reason_skipped_other(self, test_agent, test_ability, mocker, test_executor): - test_agent.platform = 'windows' + async def test_check_reason_skipped_other(self, custom_agent, test_ability, mocker, test_executor): test_executor.name = 'psh' - test_agent.executors = [test_executor.name] - op = Operation(name='test', agents=[test_agent], state='finished') - reason = op._check_reason_skipped(agent=test_agent, ability=test_ability, op_facts=[], state=op.state, - agent_executors=test_agent.executors, agent_ran={}) + agent = custom_agent(executors=[test_executor.name]) + op = Operation(name='test', agents=[agent], state='finished') + reason = op._check_reason_skipped(agent=agent, ability=test_ability, op_facts=[], state=op.state, + agent_executors=agent.executors, agent_ran={}) assert reason['reason'] == 'Other' assert reason['reason_id'] == Operation.Reason.OTHER.value assert reason['ability_id'] == test_ability.ability_id From 4e24937e3d5a68955e8ce98144db8cbb3c1924fd Mon Sep 17 00:00:00 2001 From: bleepbop Date: Wed, 20 Jul 2022 16:09:24 -0400 Subject: [PATCH 11/65] remove test_executor --- tests/objects/test_operation.py | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/tests/objects/test_operation.py b/tests/objects/test_operation.py index 8f9ba73d9..9fc90bacb 100644 --- a/tests/objects/test_operation.py +++ b/tests/objects/test_operation.py @@ -171,10 +171,11 @@ def op_without_learning_parser(ability, adversary): @pytest.fixture -def custom_agent(test_agent): - def _make_agent(executors, platform='windows', trusted=True): +def custom_agent(test_agent, test_executor): + def _make_agent(platform='windows', trusted=True, executor_name='psh'): + test_executor.name = executor_name test_agent.platform = platform - test_agent.executors = executors + test_agent.executors = [test_executor.name] test_agent.trusted = trusted return test_agent return _make_agent @@ -505,7 +506,7 @@ async def test_check_reason_skipped_valid_executor(self, test_agent, test_abilit async def test_check_reason_skipped_privilege(self, custom_agent, test_ability, mocker, test_executor): test_executor.name = 'psh' - agent = custom_agent(executors=[test_executor.name]) + agent = custom_agent() test_ability.privilege = 'Elevated' op = Operation(name='test', agents=[agent], state='running') reason = op._check_reason_skipped(agent=agent, ability=test_ability, op_facts=[], state=op.state, @@ -517,8 +518,7 @@ async def test_check_reason_skipped_privilege(self, custom_agent, test_ability, async def test_check_reason_skipped_fact_dependency(self, custom_agent, test_ability, mocker, test_executor, fact): test_executor.name = 'psh' - test_executor.command = 'whoami' - agent = custom_agent(executors=[test_executor.name]) + agent = custom_agent() op = Operation(name='test', agents=[agent], state='running') with mocker.patch('app.objects.c_ability.Ability.find_executors') as mock_find_executors: mock_find_executors.return_value = [test_executor] @@ -531,10 +531,8 @@ async def test_check_reason_skipped_fact_dependency(self, custom_agent, test_abi assert reason['ability_id'] == test_ability.ability_id assert reason['ability_name'] == test_ability.name - async def test_check_reason_skipped_link_ignored(self, custom_agent, test_ability, mocker, test_executor, - active_link): - test_executor.name = 'psh' - agent = custom_agent(executors=[test_executor.name]) + async def test_check_reason_skipped_link_ignored(self, custom_agent, test_ability, mocker, active_link): + agent = custom_agent() op = Operation(name='test', agents=[agent], state='running') test_link = Link.load(active_link) op.chain = [test_link] @@ -546,9 +544,8 @@ async def test_check_reason_skipped_link_ignored(self, custom_agent, test_abilit assert reason['ability_id'] == test_ability.ability_id assert reason['ability_name'] == test_ability.name - async def test_check_reason_skipped_untrusted(self, custom_agent, test_ability, mocker, test_executor): - test_executor.name = 'psh' - agent = custom_agent(executors=[test_executor.name], trusted=False) + async def test_check_reason_skipped_untrusted(self, custom_agent, test_ability, mocker): + agent = custom_agent(trusted=False) op = Operation(name='test', agents=[agent], state='running') reason = op._check_reason_skipped(agent=agent, ability=test_ability, op_facts=[], state=op.state, agent_executors=agent.executors, agent_ran={}) @@ -557,9 +554,8 @@ async def test_check_reason_skipped_untrusted(self, custom_agent, test_ability, assert reason['ability_id'] == test_ability.ability_id assert reason['ability_name'] == test_ability.name - async def test_check_reason_skipped_op_running(self, custom_agent, test_ability, mocker, test_executor): - test_executor.name = 'psh' - agent = custom_agent(executors=[test_executor.name]) + async def test_check_reason_skipped_op_running(self, custom_agent, test_ability, mocker): + agent = custom_agent() op = Operation(name='test', agents=[agent], state='running') reason = op._check_reason_skipped(agent=agent, ability=test_ability, op_facts=[], state=op.state, agent_executors=agent.executors, agent_ran={}) @@ -568,9 +564,8 @@ async def test_check_reason_skipped_op_running(self, custom_agent, test_ability, assert reason['ability_id'] == test_ability.ability_id assert reason['ability_name'] == test_ability.name - async def test_check_reason_skipped_other(self, custom_agent, test_ability, mocker, test_executor): - test_executor.name = 'psh' - agent = custom_agent(executors=[test_executor.name]) + async def test_check_reason_skipped_other(self, custom_agent, test_ability, mocker): + agent = custom_agent() op = Operation(name='test', agents=[agent], state='finished') reason = op._check_reason_skipped(agent=agent, ability=test_ability, op_facts=[], state=op.state, agent_executors=agent.executors, agent_ran={}) From eaffbefe5ee06a68f8361f44683b805337a89e6d Mon Sep 17 00:00:00 2001 From: bleepbop Date: Wed, 20 Jul 2022 21:45:42 -0400 Subject: [PATCH 12/65] fix reason for untrusted agent --- app/objects/c_operation.py | 2 -- tests/objects/test_operation.py | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/objects/c_operation.py b/app/objects/c_operation.py index 84e6445d6..94d562afb 100644 --- a/app/objects/c_operation.py +++ b/app/objects/c_operation.py @@ -479,8 +479,6 @@ def _check_reason_skipped(self, agent, ability, op_facts, state, agent_executors return dict(reason=reason_description, reason_id=self.Reason.LINK_IGNORED.value, ability_id=ability.ability_id, ability_name=ability.name) elif not agent.trusted: - if not untrusted_agent: - reason_description = 'Agent untrusted' return dict(reason=reason_description, reason_id=self.Reason.UNTRUSTED.value, ability_id=ability.ability_id, ability_name=ability.name) elif state != 'finished': diff --git a/tests/objects/test_operation.py b/tests/objects/test_operation.py index 9fc90bacb..c8a539acd 100644 --- a/tests/objects/test_operation.py +++ b/tests/objects/test_operation.py @@ -549,7 +549,7 @@ async def test_check_reason_skipped_untrusted(self, custom_agent, test_ability, op = Operation(name='test', agents=[agent], state='running') reason = op._check_reason_skipped(agent=agent, ability=test_ability, op_facts=[], state=op.state, agent_executors=agent.executors, agent_ran={}) - assert reason['reason'] == 'Agent untrusted' + assert reason['reason'] == 'Untrusted' assert reason['reason_id'] == Operation.Reason.UNTRUSTED.value assert reason['ability_id'] == test_ability.ability_id assert reason['ability_name'] == test_ability.name From 87a8df92cc89da5f19d80e21a13cdd41e7618d88 Mon Sep 17 00:00:00 2001 From: blee Date: Tue, 1 Nov 2022 21:17:16 -0400 Subject: [PATCH 13/65] remove checks for untrusted from every condition; check if ability supports specific agent platform --- app/objects/c_operation.py | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/app/objects/c_operation.py b/app/objects/c_operation.py index 97f1e13f9..e0a475ac6 100644 --- a/app/objects/c_operation.py +++ b/app/objects/c_operation.py @@ -451,48 +451,33 @@ def _check_reason_skipped(self, agent, ability, op_facts, state, agent_executors facts = re.findall(BasePlanningService.re_variable, executor.test) if executor.command else [] if not facts or all(fact in op_facts for fact in facts): fact_dependency_fulfilled = True - untrusted_agent = agent.paw in self.untrusted_agents associated_links = set([link.id for link in self.chain if link.paw == agent.paw and link.ability.ability_id == ability.ability_id]) - reason_description = 'Untrusted' + ability_supports_platform = ability.find_executors(agent.executors, agent.platform) - if agent.platform == 'unknown': - if not untrusted_agent: - reason_description = 'No platform specified' - return dict(reason=reason_description, reason_id=self.Reason.PLATFORM.value, + if agent.platform == 'unknown' or not ability_supports_platform: + return dict(reason='No platform specified', reason_id=self.Reason.PLATFORM.value, ability_id=ability.ability_id, ability_name=ability.name) elif not valid_executors: - if not untrusted_agent: - reason_description = 'Executor is unavailable' - return dict(reason=reason_description, reason_id=self.Reason.EXECUTOR.value, + return dict(reason='Executor is unavailable', reason_id=self.Reason.EXECUTOR.value, ability_id=ability.ability_id, ability_name=ability.name) elif not agent.privileged_to_run(ability): - if not untrusted_agent: - reason_description = 'Ability privilege not fulfilled' - return dict(reason=reason_description, reason_id=self.Reason.PRIVILEGE.value, + return dict(reason='Ability privilege not fulfilled', reason_id=self.Reason.PRIVILEGE.value, ability_id=ability.ability_id, ability_name=ability.name) elif not fact_dependency_fulfilled: - if not untrusted_agent: - reason_description = 'Fact dependency not fulfilled' - return dict(reason=reason_description, reason_id=self.Reason.FACT_DEPENDENCY.value, + return dict(reason='Fact dependency not fulfilled', reason_id=self.Reason.FACT_DEPENDENCY.value, ability_id=ability.ability_id, ability_name=ability.name) elif not set(associated_links).isdisjoint(self.ignored_links): - if not untrusted_agent: - reason_description = 'Link was ignored' - return dict(reason=reason_description, reason_id=self.Reason.LINK_IGNORED.value, + return dict(reason='Link was ignored', reason_id=self.Reason.LINK_IGNORED.value, ability_id=ability.ability_id, ability_name=ability.name) elif not agent.trusted: - return dict(reason=reason_description, reason_id=self.Reason.UNTRUSTED.value, + return dict(reason='Untrusted', reason_id=self.Reason.UNTRUSTED.value, ability_id=ability.ability_id, ability_name=ability.name) elif state != 'finished': - if not untrusted_agent: - reason_description = 'Operation not completed' - return dict(reason=reason_description, reason_id=self.Reason.OP_RUNNING.value, + return dict(reason='Operation not completed', reason_id=self.Reason.OP_RUNNING.value, ability_id=ability.ability_id, ability_name=ability.name) else: - if not untrusted_agent: - reason_description = 'Other' - return dict(reason=reason_description, reason_id=self.Reason.OTHER.value, + return dict(reason='Other', reason_id=self.Reason.OTHER.value, ability_id=ability.ability_id, ability_name=ability.name) def _get_operation_metadata_for_event_log(self): From 689acaeade42d23e5d850cdd6008f8242cdedf69 Mon Sep 17 00:00:00 2001 From: blee Date: Thu, 3 Nov 2022 14:08:50 -0400 Subject: [PATCH 14/65] remove redundant platform check --- app/objects/c_operation.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/objects/c_operation.py b/app/objects/c_operation.py index e0a475ac6..5b3684faa 100644 --- a/app/objects/c_operation.py +++ b/app/objects/c_operation.py @@ -453,9 +453,8 @@ def _check_reason_skipped(self, agent, ability, op_facts, state, agent_executors fact_dependency_fulfilled = True associated_links = set([link.id for link in self.chain if link.paw == agent.paw and link.ability.ability_id == ability.ability_id]) - ability_supports_platform = ability.find_executors(agent.executors, agent.platform) - if agent.platform == 'unknown' or not ability_supports_platform: + if agent.platform == 'unknown': return dict(reason='No platform specified', reason_id=self.Reason.PLATFORM.value, ability_id=ability.ability_id, ability_name=ability.name) elif not valid_executors: From 354fde1ba7ef87955867362ee92332c4167354c2 Mon Sep 17 00:00:00 2001 From: blee Date: Thu, 3 Nov 2022 14:17:34 -0400 Subject: [PATCH 15/65] rename test to better describe functionality --- tests/objects/test_operation.py | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/tests/objects/test_operation.py b/tests/objects/test_operation.py index 378cd318e..185b4e7bb 100644 --- a/tests/objects/test_operation.py +++ b/tests/objects/test_operation.py @@ -488,33 +488,7 @@ def test_update_untrusted_agents_with_untrusted_no_operation_agents(self, operat op.update_untrusted_agents(operation_agent) assert not op.untrusted_agents - async def test_wait_for_links_completion_ignorable_link(self, make_test_link, operation_agent): - test_agent = operation_agent - test_link = make_test_link(9876, test_agent.paw, Link().states['DISCARD']) - op = Operation(name='test', agents=[test_agent], state='running') - op.add_link(test_link) - assert not op.ignored_links - assert test_link in op.chain - await op.wait_for_links_completion([test_link.id]) - assert test_link.id in op.ignored_links - assert len(op.ignored_links) == 1 - assert test_link in op.chain - - async def test_wait_for_links_completion_non_ignorable_link(self, make_test_link, untrusted_operation_agent, mocker, - async_return): - test_agent = untrusted_operation_agent - test_link = make_test_link(9876, test_agent.paw) - op = Operation(name='test', agents=[test_agent], state='running') - op.add_link(test_link) - assert not op.ignored_links - assert test_link in op.chain - with mocker.patch('asyncio.sleep') as mock_sleep: - mock_sleep.return_value = async_return(None) - await op.wait_for_links_completion([test_link.id]) - assert not op.ignored_links - assert test_link in op.chain - - def test_check_reason_skipped_platform(self, test_agent, test_ability): + def test_check_reason_skipped_unknown_platform(self, test_agent, test_ability): test_agent.platform = 'unknown' op = Operation(name='test', agents=[test_agent], state='running') reason = op._check_reason_skipped(agent=test_agent, ability=test_ability, op_facts=[], state=op.state, From 5ff75039bebe0611a042e5208dbfcb8134022d67 Mon Sep 17 00:00:00 2001 From: blee Date: Fri, 16 Jun 2023 23:34:47 -0400 Subject: [PATCH 16/65] add in missing installations for emu --- Dockerfile | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Dockerfile b/Dockerfile index 492e2e0a5..b6defffac 100644 --- a/Dockerfile +++ b/Dockerfile @@ -62,6 +62,13 @@ fi WORKDIR /usr/src/app +#RUN if [ grep "\- emu" /usr/src/app/conf/local.yml ]; then \ +# apt-get install zlib1g; \ +# ./usr/src/app/plugins/emu/download_payloads.sh; \ +#fi +RUN apt-get -y install zlib1g unzip; +RUN ./plugins/emu/download_payloads.sh; + # Default HTTP port for web interface and agent beacons over HTTP EXPOSE 8888 From a52d3e548299246bc810065f134ea130f6430e3e Mon Sep 17 00:00:00 2001 From: blee Date: Fri, 23 Jun 2023 10:00:19 -0400 Subject: [PATCH 17/65] add pyminizip installation for Docker --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index b6defffac..ec6833acb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -67,6 +67,7 @@ WORKDIR /usr/src/app # ./usr/src/app/plugins/emu/download_payloads.sh; \ #fi RUN apt-get -y install zlib1g unzip; +RUN pip3 install pyminizip; RUN ./plugins/emu/download_payloads.sh; # Default HTTP port for web interface and agent beacons over HTTP From 34ca5ff72fb65cf263815ab8298caef23dc90e34 Mon Sep 17 00:00:00 2001 From: blee Date: Fri, 23 Jun 2023 10:50:48 -0400 Subject: [PATCH 18/65] fix if/grep statement --- Dockerfile | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index ec6833acb..6a4ad0804 100644 --- a/Dockerfile +++ b/Dockerfile @@ -62,13 +62,11 @@ fi WORKDIR /usr/src/app -#RUN if [ grep "\- emu" /usr/src/app/conf/local.yml ]; then \ -# apt-get install zlib1g; \ -# ./usr/src/app/plugins/emu/download_payloads.sh; \ -#fi -RUN apt-get -y install zlib1g unzip; -RUN pip3 install pyminizip; -RUN ./plugins/emu/download_payloads.sh; +RUN if [ $(grep -c "\- emu" conf/local.yml) ]; then \ + apt-get -y install zlib1g unzip; \ + pip3 install -r ./plugins/emu/requirements.txt; \ + ./plugins/emu/download_payloads.sh; \ +fi # Default HTTP port for web interface and agent beacons over HTTP EXPOSE 8888 From 701e982cd3ef949fff874f441ed54c8187cf5401 Mon Sep 17 00:00:00 2001 From: blee Date: Fri, 23 Jun 2023 10:57:53 -0400 Subject: [PATCH 19/65] comment dockerfile --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 6a4ad0804..7391bc1fe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -62,6 +62,7 @@ fi WORKDIR /usr/src/app +# If emu is enabled, complete necessary installation steps RUN if [ $(grep -c "\- emu" conf/local.yml) ]; then \ apt-get -y install zlib1g unzip; \ pip3 install -r ./plugins/emu/requirements.txt; \ From e563c5973a167d4875d0164a4caee3ec033bb3eb Mon Sep 17 00:00:00 2001 From: blee Date: Fri, 23 Jun 2023 14:04:41 -0400 Subject: [PATCH 20/65] don't rewrite local.yml if it exists --- Dockerfile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 7391bc1fe..3a4bcbec1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,7 +22,8 @@ RUN if [ "$WIN_BUILD" = "true" ] ; then apt-get -y install mingw-w64; fi RUN pip3 install --no-cache-dir -r requirements.txt # Set up config file and disable atomic by default -RUN grep -v "\- atomic" conf/default.yml > conf/local.yml +RUN sed -i '/\- atomic/d' conf/default.yml +RUN if [ -f "conf/local.yml" ]; then sed -i '/\- atomic/d' conf/local.yml; fi # Install golang RUN curl -L https://go.dev/dl/go1.17.6.linux-amd64.tar.gz -o go1.17.6.linux-amd64.tar.gz @@ -63,7 +64,7 @@ fi WORKDIR /usr/src/app # If emu is enabled, complete necessary installation steps -RUN if [ $(grep -c "\- emu" conf/local.yml) ]; then \ +RUN if [ $(grep -c "\- emu" conf/default.yml) ]; then \ apt-get -y install zlib1g unzip; \ pip3 install -r ./plugins/emu/requirements.txt; \ ./plugins/emu/download_payloads.sh; \ From 96e7d3573d12474d9145ef7f1186f38d0bf1565e Mon Sep 17 00:00:00 2001 From: blee Date: Fri, 7 Jul 2023 15:50:00 -0400 Subject: [PATCH 21/65] create local.yml file manually and check that for enabled plugins --- Dockerfile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3a4bcbec1..bb2f51fe9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,7 +23,9 @@ RUN pip3 install --no-cache-dir -r requirements.txt # Set up config file and disable atomic by default RUN sed -i '/\- atomic/d' conf/default.yml -RUN if [ -f "conf/local.yml" ]; then sed -i '/\- atomic/d' conf/local.yml; fi +RUN if [! -f "conf/local.yml" ]; then sed -i '/\- atomic/d' conf/local.yml; \ + else grep -v "\- atomic" conf/default.yml > conf/local.yml; \ + fi # Install golang RUN curl -L https://go.dev/dl/go1.17.6.linux-amd64.tar.gz -o go1.17.6.linux-amd64.tar.gz @@ -64,10 +66,10 @@ fi WORKDIR /usr/src/app # If emu is enabled, complete necessary installation steps -RUN if [ $(grep -c "\- emu" conf/default.yml) ]; then \ +RUN if [ $(grep -c "\- emu" conf/local.yml) ]; then \ apt-get -y install zlib1g unzip; \ pip3 install -r ./plugins/emu/requirements.txt; \ - ./plugins/emu/download_payloads.sh; \ + #./plugins/emu/download_payloads.sh; \ fi # Default HTTP port for web interface and agent beacons over HTTP From 65d5b5faf085cf94c42d2d6672464471627c06f5 Mon Sep 17 00:00:00 2001 From: blee Date: Fri, 7 Jul 2023 15:53:22 -0400 Subject: [PATCH 22/65] uncomment download_payloads script --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index bb2f51fe9..71c9cef8b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -69,7 +69,7 @@ WORKDIR /usr/src/app RUN if [ $(grep -c "\- emu" conf/local.yml) ]; then \ apt-get -y install zlib1g unzip; \ pip3 install -r ./plugins/emu/requirements.txt; \ - #./plugins/emu/download_payloads.sh; \ + ./plugins/emu/download_payloads.sh; \ fi # Default HTTP port for web interface and agent beacons over HTTP From 1f73da476ce34880efa0e81384797eaf2a45859e Mon Sep 17 00:00:00 2001 From: blee Date: Fri, 7 Jul 2023 16:18:45 -0400 Subject: [PATCH 23/65] fix RUN conditional --- Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 71c9cef8b..83f9786c7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,8 +22,7 @@ RUN if [ "$WIN_BUILD" = "true" ] ; then apt-get -y install mingw-w64; fi RUN pip3 install --no-cache-dir -r requirements.txt # Set up config file and disable atomic by default -RUN sed -i '/\- atomic/d' conf/default.yml -RUN if [! -f "conf/local.yml" ]; then sed -i '/\- atomic/d' conf/local.yml; \ +RUN if [ -f "conf/local.yml" ]; then sed -i '/\- atomic/d' conf/local.yml; \ else grep -v "\- atomic" conf/default.yml > conf/local.yml; \ fi From b4125015c2313de4c55f5a09858aeaca9b1b5a0c Mon Sep 17 00:00:00 2001 From: bleepbop Date: Sun, 9 Jul 2023 22:11:11 -0400 Subject: [PATCH 24/65] fix missing directory issues by updating workdir --- Dockerfile | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 83f9786c7..596c54c3a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -62,15 +62,17 @@ RUN if [ ! -d "/usr/src/app/plugins/atomic/data/atomic-red-team" ]; then \ /usr/src/app/plugins/atomic/data/atomic-red-team; \ fi -WORKDIR /usr/src/app +WORKDIR /usr/src/app/plugins/emu # If emu is enabled, complete necessary installation steps RUN if [ $(grep -c "\- emu" conf/local.yml) ]; then \ - apt-get -y install zlib1g unzip; \ - pip3 install -r ./plugins/emu/requirements.txt; \ - ./plugins/emu/download_payloads.sh; \ + apt-get -y install zlib1g unzip; \ + pip3 install -r ./requirements.txt; \ + ./download_payloads.sh; \ fi +WORKDIR /usr/src/app + # Default HTTP port for web interface and agent beacons over HTTP EXPOSE 8888 From fed310c059eb3e2755407c103d1ea6bf6f74703b Mon Sep 17 00:00:00 2001 From: blee Date: Tue, 11 Jul 2023 21:08:36 -0400 Subject: [PATCH 25/65] use config_generator to create missing config file --- Dockerfile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 596c54c3a..a342279af 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,8 +22,9 @@ RUN if [ "$WIN_BUILD" = "true" ] ; then apt-get -y install mingw-w64; fi RUN pip3 install --no-cache-dir -r requirements.txt # Set up config file and disable atomic by default -RUN if [ -f "conf/local.yml" ]; then sed -i '/\- atomic/d' conf/local.yml; \ - else grep -v "\- atomic" conf/default.yml > conf/local.yml; \ +RUN if [ -f "conf/local.yml" ]; then sed -i '/\- atomic/d' conf/local.yml; \ + else sed -i '/\- atomic/d' conf/default.yml; \ + python3 -c "import app; import app.utility.config_generator; app.utility.config_generator.ensure_local_config();"; \ fi # Install golang From 2e5be0f23db0c161bb594fccc377a8a50cb1f52c Mon Sep 17 00:00:00 2001 From: bleepbop Date: Tue, 11 Jul 2023 22:01:28 -0400 Subject: [PATCH 26/65] fix path to conf/local --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index a342279af..cebd79917 100644 --- a/Dockerfile +++ b/Dockerfile @@ -66,9 +66,9 @@ fi WORKDIR /usr/src/app/plugins/emu # If emu is enabled, complete necessary installation steps -RUN if [ $(grep -c "\- emu" conf/local.yml) ]; then \ +RUN if [ $(grep -c "\- emu" ../../conf/local.yml) ]; then \ apt-get -y install zlib1g unzip; \ - pip3 install -r ./requirements.txt; \ + pip3 install -r requirements.txt; \ ./download_payloads.sh; \ fi From 47f69ee5b3be5107a5b4be5062119a879b8a5dd0 Mon Sep 17 00:00:00 2001 From: bleepbop Date: Sun, 23 Jul 2023 21:08:10 -0400 Subject: [PATCH 27/65] simplify config generation + atomic removal line --- Dockerfile | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index cebd79917..8fa852e95 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,10 +22,8 @@ RUN if [ "$WIN_BUILD" = "true" ] ; then apt-get -y install mingw-w64; fi RUN pip3 install --no-cache-dir -r requirements.txt # Set up config file and disable atomic by default -RUN if [ -f "conf/local.yml" ]; then sed -i '/\- atomic/d' conf/local.yml; \ - else sed -i '/\- atomic/d' conf/default.yml; \ - python3 -c "import app; import app.utility.config_generator; app.utility.config_generator.ensure_local_config();"; \ - fi +RUN python3 -c "import app; import app.utility.config_generator; app.utility.config_generator.ensure_local_config();"; \ + sed -i '/\- atomic/d' conf/local.yml; # Install golang RUN curl -L https://go.dev/dl/go1.17.6.linux-amd64.tar.gz -o go1.17.6.linux-amd64.tar.gz From ef73897c5ca17ef32ef97ba0a42749177e973b6e Mon Sep 17 00:00:00 2001 From: Chris Lenk Date: Tue, 25 Jul 2023 23:05:32 -0400 Subject: [PATCH 28/65] Drop Python 3.7 support --- .github/workflows/quality.yml | 2 -- .github/workflows/security.yml | 4 ++-- README.md | 2 +- conf/default.yml | 4 ++-- requirements.txt | 6 ++---- sonar-project.properties | 2 +- tests/web_server/test_core_endpoints.py | 2 +- tox.ini | 5 ++--- 8 files changed, 11 insertions(+), 16 deletions(-) diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index d92481b09..b7ae0155a 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -21,8 +21,6 @@ jobs: fail-fast: false matrix: include: - - python-version: 3.7 - toxenv: py37,style,coverage-ci - python-version: 3.8 toxenv: py38,style,coverage-ci - python-version: 3.9 diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index bb239dbae..a010d3859 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -12,14 +12,14 @@ jobs: fail-fast: false matrix: include: - - python-version: 3.7 - toxenv: safety - python-version: 3.8 toxenv: safety - python-version: 3.9 toxenv: safety - python-version: 3.10.9 toxenv: safety + - python-version: 3.11 + toxenv: safety steps: - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 diff --git a/README.md b/README.md index c04268748..96b652d71 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ These plugins are ready to use but are not included by default and are not maint These requirements are for the computer running the core framework: * Any Linux or MacOS -* Python 3.7+ (with Pip3) +* Python 3.8+ (with Pip3) * Recommended hardware to run on is 8GB+ RAM and 2+ CPUs * Recommended: GoLang 1.17+ to dynamically compile GoLang-based agents. diff --git a/conf/default.yml b/conf/default.yml index 7f970ab3a..4a4ba1ce9 100644 --- a/conf/default.yml +++ b/conf/default.yml @@ -50,10 +50,10 @@ requirements: attr: version module: sys type: python_module - version: 3.7.0 + version: 3.8.0 users: blue: blue: admin red: admin: admin - red: admin \ No newline at end of file + red: admin diff --git a/requirements.txt b/requirements.txt index 06a553b03..9ea18290e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,8 +5,7 @@ aiohttp-security==0.4.0 aiohttp-apispec==2.2.3 jinja2==3.0.3 pyyaml>=5.1 -cryptography>=3.2,<37.0.0; python_version <= '3.7' -cryptography>=3.2; python_version > '3.7' +cryptography>=3.2 websockets>=10.3 Sphinx==5.1.1 docutils==0.16 # Broken bullet lists in sphinx_rtd_theme https://github.com/readthedocs/sphinx_rtd_theme/issues/1115 @@ -24,5 +23,4 @@ svglib==1.0.1 # debrief Markdown==3.3.3 # training dnspython==2.1.0 asyncssh==2.11.0 -aioftp~=0.20.0; python_version >= '3.7' -aioftp==0.16.1; python_version < '3.7' +aioftp~=0.20.0 diff --git a/sonar-project.properties b/sonar-project.properties index 48852ae9f..26d7664a0 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -11,7 +11,7 @@ sonar.sources=./app # Encoding of the source code. Default is default system encoding #sonar.sourceEncoding=UTF-8 -sonar.python.version=3.7,3.8,3.9,3.10 +sonar.python.version=3.8,3.9,3.10,3.11 sonar.python.coverage.reportPaths=coverage.xml # Make an exception to Link's constructor, since it requires a refactor to pass diff --git a/tests/web_server/test_core_endpoints.py b/tests/web_server/test_core_endpoints.py index 3bfaa297d..a1b9e6eaf 100644 --- a/tests/web_server/test_core_endpoints.py +++ b/tests/web_server/test_core_endpoints.py @@ -122,7 +122,7 @@ async def test_command_overwrite_failure(aiohttp_client, authorized_cookies): python=dict(attr='version', module='sys', type='python_module', - version='3.7.0')))) + version='3.11.0')))) assert resp.status == HTTPStatus.OK config_dict = await resp.json() diff --git a/tox.ini b/tox.ini index a69c3dd79..1a3de5584 100644 --- a/tox.ini +++ b/tox.ini @@ -6,7 +6,7 @@ [tox] skipsdist = True envlist = - py{37,38,39,310,311} + py{38,39,310,311} style coverage safety @@ -24,8 +24,7 @@ deps = pytest-aiohttp coverage commands = - py37: coverage run -p -m pytest --tb=short -Werror --asyncio-mode=auto tests - py{38,39,310,311}: coverage run -p -m pytest --tb=short --asyncio-mode=auto tests + coverage run -p -m pytest --tb=short --asyncio-mode=auto tests [testenv:style] deps = pre-commit From 8e192c5808595dd2456819d42d7e29265c3c3e68 Mon Sep 17 00:00:00 2001 From: Chris Lenk Date: Mon, 31 Jul 2023 12:54:57 -0400 Subject: [PATCH 29/65] Update stale.yml --- .github/workflows/stale.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 4e694d21d..b9e12f242 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -22,8 +22,8 @@ jobs: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-issue-label: 'no-issue-activity' stale-pr-label: 'no-pr-activity' - stale-pr-message: 'This issue is stale because it has been open 20 days with no activity. Remove stale label or comment or this will be closed in 5 days' - stale-issue-message: 'This issue is stale because it has been open 20 days with no activity. Remove stale label or comment or this will be closed in 5 days' + stale-pr-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days' + stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days' exempt-issue-labels: 'feature,keep' - days-before-stale: 20 - days-before-close: 5 + days-before-stale: 30 + days-before-close: 7 From 29585343a14e2fd87bd91221d0411426f371756c Mon Sep 17 00:00:00 2001 From: blee Date: Mon, 31 Jul 2023 21:35:12 -0400 Subject: [PATCH 30/65] update reason messages --- app/objects/c_operation.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/objects/c_operation.py b/app/objects/c_operation.py index 601b50f6d..9f1837961 100644 --- a/app/objects/c_operation.py +++ b/app/objects/c_operation.py @@ -456,10 +456,10 @@ def _check_reason_skipped(self, agent, ability, op_facts, state, agent_executors and link.ability.ability_id == ability.ability_id]) if agent.platform == 'unknown': - return dict(reason='No platform specified', reason_id=self.Reason.PLATFORM.value, + return dict(reason='Platform not available', reason_id=self.Reason.PLATFORM.value, ability_id=ability.ability_id, ability_name=ability.name) elif not valid_executors: - return dict(reason='Executor is unavailable', reason_id=self.Reason.EXECUTOR.value, + return dict(reason='Executor not available', reason_id=self.Reason.EXECUTOR.value, ability_id=ability.ability_id, ability_name=ability.name) elif not agent.privileged_to_run(ability): return dict(reason='Ability privilege not fulfilled', reason_id=self.Reason.PRIVILEGE.value, @@ -468,10 +468,10 @@ def _check_reason_skipped(self, agent, ability, op_facts, state, agent_executors return dict(reason='Fact dependency not fulfilled', reason_id=self.Reason.FACT_DEPENDENCY.value, ability_id=ability.ability_id, ability_name=ability.name) elif not set(associated_links).isdisjoint(self.ignored_links): - return dict(reason='Link was ignored', reason_id=self.Reason.LINK_IGNORED.value, + return dict(reason='Link ignored', reason_id=self.Reason.LINK_IGNORED.value, ability_id=ability.ability_id, ability_name=ability.name) elif not agent.trusted: - return dict(reason='Untrusted', reason_id=self.Reason.UNTRUSTED.value, + return dict(reason='Agent not trusted', reason_id=self.Reason.UNTRUSTED.value, ability_id=ability.ability_id, ability_name=ability.name) elif state != 'finished': return dict(reason='Operation not completed', reason_id=self.Reason.OP_RUNNING.value, From c1b9aa431b52adcff1def2ea32dd09590da5c808 Mon Sep 17 00:00:00 2001 From: Ben Lee <55265763+bleepbop@users.noreply.github.com> Date: Wed, 2 Aug 2023 11:01:19 -0400 Subject: [PATCH 31/65] catch invalid token exception, add error message for restoring encrypted server state (#2797) Co-authored-by: Chris Lenk --- app/service/file_svc.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/app/service/file_svc.py b/app/service/file_svc.py index 31ff1aba8..753fd2d71 100644 --- a/app/service/file_svc.py +++ b/app/service/file_svc.py @@ -5,10 +5,11 @@ import json import os import subprocess +import sys from aiohttp import web from multidict import CIMultiDict -from cryptography.fernet import Fernet +from cryptography.fernet import Fernet, InvalidToken from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC @@ -239,7 +240,17 @@ def _read(self, filename): with open(filename, 'rb') as f: buf = f.read() if self.encryptor and buf.startswith(bytes(FILE_ENCRYPTION_FLAG, encoding='utf-8')): - buf = self.encryptor.decrypt(buf[len(FILE_ENCRYPTION_FLAG):]) + try: + buf = self.encryptor.decrypt(buf[len(FILE_ENCRYPTION_FLAG):]) + except InvalidToken: + self.log.error('Failed to decrypt saved CALDERA state due to incorrect encryption key.\n' + ' - If attempting to restore secure backup, verify that conf/local.yml exists with ' + 'correct encryption_key value, and that the server is being run without --insecure.\n' + ' - If attempting to restore insecure backup, verify that conf/default.yml exists ' + 'with correct encryption_key value, and that the server is being run with --insecure.\n' + ' - If correct encryption_key value cannot be recovered, rerun the server with --fresh ' + 'to disregard stored server state.') + sys.exit(1) return buf def _get_encryptor(self): From e9357fe671a1551cc98f1a6cb9cd1d3a7cbe3f7e Mon Sep 17 00:00:00 2001 From: blee Date: Fri, 4 Aug 2023 15:25:08 -0400 Subject: [PATCH 32/65] update executor error message --- app/objects/c_operation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/objects/c_operation.py b/app/objects/c_operation.py index 9f1837961..ee8766a13 100644 --- a/app/objects/c_operation.py +++ b/app/objects/c_operation.py @@ -459,7 +459,7 @@ def _check_reason_skipped(self, agent, ability, op_facts, state, agent_executors return dict(reason='Platform not available', reason_id=self.Reason.PLATFORM.value, ability_id=ability.ability_id, ability_name=ability.name) elif not valid_executors: - return dict(reason='Executor not available', reason_id=self.Reason.EXECUTOR.value, + return dict(reason='Mismatched ability platform', reason_id=self.Reason.EXECUTOR.value, ability_id=ability.ability_id, ability_name=ability.name) elif not agent.privileged_to_run(ability): return dict(reason='Ability privilege not fulfilled', reason_id=self.Reason.PRIVILEGE.value, From 91cf84ad7bf956f677c9fd1ed8f0762b99560eec Mon Sep 17 00:00:00 2001 From: blee Date: Fri, 11 Aug 2023 16:46:30 -0400 Subject: [PATCH 33/65] update reason messages --- app/objects/c_operation.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/objects/c_operation.py b/app/objects/c_operation.py index ee8766a13..647a3437e 100644 --- a/app/objects/c_operation.py +++ b/app/objects/c_operation.py @@ -459,7 +459,7 @@ def _check_reason_skipped(self, agent, ability, op_facts, state, agent_executors return dict(reason='Platform not available', reason_id=self.Reason.PLATFORM.value, ability_id=ability.ability_id, ability_name=ability.name) elif not valid_executors: - return dict(reason='Mismatched ability platform', reason_id=self.Reason.EXECUTOR.value, + return dict(reason='Mismatched ability platform and executor', reason_id=self.Reason.EXECUTOR.value, ability_id=ability.ability_id, ability_name=ability.name) elif not agent.privileged_to_run(ability): return dict(reason='Ability privilege not fulfilled', reason_id=self.Reason.PRIVILEGE.value, @@ -468,8 +468,9 @@ def _check_reason_skipped(self, agent, ability, op_facts, state, agent_executors return dict(reason='Fact dependency not fulfilled', reason_id=self.Reason.FACT_DEPENDENCY.value, ability_id=ability.ability_id, ability_name=ability.name) elif not set(associated_links).isdisjoint(self.ignored_links): - return dict(reason='Link ignored', reason_id=self.Reason.LINK_IGNORED.value, - ability_id=ability.ability_id, ability_name=ability.name) + return dict(reason='Link ignored - highly visible or discarded link', + reason_id=self.Reason.LINK_IGNORED.value, ability_id=ability.ability_id, + ability_name=ability.name) elif not agent.trusted: return dict(reason='Agent not trusted', reason_id=self.Reason.UNTRUSTED.value, ability_id=ability.ability_id, ability_name=ability.name) From 9aab656fc07cbf33cdd724b99b2cdee1d3581a74 Mon Sep 17 00:00:00 2001 From: blee Date: Sun, 13 Aug 2023 21:59:37 -0400 Subject: [PATCH 34/65] update check reason skipped tests error messages --- tests/objects/test_operation.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/objects/test_operation.py b/tests/objects/test_operation.py index 0de8ae342..3a84c085f 100644 --- a/tests/objects/test_operation.py +++ b/tests/objects/test_operation.py @@ -503,7 +503,7 @@ def test_check_reason_skipped_unknown_platform(self, test_agent, test_ability): op = Operation(name='test', agents=[test_agent], state='running') reason = op._check_reason_skipped(agent=test_agent, ability=test_ability, op_facts=[], state=op.state, agent_executors=test_agent.executors, agent_ran={}) - assert reason['reason'] == 'No platform specified' + assert reason['reason'] == 'Platform not available' assert reason['reason_id'] == Operation.Reason.PLATFORM.value assert reason['ability_id'] == test_ability.ability_id assert reason['ability_name'] == test_ability.name @@ -513,7 +513,7 @@ async def test_check_reason_skipped_valid_executor(self, test_agent, test_abilit op = Operation(name='test', agents=[test_agent], state='running') reason = op._check_reason_skipped(agent=test_agent, ability=test_ability, op_facts=[], state=op.state, agent_executors=[], agent_ran={}) - assert reason['reason'] == 'Executor is unavailable' + assert reason['reason'] == 'Mismatched ability platform and executor' assert reason['reason_id'] == Operation.Reason.EXECUTOR.value assert reason['ability_id'] == test_ability.ability_id assert reason['ability_name'] == test_ability.name @@ -553,7 +553,7 @@ async def test_check_reason_skipped_link_ignored(self, custom_agent, test_abilit op.ignored_links = [test_link.id] reason = op._check_reason_skipped(agent=agent, ability=test_ability, op_facts=[], state=op.state, agent_executors=agent.executors, agent_ran={}) - assert reason['reason'] == 'Link was ignored' + assert reason['reason'] == 'Link ignored - highly visible or discarded link' assert reason['reason_id'] == Operation.Reason.LINK_IGNORED.value assert reason['ability_id'] == test_ability.ability_id assert reason['ability_name'] == test_ability.name @@ -563,7 +563,7 @@ async def test_check_reason_skipped_untrusted(self, custom_agent, test_ability, op = Operation(name='test', agents=[agent], state='running') reason = op._check_reason_skipped(agent=agent, ability=test_ability, op_facts=[], state=op.state, agent_executors=agent.executors, agent_ran={}) - assert reason['reason'] == 'Untrusted' + assert reason['reason'] == 'Agent not trusted' assert reason['reason_id'] == Operation.Reason.UNTRUSTED.value assert reason['ability_id'] == test_ability.ability_id assert reason['ability_name'] == test_ability.name From 37daa76a34e2177a062afe41aacd143ba762fbc8 Mon Sep 17 00:00:00 2001 From: JamieScottC Date: Fri, 25 Aug 2023 10:14:20 -0400 Subject: [PATCH 35/65] remove visibility slider --- templates/operations.html | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/templates/operations.html b/templates/operations.html index d005c76b8..95e6c9204 100644 --- a/templates/operations.html +++ b/templates/operations.html @@ -743,18 +743,6 @@

Operations

-
- - -