From 02f06dd616a134df6d23899fbcf672807d4b60cc Mon Sep 17 00:00:00 2001 From: novasam23 <291755769+novasam23@users.noreply.github.com> Date: Tue, 4 Aug 2026 14:09:15 +0200 Subject: [PATCH 1/6] Make filtering for active chunk of agent more robust --- src/inc/apiv2/model/AgentAPI.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/inc/apiv2/model/AgentAPI.php b/src/inc/apiv2/model/AgentAPI.php index 0e83690f1..0f8478db7 100644 --- a/src/inc/apiv2/model/AgentAPI.php +++ b/src/inc/apiv2/model/AgentAPI.php @@ -4,6 +4,7 @@ use Exception; use Hashtopolis\dba\AbstractModel; +use Hashtopolis\dba\OrderFilter; use Hashtopolis\inc\utils\AccessUtils; use Hashtopolis\inc\utils\AgentUtils; use Hashtopolis\inc\defines\DHashcatStatus; @@ -95,8 +96,10 @@ function aggregateData(AbstractModel $object, array &$includedData = [], ?array $qFs = []; $qFs[] = new QueryFilter(Chunk::AGENT_ID, $agentId, "="); $qFs[] = new QueryFilter(Chunk::STATE, DHashcatStatus::RUNNING, "="); + $qFs[] = new QueryFilter(Chunk::PROGRESS, 10000, "<"); + $oF = new OrderFilter(Chunk::SOLVE_TIME, "DESC"); - $active_chunk = Factory::getChunkFactory()->filter([Factory::FILTER => $qFs], true); + $active_chunk = Factory::getChunkFactory()->filter([Factory::FILTER => $qFs, Factory::ORDER => $oF], true); if ($active_chunk !== NULL) { $includedData["chunks"][$agentId] = [$active_chunk]; } From fbf2c73e65360b3afd52d9ea1c540e0ddeb58c3c Mon Sep 17 00:00:00 2001 From: novasam23 <291755769+novasam23@users.noreply.github.com> Date: Fri, 7 Aug 2026 09:41:17 +0200 Subject: [PATCH 2/6] Also consider chunk timeout and add a test --- ci/apiv2/test_agent.py | 23 ++++++++++++++++++++++- src/inc/apiv2/model/AgentAPI.php | 2 +- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/ci/apiv2/test_agent.py b/ci/apiv2/test_agent.py index 83e619091..59694b24d 100644 --- a/ci/apiv2/test_agent.py +++ b/ci/apiv2/test_agent.py @@ -2,8 +2,9 @@ import test_user from hashtopolis import Agent, Config, Helper from hashtopolis import HashtopolisError +from hashtopolis_agent import ProcessState -from utils import BaseTest +from utils import BaseTest, do_create_dummy_agent, do_create_agentassignent class AgentTest(BaseTest): @@ -110,3 +111,23 @@ def test_hide_ip_info(self): def test_acl(self): model_obj = self.create_test_object() self._test_acl_list(model_obj, {'permAgentRead': True}) + + def test_active_chunk(self): + dummy_agent, agent, _, task = self.create_agent_with_task().values() + dummy_agent.get_chunk() + dummy_agent.send_process(progress=50, state=ProcessState.RUNNING) + agent_resp = Agent.objects.get(pk=agent.id) + chunks = agent_resp._Model__relationships['chunks'].get('data', []) + active_chunk_id = next(iter(chunks), {}).get('id', None) + + self.assertEqual(dummy_agent.chunk['chunkId'], active_chunk_id, "Active chunk is reported incorrectly") + + dummy_agent.get_chunk() + # To simulate a stop instruction (e.g. after a hashlist has been completed by other agents), + # report completeness on current chunk, but leave state as RUNNING. + dummy_agent.send_process(progress=100, state=ProcessState.RUNNING) + helper = Helper() + result = helper.unassign_agent(agent=agent) + agent_resp = Agent.objects.get(pk=agent.id) + + self.assertNotIn('data', agent_resp._Model__relationships['chunks'], "Chunks of a completed hashlist should not be returned as active") \ No newline at end of file diff --git a/src/inc/apiv2/model/AgentAPI.php b/src/inc/apiv2/model/AgentAPI.php index 0f8478db7..374684095 100644 --- a/src/inc/apiv2/model/AgentAPI.php +++ b/src/inc/apiv2/model/AgentAPI.php @@ -97,8 +97,8 @@ function aggregateData(AbstractModel $object, array &$includedData = [], ?array $qFs[] = new QueryFilter(Chunk::AGENT_ID, $agentId, "="); $qFs[] = new QueryFilter(Chunk::STATE, DHashcatStatus::RUNNING, "="); $qFs[] = new QueryFilter(Chunk::PROGRESS, 10000, "<"); + $qFs[] = new QueryFilter(Chunk::SOLVE_TIME, time() - SConfig::getInstance()->getVal(DConfig::CHUNK_TIMEOUT), ">"); $oF = new OrderFilter(Chunk::SOLVE_TIME, "DESC"); - $active_chunk = Factory::getChunkFactory()->filter([Factory::FILTER => $qFs, Factory::ORDER => $oF], true); if ($active_chunk !== NULL) { $includedData["chunks"][$agentId] = [$active_chunk]; From de0b032d162c35efafa742ca6ac2f53fb033ffae Mon Sep 17 00:00:00 2001 From: novasam23 <291755769+novasam23@users.noreply.github.com> Date: Mon, 10 Aug 2026 15:48:12 +0200 Subject: [PATCH 3/6] Consolidate similar logic from two places into a single helper function --- src/inc/apiv2/model/AgentAPI.php | 11 ++--------- src/inc/apiv2/model/AgentAssignmentAPI.php | 8 ++------ src/inc/utils/AgentUtils.php | 21 +++++++++++++++++++++ 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/inc/apiv2/model/AgentAPI.php b/src/inc/apiv2/model/AgentAPI.php index 374684095..eea388ddd 100644 --- a/src/inc/apiv2/model/AgentAPI.php +++ b/src/inc/apiv2/model/AgentAPI.php @@ -93,17 +93,10 @@ protected function getAggregateCrackingTime(AbstractModel $object): int { */ function aggregateData(AbstractModel $object, array &$includedData = [], ?array $aggregateFieldsets = null): array { $agentId = $object->getId(); - $qFs = []; - $qFs[] = new QueryFilter(Chunk::AGENT_ID, $agentId, "="); - $qFs[] = new QueryFilter(Chunk::STATE, DHashcatStatus::RUNNING, "="); - $qFs[] = new QueryFilter(Chunk::PROGRESS, 10000, "<"); - $qFs[] = new QueryFilter(Chunk::SOLVE_TIME, time() - SConfig::getInstance()->getVal(DConfig::CHUNK_TIMEOUT), ">"); - $oF = new OrderFilter(Chunk::SOLVE_TIME, "DESC"); - $active_chunk = Factory::getChunkFactory()->filter([Factory::FILTER => $qFs, Factory::ORDER => $oF], true); - if ($active_chunk !== NULL) { + $active_chunk = AgentUtils::getActiveChunk($agentId); + if ($active_chunk !== null) { $includedData["chunks"][$agentId] = [$active_chunk]; } - return parent::aggregateData($object, $includedData, $aggregateFieldsets); } diff --git a/src/inc/apiv2/model/AgentAssignmentAPI.php b/src/inc/apiv2/model/AgentAssignmentAPI.php index b3f8e30c8..67c90eae0 100644 --- a/src/inc/apiv2/model/AgentAssignmentAPI.php +++ b/src/inc/apiv2/model/AgentAssignmentAPI.php @@ -155,12 +155,8 @@ protected function getAggregateCurrentSpeed(AbstractModel $object): int { * @throws Exception */ protected function getAggregateCurrentChunkId(AbstractModel $object): ?int { - $qF1 = new QueryFilter(Chunk::TASK_ID, $object->getTaskId(), "="); - $qF2 = new QueryFilter(Chunk::AGENT_ID, $object->getAgentId(), "="); - $qF3 = new QueryFilter(Chunk::SOLVE_TIME, time() - SConfig::getInstance()->getVal(DConfig::CHUNK_TIMEOUT), ">"); - $qF4 = new QueryFilter(Chunk::PROGRESS, 10000, "<"); - $chunk = Factory::getChunkFactory()->filter([Factory::FILTER => array_filter([$qF1, $qF2, $qF3, $qF4])], true); - return $chunk?->getId(); + $active_chunk = AgentUtils::getActiveChunk($object->getAgentId(), $object->getTaskId()); + return $active_chunk?->getId(); } /** diff --git a/src/inc/utils/AgentUtils.php b/src/inc/utils/AgentUtils.php index 8fb9b7d0d..bfd1f2fdb 100644 --- a/src/inc/utils/AgentUtils.php +++ b/src/inc/utils/AgentUtils.php @@ -27,6 +27,7 @@ use Hashtopolis\inc\apiv2\error\HttpError; use Hashtopolis\inc\defines\DAgentStatsType; use Hashtopolis\inc\defines\DConfig; +use Hashtopolis\inc\defines\DHashcatStatus; use Hashtopolis\inc\defines\DLogEntry; use Hashtopolis\inc\defines\DLogEntryIssuer; use Hashtopolis\inc\defines\DNotificationObjectType; @@ -644,4 +645,24 @@ public static function getAggregateCracked(int $agentId, ?int $taskId = null): i $results = Factory::getChunkFactory()->multicolAggregationFilter([Factory::FILTER => array_filter([$qF1, $qF2])], [$agg1]); return (int)($results[$agg1->getName()] ?? 0); } + + /** + * Get the active chunk being worked on by an agent or + * (if task ID is specified) by an agent on a specific task. + * + * @param int $agentId + * @param int|null $taskId + * @return Chunk|null + * @throws Exception + */ + public static function getActiveChunk(int $agentId, ?int $taskId = null): ?Chunk { + $qFs = []; + $qFs[] = new QueryFilter(Chunk::AGENT_ID, $agentId, "="); + $qFs[] = $taskId !== null ? new QueryFilter(Chunk::TASK_ID, $taskId, "=") : null; + $qFs[] = new QueryFilter(Chunk::STATE, DHashcatStatus::RUNNING, "="); + $qFs[] = new QueryFilter(Chunk::SOLVE_TIME, time() - SConfig::getInstance()->getVal(DConfig::CHUNK_TIMEOUT), ">"); + $qFs[] = new QueryFilter(Chunk::PROGRESS, 10000, "<"); + $oF = new OrderFilter(Chunk::SOLVE_TIME, "DESC"); + return Factory::getChunkFactory()->filter([Factory::FILTER => array_filter($qFs), Factory::ORDER => $oF], true); + } } From 656703b8b1df5228f55d9542450102be9ea8b63a Mon Sep 17 00:00:00 2001 From: novasam23 <291755769+novasam23@users.noreply.github.com> Date: Tue, 11 Aug 2026 12:16:34 +0200 Subject: [PATCH 4/6] Fix imports Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- ci/apiv2/test_agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/apiv2/test_agent.py b/ci/apiv2/test_agent.py index 59694b24d..c044b573a 100644 --- a/ci/apiv2/test_agent.py +++ b/ci/apiv2/test_agent.py @@ -4,7 +4,7 @@ from hashtopolis import HashtopolisError from hashtopolis_agent import ProcessState -from utils import BaseTest, do_create_dummy_agent, do_create_agentassignent +from utils import BaseTest class AgentTest(BaseTest): From ab689d7b1378f6bee2b941cab840f6b7ba9b7da0 Mon Sep 17 00:00:00 2001 From: novasam23 <291755769+novasam23@users.noreply.github.com> Date: Tue, 11 Aug 2026 12:17:10 +0200 Subject: [PATCH 5/6] Fix unused imports Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/inc/apiv2/model/AgentAPI.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/inc/apiv2/model/AgentAPI.php b/src/inc/apiv2/model/AgentAPI.php index eea388ddd..96e43c778 100644 --- a/src/inc/apiv2/model/AgentAPI.php +++ b/src/inc/apiv2/model/AgentAPI.php @@ -4,7 +4,6 @@ use Exception; use Hashtopolis\dba\AbstractModel; -use Hashtopolis\dba\OrderFilter; use Hashtopolis\inc\utils\AccessUtils; use Hashtopolis\inc\utils\AgentUtils; use Hashtopolis\inc\defines\DHashcatStatus; From 6c9bfc5e850b3c34ce89075fb103cb9452e73215 Mon Sep 17 00:00:00 2001 From: novasam23 <291755769+novasam23@users.noreply.github.com> Date: Tue, 11 Aug 2026 12:18:32 +0200 Subject: [PATCH 6/6] Fix unused imports --- src/inc/apiv2/model/AgentAPI.php | 2 -- src/inc/apiv2/model/AgentAssignmentAPI.php | 4 ---- 2 files changed, 6 deletions(-) diff --git a/src/inc/apiv2/model/AgentAPI.php b/src/inc/apiv2/model/AgentAPI.php index 96e43c778..7a5f6bde5 100644 --- a/src/inc/apiv2/model/AgentAPI.php +++ b/src/inc/apiv2/model/AgentAPI.php @@ -6,7 +6,6 @@ use Hashtopolis\dba\AbstractModel; use Hashtopolis\inc\utils\AccessUtils; use Hashtopolis\inc\utils\AgentUtils; -use Hashtopolis\inc\defines\DHashcatStatus; use Hashtopolis\dba\ContainFilter; use Hashtopolis\dba\ExistsFilter; use Hashtopolis\dba\Factory; @@ -18,7 +17,6 @@ use Hashtopolis\dba\models\AgentStat; use Hashtopolis\dba\models\Assignment; use Hashtopolis\dba\models\Chunk; -use Hashtopolis\dba\QueryFilter; use Hashtopolis\dba\models\Task; use Hashtopolis\dba\models\User; use Hashtopolis\inc\apiv2\common\AbstractModelAPI; diff --git a/src/inc/apiv2/model/AgentAssignmentAPI.php b/src/inc/apiv2/model/AgentAssignmentAPI.php index 67c90eae0..8b4412480 100644 --- a/src/inc/apiv2/model/AgentAssignmentAPI.php +++ b/src/inc/apiv2/model/AgentAssignmentAPI.php @@ -4,10 +4,6 @@ use Exception; use Hashtopolis\dba\AbstractModel; -use Hashtopolis\dba\models\Chunk; -use Hashtopolis\dba\QueryFilter; -use Hashtopolis\inc\defines\DConfig; -use Hashtopolis\inc\SConfig; use Hashtopolis\inc\utils\AccessUtils; use Hashtopolis\inc\utils\AgentUtils; use Hashtopolis\inc\utils\AssignmentUtils;