mirror of
https://github.com/trustedsec/hate_crack.git
synced 2026-07-28 14:47:22 -07:00
feat(hashview): download rule files from /v1/rules
Add HashviewAPI.list_rules and download_rules wrapping the Hashview rule endpoints. The server gzip-compresses plaintext rules on the fly, so download_rules decompresses before saving, yielding a file usable directly with hashcat -r. Wired into the interactive Hashview menu (Download Rule) and the CLI (hashview download-rules --rules-id/--output). Unknown rule ids surface the real HTTP 404. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
201cc37e55
commit
144d1a5334
@@ -239,6 +239,60 @@ class TestHashviewAPI:
|
||||
|
||||
assert api.get_hashfile_hash_type(1000) == [3, 9]
|
||||
|
||||
def test_list_rules_native_array(self, api):
|
||||
"""/v1/rules returns {rules: [...]} as a native JSON array."""
|
||||
mock_resp = Mock()
|
||||
mock_resp.json.return_value = {
|
||||
"status": 200,
|
||||
"rules": [{"id": 4, "name": "best64.rule", "size": 77}],
|
||||
}
|
||||
mock_resp.raise_for_status = Mock()
|
||||
api.session.get.return_value = mock_resp
|
||||
|
||||
rules = api.list_rules()
|
||||
assert rules == [{"id": 4, "name": "best64.rule", "size": 77}]
|
||||
|
||||
def test_download_rules_gunzips_to_plaintext(self, api, tmp_path):
|
||||
"""Rule download arrives gzip-compressed; saved file must be plaintext."""
|
||||
import gzip
|
||||
|
||||
plaintext = b":\nc\nu\nsa\n"
|
||||
mock_resp = Mock()
|
||||
mock_resp.content = gzip.compress(plaintext)
|
||||
mock_resp.headers = {}
|
||||
mock_resp.raise_for_status = Mock()
|
||||
api.session.get.return_value = mock_resp
|
||||
|
||||
out = os.path.join(str(tmp_path), "best64.rule")
|
||||
result = api.download_rules(4, out)
|
||||
assert result["output_file"] == out
|
||||
with open(out, "rb") as f:
|
||||
assert f.read() == plaintext
|
||||
|
||||
def test_download_rules_passes_plaintext_through(self, api, tmp_path):
|
||||
"""If the body is already plaintext (not gzip), save it unchanged."""
|
||||
mock_resp = Mock()
|
||||
mock_resp.content = b":\nc\nu\n"
|
||||
mock_resp.headers = {}
|
||||
mock_resp.raise_for_status = Mock()
|
||||
api.session.get.return_value = mock_resp
|
||||
|
||||
out = os.path.join(str(tmp_path), "plain.rule")
|
||||
api.download_rules(7, out)
|
||||
with open(out, "rb") as f:
|
||||
assert f.read() == b":\nc\nu\n"
|
||||
|
||||
def test_download_rules_raises_on_404(self, api, tmp_path):
|
||||
"""Unknown rule id is a real HTTP 404 -> raise_for_status propagates."""
|
||||
from requests.exceptions import HTTPError
|
||||
|
||||
mock_resp = Mock()
|
||||
mock_resp.raise_for_status = Mock(side_effect=HTTPError("404"))
|
||||
api.session.get.return_value = mock_resp
|
||||
|
||||
with pytest.raises(HTTPError):
|
||||
api.download_rules(99999999, os.path.join(str(tmp_path), "x.rule"))
|
||||
|
||||
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()
|
||||
|
||||
@@ -19,6 +19,13 @@ class DummyHashviewAPI:
|
||||
self.calls.append(("upload_wordlist_file", wordlist_path, wordlist_name))
|
||||
return {"msg": "Wordlist uploaded", "wordlist_id": 123}
|
||||
|
||||
def download_rules(self, rules_id, output_file=None):
|
||||
self.calls.append(("download_rules", rules_id, output_file))
|
||||
return {
|
||||
"output_file": output_file or f"rule_{rules_id}.rule",
|
||||
"size": 77,
|
||||
}
|
||||
|
||||
def download_left_hashes(self, customer_id, hashfile_id, output_file=None):
|
||||
self.calls.append(
|
||||
("download_left_hashes", customer_id, hashfile_id, output_file)
|
||||
@@ -111,6 +118,23 @@ def test_hashview_cli_upload_wordlist(_patch_hashview, monkeypatch, tmp_path, ca
|
||||
assert "Wordlist uploaded" in captured.out
|
||||
|
||||
|
||||
def test_hashview_cli_download_rules(_patch_hashview, monkeypatch, tmp_path, capsys):
|
||||
code = _run_main_with_args(
|
||||
monkeypatch,
|
||||
[
|
||||
"hashview",
|
||||
"download-rules",
|
||||
"--rules-id",
|
||||
"4",
|
||||
"--output",
|
||||
str(tmp_path / "best64.rule"),
|
||||
],
|
||||
)
|
||||
captured = capsys.readouterr()
|
||||
assert code == 0
|
||||
assert "Downloaded 77 bytes" in captured.out
|
||||
|
||||
|
||||
def test_hashview_cli_upload_hashfile_job(
|
||||
_patch_hashview, monkeypatch, tmp_path, capsys
|
||||
):
|
||||
|
||||
Reference in New Issue
Block a user