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
-