From 6f3a8b98f59b72fb616505ecec7ad06b3a008c66 Mon Sep 17 00:00:00 2001 From: Eric Wasson <229096365+Eric-Wasson@users.noreply.github.com> Date: Mon, 10 Nov 2025 14:44:05 +0100 Subject: [PATCH 1/2] Implemented a better handling of (partially) unsalted hashlists --- src/dba/models/Hashlist.class.php | 68 +++++++++++++++++- src/dba/models/HashlistFactory.class.php | 6 +- src/inc/apiv2/model/hashlists.routes.php | 2 +- src/inc/utils/HashlistUtils.class.php | 91 +++++++++++++++++++----- 4 files changed, 144 insertions(+), 23 deletions(-) diff --git a/src/dba/models/Hashlist.class.php b/src/dba/models/Hashlist.class.php index aa5a9940b..99a7b33e3 100644 --- a/src/dba/models/Hashlist.class.php +++ b/src/dba/models/Hashlist.class.php @@ -18,8 +18,13 @@ class Hashlist extends AbstractModel { private ?int $brainId; private ?int $brainFeatures; private ?int $isArchived; + private ?int $uploadedTotalLines; + private ?int $uploadedEmptyLines; + private ?int $uploadedValidHashes; + private ?int $uploadedValidHashesWithoutExpectedSalt; + private ?int $uploadedInvalidHashes; - function __construct(?int $hashlistId, ?string $hashlistName, ?int $format, ?int $hashTypeId, ?int $hashCount, ?string $saltSeparator, ?int $cracked, ?int $isSecret, ?int $hexSalt, ?int $isSalted, ?int $accessGroupId, ?string $notes, ?int $brainId, ?int $brainFeatures, ?int $isArchived) { + function __construct(?int $hashlistId, ?string $hashlistName, ?int $format, ?int $hashTypeId, ?int $hashCount, ?string $saltSeparator, ?int $cracked, ?int $isSecret, ?int $hexSalt, ?int $isSalted, ?int $accessGroupId, ?string $notes, ?int $brainId, ?int $brainFeatures, ?int $isArchived, ?int $uploadedTotalLines, ?int $uploadedEmptyLines, ?int $uploadedValidHashes, ?int $uploadedValidHashesWithoutExpectedSalt, ?int $uploadedInvalidHashes) { $this->hashlistId = $hashlistId; $this->hashlistName = $hashlistName; $this->format = $format; @@ -35,6 +40,11 @@ function __construct(?int $hashlistId, ?string $hashlistName, ?int $format, ?int $this->brainId = $brainId; $this->brainFeatures = $brainFeatures; $this->isArchived = $isArchived; + $this->uploadedTotalLines = $uploadedTotalLines; + $this->uploadedEmptyLines = $uploadedEmptyLines; + $this->uploadedValidHashes = $uploadedValidHashes; + $this->uploadedValidHashesWithoutExpectedSalt = $uploadedValidHashesWithoutExpectedSalt; + $this->uploadedInvalidHashes = $uploadedInvalidHashes; } function getKeyValueDict(): array { @@ -54,6 +64,11 @@ function getKeyValueDict(): array { $dict['brainId'] = $this->brainId; $dict['brainFeatures'] = $this->brainFeatures; $dict['isArchived'] = $this->isArchived; + $dict['uploadedTotalLines'] = $this->uploadedTotalLines; + $dict['uploadedEmptyLines'] = $this->uploadedEmptyLines; + $dict['uploadedValidHashes'] = $this->uploadedValidHashes; + $dict['uploadedValidHashesWithoutExpectedSalt'] = $this->uploadedValidHashesWithoutExpectedSalt; + $dict['uploadedInvalidHashes'] = $this->uploadedInvalidHashes; return $dict; } @@ -75,6 +90,11 @@ static function getFeatures(): array { $dict['brainId'] = ['read_only' => True, "type" => "bool", "subtype" => "unset", "choices" => "unset", "null" => False, "pk" => False, "protected" => False, "private" => False, "alias" => "useBrain", "public" => False]; $dict['brainFeatures'] = ['read_only' => True, "type" => "int", "subtype" => "unset", "choices" => "unset", "null" => False, "pk" => False, "protected" => False, "private" => False, "alias" => "brainFeatures", "public" => False]; $dict['isArchived'] = ['read_only' => False, "type" => "bool", "subtype" => "unset", "choices" => "unset", "null" => False, "pk" => False, "protected" => False, "private" => False, "alias" => "isArchived", "public" => False]; + $dict['uploadedTotalLines'] = ['read_only' => True, "type" => "int", "subtype" => "unset", "choices" => "unset", "null" => True, "pk" => False, "protected" => False, "private" => False, "alias" => "uploadedTotalLines", "public" => False]; + $dict['uploadedEmptyLines'] = ['read_only' => True, "type" => "int", "subtype" => "unset", "choices" => "unset", "null" => True, "pk" => False, "protected" => False, "private" => False, "alias" => "uploadedEmptyLines", "public" => False]; + $dict['uploadedValidHashes'] = ['read_only' => True, "type" => "int", "subtype" => "unset", "choices" => "unset", "null" => True, "pk" => False, "protected" => False, "private" => False, "alias" => "uploadedValidHashes", "public" => False]; + $dict['uploadedValidHashesWithoutExpectedSalt'] = ['read_only' => True, "type" => "int", "subtype" => "unset", "choices" => "unset", "null" => True, "pk" => False, "protected" => False, "private" => False, "alias" => "uploadedValidHashesWithoutExpectedSalt", "public" => False]; + $dict['uploadedInvalidHashes'] = ['read_only' => True, "type" => "int", "subtype" => "unset", "choices" => "unset", "null" => True, "pk" => False, "protected" => False, "private" => False, "alias" => "uploadedInvalidHashes", "public" => False]; return $dict; } @@ -215,6 +235,47 @@ function setIsArchived(?int $isArchived): void { $this->isArchived = $isArchived; } + function getUploadedTotalLines(): ?int { + return $this->uploadedTotalLines; + } + + function setUploadedTotalLines(?int $uploadedTotalLines): void { + $this->uploadedTotalLines = $uploadedTotalLines; + } + + function getUploadedEmptyLines(): ?int { + return $this->uploadedEmptyLines; + } + + function setUploadedEmptyLines(?int $uploadedEmptyLines): void { + $this->uploadedEmptyLines = $uploadedEmptyLines; + } + + function getUploadedValidHashes(): ?int { + return $this->uploadedValidHashes; + } + + function setUploadedValidHashes(?int $uploadedValidHashes): void { + $this->uploadedValidHashes = $uploadedValidHashes; + } + + function getUploadedValidHashesWithoutExpectedSalt(): ?int { + return $this->uploadedValidHashesWithoutExpectedSalt; + } + + function setUploadedValidHashesWithoutExpectedSalt(?int $uploadedValidHashesWithoutExpectedSalt): void { + $this->uploadedValidHashesWithoutExpectedSalt = $uploadedValidHashesWithoutExpectedSalt; + } + + function getUploadedInvalidHashes(): ?int { + return $this->uploadedInvalidHashes; + } + + function setUploadedInvalidHashes(?int $uploadedInvalidHashes): void { + $this->uploadedInvalidHashes = $uploadedInvalidHashes; + } + + const HASHLIST_ID = "hashlistId"; const HASHLIST_NAME = "hashlistName"; const FORMAT = "format"; @@ -230,6 +291,11 @@ function setIsArchived(?int $isArchived): void { const BRAIN_ID = "brainId"; const BRAIN_FEATURES = "brainFeatures"; const IS_ARCHIVED = "isArchived"; + const UPLOADED_TOTAL_LINES = "uploadedTotalLines"; + const UPLOADED_EMPTY_LINES = "uploadedEmptyLines"; + const UPLOADED_VALID_HASHES = "uploadedValidHashes"; + const UPLOADED_VALID_HASHES_WITHOUT_EXPECTED_SALT = "uploadedValidHashesWithoutExpectedSalt"; + const UPLOADED_INVALID_HASHES = "uploadedInvalidHashes"; const PERM_CREATE = "permHashlistCreate"; const PERM_READ = "permHashlistRead"; diff --git a/src/dba/models/HashlistFactory.class.php b/src/dba/models/HashlistFactory.class.php index 42ab7a250..b18b989c3 100644 --- a/src/dba/models/HashlistFactory.class.php +++ b/src/dba/models/HashlistFactory.class.php @@ -23,7 +23,7 @@ function getCacheValidTime(): int { * @return Hashlist */ function getNullObject(): Hashlist { - return new Hashlist(-1, null, null, null, null, null, null, null, null, null, null, null, null, null, null); + return new Hashlist(-1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null); } /** @@ -32,7 +32,7 @@ function getNullObject(): Hashlist { * @return Hashlist */ function createObjectFromDict($pk, $dict): Hashlist { - return new Hashlist($dict['hashlistId'], $dict['hashlistName'], $dict['format'], $dict['hashTypeId'], $dict['hashCount'], $dict['saltSeparator'], $dict['cracked'], $dict['isSecret'], $dict['hexSalt'], $dict['isSalted'], $dict['accessGroupId'], $dict['notes'], $dict['brainId'], $dict['brainFeatures'], $dict['isArchived']); + return new Hashlist($dict['hashlistId'], $dict['hashlistName'], $dict['format'], $dict['hashTypeId'], $dict['hashCount'], $dict['saltSeparator'], $dict['cracked'], $dict['isSecret'], $dict['hexSalt'], $dict['isSalted'], $dict['accessGroupId'], $dict['notes'], $dict['brainId'], $dict['brainFeatures'], $dict['isArchived'], $dict['uploadedTotalLines'], $dict['uploadedEmptyLines'], $dict['uploadedValidHashes'], $dict['uploadedValidHashesWithoutExpectedSalt'], $dict['uploadedInvalidHashes']); } /** @@ -77,4 +77,4 @@ function get($pk): ?Hashlist { function save($model): Hashlist { return Util::cast(parent::save($model), Hashlist::class); } -} \ No newline at end of file +} diff --git a/src/inc/apiv2/model/hashlists.routes.php b/src/inc/apiv2/model/hashlists.routes.php index 99eda1fe6..ebf6badb2 100644 --- a/src/inc/apiv2/model/hashlists.routes.php +++ b/src/inc/apiv2/model/hashlists.routes.php @@ -175,4 +175,4 @@ protected function getUpdateHandlers($id, $current_user): array { } } -HashlistAPI::register($app); \ No newline at end of file +HashlistAPI::register($app); diff --git a/src/inc/utils/HashlistUtils.class.php b/src/inc/utils/HashlistUtils.class.php index e2dece3c2..d1303d9d7 100644 --- a/src/inc/utils/HashlistUtils.class.php +++ b/src/inc/utils/HashlistUtils.class.php @@ -797,7 +797,7 @@ public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted, } Factory::getAgentFactory()->getDB()->beginTransaction(); - $hashlist = new Hashlist(null, $name, $format, $hashtype, 0, $separator, 0, $secret, $hexsalted, $salted, $accessGroup->getId(), '', $brainId, $brainFeatures, 0); + $hashlist = new Hashlist(null, $name, $format, $hashtype, 0, $separator, 0, $secret, $hexsalted, $salted, $accessGroup->getId(), '', $brainId, $brainFeatures, 0, 0, 0, 0, 0, 0); $hashlist = Factory::getHashlistFactory()->save($hashlist); $dataSource = ""; @@ -829,34 +829,60 @@ public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted, Factory::getAgentFactory()->getDB()->rollback(); throw new HttpError("Hashlist has too many lines!"); } + $file = fopen($tmpfile, "rb"); if (!$file) { throw new HttpError("Failed to open file!"); } + + if ($format == DHashlistFormat::PLAIN && $salted) { + // find out if the file contains a salt separator at all + rewind($file); + + $saltSeparatorFound = false; + while (($currentLine = fgets($file)) !== false) { + if (strpos($currentLine, $saltSeparator) !== false) { + $saltSeparatorFound = true; + break; + } + } + + if ($saltSeparatorFound === false) { + fclose($file); + unlink($tmpfile); + Factory::getAgentFactory()->getDB()->rollback(); + + throw new HttpError("Salted hashes separator not found at all in the hashlist! Hashlist not created."); + } + } + else { + $saltSeparator = ""; + } + Factory::getAgentFactory()->getDB()->commit(); + $added = 0; $preFound = 0; + $uploadedTotalLines = 0; + $uploadedEmptyLines = 0; + $uploadedValidHashes = 0; + $uploadedValidHashesWithoutExpectedSalt = 0; + $uploadedInvalidHashes = 0; switch ($format) { case DHashlistFormat::PLAIN: - if ($salted) { - // find out if the first line contains field separator - rewind($file); - $bufline = stream_get_line($file, 1024); - if (strpos($bufline, $saltSeparator) === false) { - throw new HttpError("Salted hashes separator not found in file!"); - } - } - else { - $saltSeparator = ""; - } rewind($file); + Factory::getAgentFactory()->getDB()->beginTransaction(); $values = array(); $bufferCount = 0; + while (!feof($file)) { $line = trim(fgets($file)); + $uploadedTotalLines++; + if (strlen($line) == 0) { + $uploadedEmptyLines++; continue; } $hash = $line; @@ -867,8 +893,12 @@ public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted, $hash = substr($line, 0, $pos); $salt = substr($line, $pos + 1); } + else { + $uploadedValidHashesWithoutExpectedSalt++; + } } if (strlen($hash) == 0) { + $uploadedEmptyLines++; continue; } //TODO: check hash length here @@ -885,6 +915,7 @@ public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted, } } } + if ($found == null) { $values[] = new Hash(null, $hashlist->getId(), $hash, $salt, "", 0, null, 0, 0); } @@ -892,7 +923,10 @@ public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted, $values[] = new Hash(null, $hashlist->getId(), $hash, $salt, $found->getPlaintext(), time(), null, 1, 0); $preFound++; } + $bufferCount++; + $uploadedValidHashes++; + if ($bufferCount >= 10000) { $result = Factory::getHashFactory()->massSave($values); $added += $result->rowCount(); @@ -902,28 +936,43 @@ public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted, $bufferCount = 0; } } + if (sizeof($values) > 0) { $result = Factory::getHashFactory()->massSave($values); $added += $result->rowCount(); } + fclose($file); unlink($tmpfile); - Factory::getHashlistFactory()->mset($hashlist, [Hashlist::HASH_COUNT => $added, Hashlist::CRACKED => $preFound]); + + if ($added === 0) { + Factory::getAgentFactory()->getDB()->rollback(); + Factory::getHashlistFactory()->delete($hashlist); + Factory::getAgentFactory()->getDB()->commit(); + throw new HttpError("No valid hashes found! Hashlist not created."); + } + + Factory::getHashlistFactory()->mset($hashlist, [Hashlist::HASH_COUNT => $added, Hashlist::CRACKED => $preFound, Hashlist::UPLOADED_TOTAL_LINES => $uploadedTotalLines, Hashlist::UPLOADED_EMPTY_LINES => $uploadedEmptyLines, Hashlist::UPLOADED_VALID_HASHES => $uploadedValidHashes, Hashlist::UPLOADED_VALID_HASHES_WITHOUT_EXPECTED_SALT => $uploadedValidHashesWithoutExpectedSalt, Hashlist::UPLOADED_VALID_HASHES => $uploadedValidHashes]); Factory::getAgentFactory()->getDB()->commit(); - Util::createLogEntry("User", $user->getId(), DLogEntry::INFO, "New Hashlist created: " . $hashlist->getHashlistName()); + Util::createLogEntry("User", $user->getId(), DLogEntry::INFO, "New Hashlist created: " . $hashlist->getHashlistName() . ". Total lines: " . $uploadedTotalLines . " Empty lines: " . $uploadedEmptyLines . " Valid hashes: " . $uploadedValidHashes . " Valid hashes without expected salt: " . $uploadedValidHashesWithoutExpectedSalt . " Invalid hashes: " . $uploadedInvalidHashes); NotificationHandler::checkNotifications(DNotificationType::NEW_HASHLIST, new DataSet(array(DPayloadKeys::HASHLIST => $hashlist))); break; case DHashlistFormat::WPA: $added = 0; $values = []; + while (!feof($file)) { + $uploadedTotalLines++; + if ($hashlist->getHashTypeId() == 2500) { // HCCAPX hashes $data = fread($file, 393); if (strlen($data) == 0) { + $uploadedInvalidHashes++; break; } if (strlen($data) != 393) { + $uploadedInvalidHashes++; UI::printError("ERROR", "Data file only contains " . strlen($data) . " bytes!"); } // get the SSID @@ -951,11 +1000,13 @@ public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted, $mac_cli = Util::bintohex($mac_cli); $hash = new HashBinary(null, $hashlist->getId(), $mac_ap . SConfig::getInstance()->getVal(DConfig::FIELD_SEPARATOR) . $mac_cli . SConfig::getInstance()->getVal(DConfig::FIELD_SEPARATOR) . Util::bintohex($network), Util::bintohex($data), null, 0, null, 0, 0); Factory::getHashBinaryFactory()->save($hash); + $uploadedValidHashes++; $added++; } else { // PMKID hashes $line = trim(fgets($file)); if (strlen($line) == 0) { + $uploadedEmptyLines++; continue; } if (strpos($line, "*") !== false) { @@ -974,14 +1025,15 @@ public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted, } $hash = new HashBinary(null, $hashlist->getId(), $identification, Util::bintohex($line . "\n"), null, 0, null, 0, 0); Factory::getHashBinaryFactory()->save($hash); + $uploadedValidHashes++; $added++; } } fclose($file); unlink($tmpfile); - Factory::getHashlistFactory()->set($hashlist, Hashlist::HASH_COUNT, $added); - Util::createLogEntry("User", $user->getId(), DLogEntry::INFO, "New Hashlist created: " . $hashlist->getHashlistName()); + Factory::getHashlistFactory()->mset($hashlist, [Hashlist::HASH_COUNT => $added, Hashlist::UPLOADED_TOTAL_LINES => $uploadedTotalLines, Hashlist::UPLOADED_EMPTY_LINES => $uploadedEmptyLines, Hashlist::UPLOADED_VALID_HASHES => $uploadedValidHashes, Hashlist::UPLOADED_VALID_HASHES_WITHOUT_EXPECTED_SALT => $uploadedValidHashesWithoutExpectedSalt, Hashlist::UPLOADED_VALID_HASHES => $uploadedValidHashes]); + Util::createLogEntry("User", $user->getId(), DLogEntry::INFO, "New Hashlist created: " . $hashlist->getHashlistName() . ". Total lines: " . $uploadedTotalLines . " Empty lines: " . $uploadedEmptyLines . " Valid hashes: " . $uploadedValidHashes . " Valid hashes without expected salt: " . $uploadedValidHashesWithoutExpectedSalt . " Invalid hashes: " . $uploadedInvalidHashes); NotificationHandler::checkNotifications(DNotificationType::NEW_HASHLIST, new DataSet(array(DPayloadKeys::HASHLIST => $hashlist))); break; @@ -990,11 +1042,14 @@ public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted, $data = fread($file, Util::filesize($tmpfile)); $hash = new HashBinary(null, $hashlist->getId(), "", Util::bintohex($data), "", 0, null, 0, 0); Factory::getHashBinaryFactory()->save($hash); + $uploadedValidHashes++; } + fclose($file); unlink($tmpfile); - Factory::getHashlistFactory()->set($hashlist, Hashlist::HASH_COUNT, 1); - Util::createLogEntry("User", $user->getId(), DLogEntry::INFO, "New Hashlist created: " . $hashlist->getHashlistName()); + + Factory::getHashlistFactory()->mset($hashlist, [Hashlist::HASH_COUNT => 1, Hashlist::UPLOADED_TOTAL_LINES => $uploadedTotalLines, Hashlist::UPLOADED_EMPTY_LINES => $uploadedEmptyLines, Hashlist::UPLOADED_VALID_HASHES => $uploadedValidHashes, Hashlist::UPLOADED_VALID_HASHES_WITHOUT_EXPECTED_SALT => $uploadedValidHashesWithoutExpectedSalt, Hashlist::UPLOADED_VALID_HASHES => $uploadedValidHashes]); + Util::createLogEntry("User", $user->getId(), DLogEntry::INFO, "New Hashlist created: " . $hashlist->getHashlistName() . ". Total lines: " . $uploadedTotalLines . " Empty lines: " . $uploadedEmptyLines . " Valid hashes: " . $uploadedValidHashes . " Valid hashes without expected salt: " . $uploadedValidHashesWithoutExpectedSalt . " Invalid hashes: " . $uploadedInvalidHashes); NotificationHandler::checkNotifications(DNotificationType::NEW_HASHLIST, new DataSet(array(DPayloadKeys::HASHLIST => $hashlist))); break; From 4c11b58041e8072d927dfa929ef12492bddc4878 Mon Sep 17 00:00:00 2001 From: Eric Wasson <229096365+Eric-Wasson@users.noreply.github.com> Date: Wed, 26 Nov 2025 17:22:53 +0100 Subject: [PATCH 2/2] Added return of creation information when creating a new hashlist --- ci/tests/integration/MaxAgentsTest.class.php | 2 +- ci/tests/integration/RuleSplitTest.class.php | 2 +- src/dba/models/Hashlist.class.php | 68 +------------------ src/dba/models/HashlistFactory.class.php | 6 +- .../apiv2/common/AbstractBaseAPI.class.php | 8 ++- .../apiv2/common/AbstractHelperAPI.class.php | 4 +- .../apiv2/common/AbstractModelAPI.class.php | 24 +++++-- src/inc/apiv2/model/hashlists.routes.php | 30 +++++--- src/inc/handlers/HashlistHandler.class.php | 2 +- src/inc/user-api/UserAPIHashlist.class.php | 2 +- src/inc/utils/HashlistUtils.class.php | 55 ++++++++------- 11 files changed, 86 insertions(+), 117 deletions(-) diff --git a/ci/tests/integration/MaxAgentsTest.class.php b/ci/tests/integration/MaxAgentsTest.class.php index 322d5e5f0..0aa70ab5d 100644 --- a/ci/tests/integration/MaxAgentsTest.class.php +++ b/ci/tests/integration/MaxAgentsTest.class.php @@ -49,7 +49,7 @@ public function run() { HashtopolisTestFramework::log(HashtopolisTestFramework::LOG_INFO, "Running " . $this->getTestName() . "..."); $this->prepare(); try { - $response = $this->addHashlist(["name" => "NotSecureList", "isSecure" => false]); + $response = $this->addHashlist(["name" => "NotSecureList", "isSecure" => false])["hashlist"]; $hashlistId = $response["hashlistId"]; $this->testTaskMaxAgents($hashlistId); diff --git a/ci/tests/integration/RuleSplitTest.class.php b/ci/tests/integration/RuleSplitTest.class.php index 170733ada..bc2af3e4a 100644 --- a/ci/tests/integration/RuleSplitTest.class.php +++ b/ci/tests/integration/RuleSplitTest.class.php @@ -59,7 +59,7 @@ private function testRuleSplit() { $file_id2 = $this->getFile('best64.rule'); # Create hashlist - $response = $this->addHashlist(["isSecure" => false]); + $response = $this->addHashlist(["isSecure" => false])["hashlist"]; $hashlistId = $response["hashlistId"]; # Create task with rule/wordlist diff --git a/src/dba/models/Hashlist.class.php b/src/dba/models/Hashlist.class.php index 99a7b33e3..aa5a9940b 100644 --- a/src/dba/models/Hashlist.class.php +++ b/src/dba/models/Hashlist.class.php @@ -18,13 +18,8 @@ class Hashlist extends AbstractModel { private ?int $brainId; private ?int $brainFeatures; private ?int $isArchived; - private ?int $uploadedTotalLines; - private ?int $uploadedEmptyLines; - private ?int $uploadedValidHashes; - private ?int $uploadedValidHashesWithoutExpectedSalt; - private ?int $uploadedInvalidHashes; - function __construct(?int $hashlistId, ?string $hashlistName, ?int $format, ?int $hashTypeId, ?int $hashCount, ?string $saltSeparator, ?int $cracked, ?int $isSecret, ?int $hexSalt, ?int $isSalted, ?int $accessGroupId, ?string $notes, ?int $brainId, ?int $brainFeatures, ?int $isArchived, ?int $uploadedTotalLines, ?int $uploadedEmptyLines, ?int $uploadedValidHashes, ?int $uploadedValidHashesWithoutExpectedSalt, ?int $uploadedInvalidHashes) { + function __construct(?int $hashlistId, ?string $hashlistName, ?int $format, ?int $hashTypeId, ?int $hashCount, ?string $saltSeparator, ?int $cracked, ?int $isSecret, ?int $hexSalt, ?int $isSalted, ?int $accessGroupId, ?string $notes, ?int $brainId, ?int $brainFeatures, ?int $isArchived) { $this->hashlistId = $hashlistId; $this->hashlistName = $hashlistName; $this->format = $format; @@ -40,11 +35,6 @@ function __construct(?int $hashlistId, ?string $hashlistName, ?int $format, ?int $this->brainId = $brainId; $this->brainFeatures = $brainFeatures; $this->isArchived = $isArchived; - $this->uploadedTotalLines = $uploadedTotalLines; - $this->uploadedEmptyLines = $uploadedEmptyLines; - $this->uploadedValidHashes = $uploadedValidHashes; - $this->uploadedValidHashesWithoutExpectedSalt = $uploadedValidHashesWithoutExpectedSalt; - $this->uploadedInvalidHashes = $uploadedInvalidHashes; } function getKeyValueDict(): array { @@ -64,11 +54,6 @@ function getKeyValueDict(): array { $dict['brainId'] = $this->brainId; $dict['brainFeatures'] = $this->brainFeatures; $dict['isArchived'] = $this->isArchived; - $dict['uploadedTotalLines'] = $this->uploadedTotalLines; - $dict['uploadedEmptyLines'] = $this->uploadedEmptyLines; - $dict['uploadedValidHashes'] = $this->uploadedValidHashes; - $dict['uploadedValidHashesWithoutExpectedSalt'] = $this->uploadedValidHashesWithoutExpectedSalt; - $dict['uploadedInvalidHashes'] = $this->uploadedInvalidHashes; return $dict; } @@ -90,11 +75,6 @@ static function getFeatures(): array { $dict['brainId'] = ['read_only' => True, "type" => "bool", "subtype" => "unset", "choices" => "unset", "null" => False, "pk" => False, "protected" => False, "private" => False, "alias" => "useBrain", "public" => False]; $dict['brainFeatures'] = ['read_only' => True, "type" => "int", "subtype" => "unset", "choices" => "unset", "null" => False, "pk" => False, "protected" => False, "private" => False, "alias" => "brainFeatures", "public" => False]; $dict['isArchived'] = ['read_only' => False, "type" => "bool", "subtype" => "unset", "choices" => "unset", "null" => False, "pk" => False, "protected" => False, "private" => False, "alias" => "isArchived", "public" => False]; - $dict['uploadedTotalLines'] = ['read_only' => True, "type" => "int", "subtype" => "unset", "choices" => "unset", "null" => True, "pk" => False, "protected" => False, "private" => False, "alias" => "uploadedTotalLines", "public" => False]; - $dict['uploadedEmptyLines'] = ['read_only' => True, "type" => "int", "subtype" => "unset", "choices" => "unset", "null" => True, "pk" => False, "protected" => False, "private" => False, "alias" => "uploadedEmptyLines", "public" => False]; - $dict['uploadedValidHashes'] = ['read_only' => True, "type" => "int", "subtype" => "unset", "choices" => "unset", "null" => True, "pk" => False, "protected" => False, "private" => False, "alias" => "uploadedValidHashes", "public" => False]; - $dict['uploadedValidHashesWithoutExpectedSalt'] = ['read_only' => True, "type" => "int", "subtype" => "unset", "choices" => "unset", "null" => True, "pk" => False, "protected" => False, "private" => False, "alias" => "uploadedValidHashesWithoutExpectedSalt", "public" => False]; - $dict['uploadedInvalidHashes'] = ['read_only' => True, "type" => "int", "subtype" => "unset", "choices" => "unset", "null" => True, "pk" => False, "protected" => False, "private" => False, "alias" => "uploadedInvalidHashes", "public" => False]; return $dict; } @@ -235,47 +215,6 @@ function setIsArchived(?int $isArchived): void { $this->isArchived = $isArchived; } - function getUploadedTotalLines(): ?int { - return $this->uploadedTotalLines; - } - - function setUploadedTotalLines(?int $uploadedTotalLines): void { - $this->uploadedTotalLines = $uploadedTotalLines; - } - - function getUploadedEmptyLines(): ?int { - return $this->uploadedEmptyLines; - } - - function setUploadedEmptyLines(?int $uploadedEmptyLines): void { - $this->uploadedEmptyLines = $uploadedEmptyLines; - } - - function getUploadedValidHashes(): ?int { - return $this->uploadedValidHashes; - } - - function setUploadedValidHashes(?int $uploadedValidHashes): void { - $this->uploadedValidHashes = $uploadedValidHashes; - } - - function getUploadedValidHashesWithoutExpectedSalt(): ?int { - return $this->uploadedValidHashesWithoutExpectedSalt; - } - - function setUploadedValidHashesWithoutExpectedSalt(?int $uploadedValidHashesWithoutExpectedSalt): void { - $this->uploadedValidHashesWithoutExpectedSalt = $uploadedValidHashesWithoutExpectedSalt; - } - - function getUploadedInvalidHashes(): ?int { - return $this->uploadedInvalidHashes; - } - - function setUploadedInvalidHashes(?int $uploadedInvalidHashes): void { - $this->uploadedInvalidHashes = $uploadedInvalidHashes; - } - - const HASHLIST_ID = "hashlistId"; const HASHLIST_NAME = "hashlistName"; const FORMAT = "format"; @@ -291,11 +230,6 @@ function setUploadedInvalidHashes(?int $uploadedInvalidHashes): void { const BRAIN_ID = "brainId"; const BRAIN_FEATURES = "brainFeatures"; const IS_ARCHIVED = "isArchived"; - const UPLOADED_TOTAL_LINES = "uploadedTotalLines"; - const UPLOADED_EMPTY_LINES = "uploadedEmptyLines"; - const UPLOADED_VALID_HASHES = "uploadedValidHashes"; - const UPLOADED_VALID_HASHES_WITHOUT_EXPECTED_SALT = "uploadedValidHashesWithoutExpectedSalt"; - const UPLOADED_INVALID_HASHES = "uploadedInvalidHashes"; const PERM_CREATE = "permHashlistCreate"; const PERM_READ = "permHashlistRead"; diff --git a/src/dba/models/HashlistFactory.class.php b/src/dba/models/HashlistFactory.class.php index b18b989c3..42ab7a250 100644 --- a/src/dba/models/HashlistFactory.class.php +++ b/src/dba/models/HashlistFactory.class.php @@ -23,7 +23,7 @@ function getCacheValidTime(): int { * @return Hashlist */ function getNullObject(): Hashlist { - return new Hashlist(-1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null); + return new Hashlist(-1, null, null, null, null, null, null, null, null, null, null, null, null, null, null); } /** @@ -32,7 +32,7 @@ function getNullObject(): Hashlist { * @return Hashlist */ function createObjectFromDict($pk, $dict): Hashlist { - return new Hashlist($dict['hashlistId'], $dict['hashlistName'], $dict['format'], $dict['hashTypeId'], $dict['hashCount'], $dict['saltSeparator'], $dict['cracked'], $dict['isSecret'], $dict['hexSalt'], $dict['isSalted'], $dict['accessGroupId'], $dict['notes'], $dict['brainId'], $dict['brainFeatures'], $dict['isArchived'], $dict['uploadedTotalLines'], $dict['uploadedEmptyLines'], $dict['uploadedValidHashes'], $dict['uploadedValidHashesWithoutExpectedSalt'], $dict['uploadedInvalidHashes']); + return new Hashlist($dict['hashlistId'], $dict['hashlistName'], $dict['format'], $dict['hashTypeId'], $dict['hashCount'], $dict['saltSeparator'], $dict['cracked'], $dict['isSecret'], $dict['hexSalt'], $dict['isSalted'], $dict['accessGroupId'], $dict['notes'], $dict['brainId'], $dict['brainFeatures'], $dict['isArchived']); } /** @@ -77,4 +77,4 @@ function get($pk): ?Hashlist { function save($model): Hashlist { return Util::cast(parent::save($model), Hashlist::class); } -} +} \ No newline at end of file diff --git a/src/inc/apiv2/common/AbstractBaseAPI.class.php b/src/inc/apiv2/common/AbstractBaseAPI.class.php index 7261574e7..ab2f04744 100644 --- a/src/inc/apiv2/common/AbstractBaseAPI.class.php +++ b/src/inc/apiv2/common/AbstractBaseAPI.class.php @@ -1409,7 +1409,7 @@ static function createJsonResponse(array $data = [], array $links = [], array $i /** * Get single Resource */ - protected static function getOneResource(object $apiClass, object $object, Request $request, Response $response, int $statusCode = 200): Response { + protected static function getOneResource(object $apiClass, object $object, Request $request, Response $response, int $statusCode = 200, array $creationInformation = null): Response { $apiClass->preCommon($request); $validExpandables = $apiClass->getExpandables(); @@ -1464,8 +1464,12 @@ protected static function getOneResource(object $apiClass, object $object, Reque $metaData = []; if ($apiClass->permissionErrors !== null) { - $metadata["Include errors"] = $apiClass->permissionErrors; + $metaData["Include errors"] = $apiClass->permissionErrors; } + if(is_array($creationInformation)) { + $metaData["creationInformation"] = $creationInformation; + } + // Generate JSON:API GET output $ret = self::createJsonResponse($dataResources[0], $links, $includedResources, $metaData); diff --git a/src/inc/apiv2/common/AbstractHelperAPI.class.php b/src/inc/apiv2/common/AbstractHelperAPI.class.php index 2ea5d8692..bf2704f4c 100644 --- a/src/inc/apiv2/common/AbstractHelperAPI.class.php +++ b/src/inc/apiv2/common/AbstractHelperAPI.class.php @@ -12,8 +12,8 @@ abstract class AbstractHelperAPI extends AbstractBaseAPI { abstract public function actionPost(array $data): object|array|null; /** - * Function in order to create swagger documentation. SHould return either a map of strings that - * describes the output ex: ["assign" => "succes"] or if the endpoint returns an object it should return + * Function in order to create swagger documentation. Should return either a map of strings that + * describes the output ex: ["assign" => "success"] or if the endpoint returns an object it should return * the string representation of that object ex: File. */ abstract public static function getResponse(): array|string|null; diff --git a/src/inc/apiv2/common/AbstractModelAPI.class.php b/src/inc/apiv2/common/AbstractModelAPI.class.php index 0a6890b76..f91315e23 100644 --- a/src/inc/apiv2/common/AbstractModelAPI.class.php +++ b/src/inc/apiv2/common/AbstractModelAPI.class.php @@ -20,9 +20,13 @@ abstract class AbstractModelAPI extends AbstractBaseAPI { abstract static public function getDBAClass(); abstract protected function createObject(array $data): int; - + abstract protected function deleteObject(object $object): void; + protected function createObjectAndGetResult(array $data): array { + return []; + } + public static function getToOneRelationships(): array { return []; } @@ -1179,11 +1183,21 @@ public function post(Request $request, Response $response, array $args): Respons // Remove key aliases and sanitize to 'db values and request creation $mappedData = $this->unaliasData($attributes, $allFeatures); - $pk = $this->createObject($mappedData); - // Request object again, since post-modified entries are not reflected into object. - $object = $this->getFactory()->get($pk); - return self::getOneResource($this, $object, $request, $response, 201); + $object = null; + + if (isset($data["getCreationInformation"])) { + $creationResult = $this->createObjectAndGetResult($mappedData); + // Request object again, since post-modified entries are not reflected into object. + $object = $this->getFactory()->get($creationResult["pk"]); + return self::getOneResource($this, $object, $request, $response, 201, $creationResult["creationInformation"]); + } + else { + $pk = $this->createObject($mappedData); + // Request object again, since post-modified entries are not reflected into object. + $object = $this->getFactory()->get($pk); + return self::getOneResource($this, $object, $request, $response, 201); + } } diff --git a/src/inc/apiv2/model/hashlists.routes.php b/src/inc/apiv2/model/hashlists.routes.php index ebf6badb2..634e98d73 100644 --- a/src/inc/apiv2/model/hashlists.routes.php +++ b/src/inc/apiv2/model/hashlists.routes.php @@ -102,9 +102,19 @@ public function getFormFields(): array { /** * @throws HttpErrorException + * @throws HttpError * @throws HTException */ protected function createObject(array $data): int { + return $this->createObjectAndGetResult($data)["pk"]; + } + + /** + * @throws HttpErrorException + * @throws HttpError + * @throws HTException + */ + protected function createObjectAndGetResult(array $data): array { // Cast to createHashlist compatible upload format $dummyPost = []; switch ($data["sourceType"]) { @@ -122,15 +132,16 @@ protected function createObject(array $data): int { throw new HttpErrorException("sourceType value '" . $data["sourceType"] . "' is not supported (choices paste, import, url"); } - // TODO: validate input is valid base64 encoded if ($data["sourceType"] == "paste") { if (strlen($data["sourceData"]) == 0) { - // TODO: Should be 400 instead - throw new HttpErrorException("sourceType=paste, requires sourceData to be non-empty"); + throw new HttpError("sourceType=paste, requires sourceData to be non-empty"); + } + else if ($dummyPost["hashfield"] === false) { + throw new HttpError("sourceData not valid base64 encoding"); } } - $hashlist = HashlistUtils::createHashlist( + $hashlistData = HashlistUtils::createHashlist( $data[Hashlist::HASHLIST_NAME], $data[Hashlist::IS_SALTED], $data[Hashlist::IS_SECRET], @@ -150,11 +161,14 @@ protected function createObject(array $data): int { // Modify fields not set on hashlist creation if (array_key_exists("notes", $data)) { - HashlistUtils::editNotes($hashlist->getId(), $data["notes"], $this->getCurrentUser()); - }; - HashlistUtils::setArchived($hashlist->getId(), $data[UQueryHashlist::HASHLIST_IS_ARCHIVED], $this->getCurrentUser()); + HashlistUtils::editNotes($hashlistData["hashlist"]->getId(), $data["notes"], $this->getCurrentUser()); + } + HashlistUtils::setArchived($hashlistData["hashlist"]->getId(), $data[UQueryHashlist::HASHLIST_IS_ARCHIVED], $this->getCurrentUser()); - return $hashlist->getId(); + $creationResult["pk"] = $hashlistData["hashlist"]->getId(); + $creationResult["creationInformation"] = $hashlistData["statistics"]; + + return $creationResult; } /** diff --git a/src/inc/handlers/HashlistHandler.class.php b/src/inc/handlers/HashlistHandler.class.php index 1e2672282..ce6c3adb0 100644 --- a/src/inc/handlers/HashlistHandler.class.php +++ b/src/inc/handlers/HashlistHandler.class.php @@ -92,7 +92,7 @@ public function handle($action) { AccessControl::getInstance()->getUser(), (isset($_POST["useBrain"]) && intval($_POST["useBrain"]) == 1) ? 1 : 0, (isset($_POST['brain-features'])) ? intval($_POST['brain-features']) : 0 - ); + )["hashlist"]; header("Location: hashlists.php?id=" . $hashlist->getId()); die(); case DHashlistAction::CREATE_SUPERHASHLIST: diff --git a/src/inc/user-api/UserAPIHashlist.class.php b/src/inc/user-api/UserAPIHashlist.class.php index 472298bae..9679c8b4c 100644 --- a/src/inc/user-api/UserAPIHashlist.class.php +++ b/src/inc/user-api/UserAPIHashlist.class.php @@ -273,7 +273,7 @@ private function createHashlist($QUERY) { $this->user, $QUERY[UQueryHashlist::HASHLIST_USE_BRAIN], $QUERY[UQueryHashlist::HASHLIST_BRAIN_FEATURES] - ); + )["hashlist"]; $this->sendResponse(array( UResponseHashlist::SECTION => $QUERY[UQuery::SECTION], UResponseHashlist::REQUEST => $QUERY[UQuery::REQUEST], diff --git a/src/inc/utils/HashlistUtils.class.php b/src/inc/utils/HashlistUtils.class.php index d1303d9d7..0249478ae 100644 --- a/src/inc/utils/HashlistUtils.class.php +++ b/src/inc/utils/HashlistUtils.class.php @@ -761,7 +761,8 @@ public static function export($hashlistId, $user) { * @param User $user * @param int $brainId * @param int $brainFeatures - * @return Hashlist + * @param boolean $writeResultsToNotes + * @return array * @throws HTException */ public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted, $separator, $format, $hashtype, $saltSeparator, $accessGroupId, $source, $post, $files, $user, $brainId, $brainFeatures) { @@ -797,7 +798,7 @@ public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted, } Factory::getAgentFactory()->getDB()->beginTransaction(); - $hashlist = new Hashlist(null, $name, $format, $hashtype, 0, $separator, 0, $secret, $hexsalted, $salted, $accessGroup->getId(), '', $brainId, $brainFeatures, 0, 0, 0, 0, 0, 0); + $hashlist = new Hashlist(null, $name, $format, $hashtype, 0, $separator, 0, $secret, $hexsalted, $salted, $accessGroup->getId(), '', $brainId, $brainFeatures, 0); $hashlist = Factory::getHashlistFactory()->save($hashlist); $dataSource = ""; @@ -863,11 +864,12 @@ public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted, $added = 0; $preFound = 0; - $uploadedTotalLines = 0; - $uploadedEmptyLines = 0; - $uploadedValidHashes = 0; - $uploadedValidHashesWithoutExpectedSalt = 0; - $uploadedInvalidHashes = 0; + $hashlistStatistics = []; + $hashlistStatistics["uploadedTotalLines"] = 0; + $hashlistStatistics["uploadedEmptyLines"] = 0; + $hashlistStatistics["uploadedValidHashes"] = 0; + $hashlistStatistics["uploadedValidHashesWithoutExpectedSalt"] = 0; + $hashlistStatistics["uploadedInvalidHashes"] = 0; switch ($format) { case DHashlistFormat::PLAIN: @@ -879,10 +881,10 @@ public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted, while (!feof($file)) { $line = trim(fgets($file)); - $uploadedTotalLines++; + $hashlistStatistics["uploadedTotalLines"]++; if (strlen($line) == 0) { - $uploadedEmptyLines++; + $hashlistStatistics["uploadedEmptyLines"]++; continue; } $hash = $line; @@ -894,11 +896,11 @@ public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted, $salt = substr($line, $pos + 1); } else { - $uploadedValidHashesWithoutExpectedSalt++; + $hashlistStatistics["uploadedValidHashesWithoutExpectedSalt"]++; } } if (strlen($hash) == 0) { - $uploadedEmptyLines++; + $hashlistStatistics["uploadedEmptyLines"]++; continue; } //TODO: check hash length here @@ -925,7 +927,7 @@ public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted, } $bufferCount++; - $uploadedValidHashes++; + $hashlistStatistics["uploadedValidHashes"]++; if ($bufferCount >= 10000) { $result = Factory::getHashFactory()->massSave($values); @@ -952,9 +954,9 @@ public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted, throw new HttpError("No valid hashes found! Hashlist not created."); } - Factory::getHashlistFactory()->mset($hashlist, [Hashlist::HASH_COUNT => $added, Hashlist::CRACKED => $preFound, Hashlist::UPLOADED_TOTAL_LINES => $uploadedTotalLines, Hashlist::UPLOADED_EMPTY_LINES => $uploadedEmptyLines, Hashlist::UPLOADED_VALID_HASHES => $uploadedValidHashes, Hashlist::UPLOADED_VALID_HASHES_WITHOUT_EXPECTED_SALT => $uploadedValidHashesWithoutExpectedSalt, Hashlist::UPLOADED_VALID_HASHES => $uploadedValidHashes]); + Factory::getHashlistFactory()->mset($hashlist, [Hashlist::HASH_COUNT => $added, Hashlist::CRACKED => $preFound]); Factory::getAgentFactory()->getDB()->commit(); - Util::createLogEntry("User", $user->getId(), DLogEntry::INFO, "New Hashlist created: " . $hashlist->getHashlistName() . ". Total lines: " . $uploadedTotalLines . " Empty lines: " . $uploadedEmptyLines . " Valid hashes: " . $uploadedValidHashes . " Valid hashes without expected salt: " . $uploadedValidHashesWithoutExpectedSalt . " Invalid hashes: " . $uploadedInvalidHashes); + Util::createLogEntry("User", $user->getId(), DLogEntry::INFO, "New Hashlist created: " . $hashlist->getHashlistName() . ". Total lines: " . $hashlistStatistics["uploadedTotalLines"] . " Empty lines: " . $hashlistStatistics["uploadedEmptyLines"] . " Valid hashes: " . $hashlistStatistics["uploadedValidHashes"] . " Valid hashes without expected salt: " . $hashlistStatistics["uploadedValidHashesWithoutExpectedSalt"] . " Invalid hashes: " . $hashlistStatistics["uploadedInvalidHashes"]); NotificationHandler::checkNotifications(DNotificationType::NEW_HASHLIST, new DataSet(array(DPayloadKeys::HASHLIST => $hashlist))); break; @@ -963,16 +965,16 @@ public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted, $values = []; while (!feof($file)) { - $uploadedTotalLines++; + $hashlistStatistics["uploadedTotalLines"]++; if ($hashlist->getHashTypeId() == 2500) { // HCCAPX hashes $data = fread($file, 393); if (strlen($data) == 0) { - $uploadedInvalidHashes++; + $hashlistStatistics["uploadedInvalidHashes"]++; break; } if (strlen($data) != 393) { - $uploadedInvalidHashes++; + $hashlistStatistics["uploadedInvalidHashes"]++; UI::printError("ERROR", "Data file only contains " . strlen($data) . " bytes!"); } // get the SSID @@ -1000,13 +1002,13 @@ public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted, $mac_cli = Util::bintohex($mac_cli); $hash = new HashBinary(null, $hashlist->getId(), $mac_ap . SConfig::getInstance()->getVal(DConfig::FIELD_SEPARATOR) . $mac_cli . SConfig::getInstance()->getVal(DConfig::FIELD_SEPARATOR) . Util::bintohex($network), Util::bintohex($data), null, 0, null, 0, 0); Factory::getHashBinaryFactory()->save($hash); - $uploadedValidHashes++; + $hashlistStatistics["uploadedValidHashes"]++; $added++; } else { // PMKID hashes $line = trim(fgets($file)); if (strlen($line) == 0) { - $uploadedEmptyLines++; + $hashlistStatistics["uploadedEmptyLines"]++; continue; } if (strpos($line, "*") !== false) { @@ -1025,15 +1027,15 @@ public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted, } $hash = new HashBinary(null, $hashlist->getId(), $identification, Util::bintohex($line . "\n"), null, 0, null, 0, 0); Factory::getHashBinaryFactory()->save($hash); - $uploadedValidHashes++; + $hashlistStatistics["uploadedValidHashes"]++; $added++; } } fclose($file); unlink($tmpfile); - Factory::getHashlistFactory()->mset($hashlist, [Hashlist::HASH_COUNT => $added, Hashlist::UPLOADED_TOTAL_LINES => $uploadedTotalLines, Hashlist::UPLOADED_EMPTY_LINES => $uploadedEmptyLines, Hashlist::UPLOADED_VALID_HASHES => $uploadedValidHashes, Hashlist::UPLOADED_VALID_HASHES_WITHOUT_EXPECTED_SALT => $uploadedValidHashesWithoutExpectedSalt, Hashlist::UPLOADED_VALID_HASHES => $uploadedValidHashes]); - Util::createLogEntry("User", $user->getId(), DLogEntry::INFO, "New Hashlist created: " . $hashlist->getHashlistName() . ". Total lines: " . $uploadedTotalLines . " Empty lines: " . $uploadedEmptyLines . " Valid hashes: " . $uploadedValidHashes . " Valid hashes without expected salt: " . $uploadedValidHashesWithoutExpectedSalt . " Invalid hashes: " . $uploadedInvalidHashes); + Factory::getHashlistFactory()->set($hashlist, Hashlist::HASH_COUNT, $added); + Util::createLogEntry("User", $user->getId(), DLogEntry::INFO, "New Hashlist created: " . $hashlist->getHashlistName() . ". Total lines: " . $hashlistStatistics["uploadedTotalLines"] . " Empty lines: " . $hashlistStatistics["uploadedEmptyLines"] . " Valid hashes: " . $hashlistStatistics["uploadedValidHashes"] . " Valid hashes without expected salt: " . $hashlistStatistics["uploadedValidHashesWithoutExpectedSalt"] . " Invalid hashes: " . $hashlistStatistics["uploadedInvalidHashes"]); NotificationHandler::checkNotifications(DNotificationType::NEW_HASHLIST, new DataSet(array(DPayloadKeys::HASHLIST => $hashlist))); break; @@ -1042,19 +1044,20 @@ public static function createHashlist($name, $isSalted, $isSecret, $isHexSalted, $data = fread($file, Util::filesize($tmpfile)); $hash = new HashBinary(null, $hashlist->getId(), "", Util::bintohex($data), "", 0, null, 0, 0); Factory::getHashBinaryFactory()->save($hash); - $uploadedValidHashes++; + $hashlistStatistics["uploadedValidHashes"]++; } fclose($file); unlink($tmpfile); - Factory::getHashlistFactory()->mset($hashlist, [Hashlist::HASH_COUNT => 1, Hashlist::UPLOADED_TOTAL_LINES => $uploadedTotalLines, Hashlist::UPLOADED_EMPTY_LINES => $uploadedEmptyLines, Hashlist::UPLOADED_VALID_HASHES => $uploadedValidHashes, Hashlist::UPLOADED_VALID_HASHES_WITHOUT_EXPECTED_SALT => $uploadedValidHashesWithoutExpectedSalt, Hashlist::UPLOADED_VALID_HASHES => $uploadedValidHashes]); - Util::createLogEntry("User", $user->getId(), DLogEntry::INFO, "New Hashlist created: " . $hashlist->getHashlistName() . ". Total lines: " . $uploadedTotalLines . " Empty lines: " . $uploadedEmptyLines . " Valid hashes: " . $uploadedValidHashes . " Valid hashes without expected salt: " . $uploadedValidHashesWithoutExpectedSalt . " Invalid hashes: " . $uploadedInvalidHashes); + Factory::getHashlistFactory()->set($hashlist, Hashlist::HASH_COUNT, 1); + Util::createLogEntry("User", $user->getId(), DLogEntry::INFO, "New Hashlist created: " . $hashlist->getHashlistName() . ". Total lines: " . $hashlistStatistics["uploadedTotalLines"] . " Empty lines: " . $hashlistStatistics["uploadedEmptyLines"] . " Valid hashes: " . $hashlistStatistics["uploadedValidHashes"] . " Valid hashes without expected salt: " . $hashlistStatistics["uploadedValidHashesWithoutExpectedSalt"] . " Invalid hashes: " . $hashlistStatistics["uploadedInvalidHashes"]); NotificationHandler::checkNotifications(DNotificationType::NEW_HASHLIST, new DataSet(array(DPayloadKeys::HASHLIST => $hashlist))); break; } - return $hashlist; + + return ["hashlist" => $hashlist, "statistics" => $hashlistStatistics]; } /**