Merge branch 'fix/hashview-hashtype-parsing'

This commit is contained in:
Justin Bollinger
2026-07-21 17:56:34 -04:00
2 changed files with 64 additions and 8 deletions
+21 -8
View File
@@ -1138,7 +1138,13 @@ class HashviewAPI:
data = None
hashtype = None
if data:
hashtype = data.get("hashtype") or data.get("hash_type") or data.get("type")
# Prefer explicit hash-type keys by PRESENCE, not truthiness: MD5 is
# hash_type 0, which is falsy. Never fall back to `type` — that is
# the envelope tag ("message"), never a hash mode.
if "hash_type" in data:
hashtype = data["hash_type"]
elif "hashtype" in data:
hashtype = data["hashtype"]
if self.debug:
print(
f"[DEBUG] get_hashfile_details({hashfile_id}): raw data={data}, hashtype={hashtype}"
@@ -1660,21 +1666,28 @@ class HashviewAPI:
def get_hashfile_hash_type(self, hashtype_id):
"""
Query /v1/hashfiles/hash_type/<int:hashtype_id> and return a list of file IDs.
The endpoint answers with an envelope ``{..., "hashfiles": [ {...} ]}``
(native JSON objects); we extract the ``id`` of each hashfile.
"""
url = f"{self.base_url}/v1/hashfiles/hash_type/{hashtype_id}"
resp = self.session.get(url)
resp.raise_for_status()
try:
data = resp.json()
# Expecting a list of file IDs or a dict with a key containing them
# A bare list is tolerated for forward/backward compatibility.
if isinstance(data, list):
return data
hashfiles = data
elif isinstance(data, dict):
# Try common keys
for key in ("file_ids", "ids", "hashfile_ids"):
if key in data and isinstance(data[key], list):
return data[key]
return []
hashfiles = data.get("hashfiles") or []
else:
return []
ids = []
for hf in hashfiles:
hf_id = hf.get("id") if isinstance(hf, dict) else hf
if hf_id is not None:
ids.append(hf_id)
return ids
except Exception:
return []
+43
View File
@@ -196,6 +196,49 @@ class TestHashviewAPI:
result = api.list_customers()
assert result["customers"] == [{"id": 2, "name": "Beta"}]
def test_get_hashfile_details_md5_zero(self, api):
"""hash_type 0 (MD5) is falsy; must not fall through to the envelope `type`."""
mock_resp = Mock()
mock_resp.json.return_value = {
"hash_type": 0,
"msg": "OK",
"status": 200,
"type": "message",
}
mock_resp.raise_for_status = Mock()
api.session.get.return_value = mock_resp
details = api.get_hashfile_details(42)
assert details["hashtype"] == 0
def test_get_hashfile_details_ntlm(self, api):
"""Sanity: NTLM (1000) still parses from `hash_type`."""
mock_resp = Mock()
mock_resp.json.return_value = {
"hash_type": 1000,
"msg": "OK",
"status": 200,
"type": "message",
}
mock_resp.raise_for_status = Mock()
api.session.get.return_value = mock_resp
assert api.get_hashfile_details(7)["hashtype"] == 1000
def test_get_hashfile_hash_type_reads_hashfiles_key(self, api):
"""Endpoint returns {hashfiles: [...]} objects; return their ids."""
mock_resp = Mock()
mock_resp.json.return_value = {
"status": 200,
"type": "message",
"msg": "OK",
"hashfiles": [{"id": 3, "name": "a"}, {"id": 9, "name": "b"}],
}
mock_resp.raise_for_status = Mock()
api.session.get.return_value = mock_resp
assert api.get_hashfile_hash_type(1000) == [3, 9]
def test_upload_cracked_hashes_success(self, api, tmp_path):
"""Test uploading cracked hashes with valid lines (real API if possible)."""
hashview_url, hashview_api_key = self._get_hashview_config()