From 9b684bb44c3cd04b00a5421691179ea56162780c Mon Sep 17 00:00:00 2001 From: Justin Bollinger Date: Wed, 22 Apr 2026 19:32:44 -0400 Subject: [PATCH] style: ruff format pass for Notifications submenu Co-Authored-By: Claude Sonnet 4.6 --- hate_crack/main.py | 46 ++++++++++++++++++------- hate_crack/notify/settings.py | 8 +++-- tests/test_notifications_submenu.py | 1 + tests/test_notify_per_crack_toggle.py | 5 ++- tests/test_notify_settings.py | 49 ++++++++++++++++----------- 5 files changed, 71 insertions(+), 38 deletions(-) diff --git a/hate_crack/main.py b/hate_crack/main.py index f9bccf7..a975686 100755 --- a/hate_crack/main.py +++ b/hate_crack/main.py @@ -401,7 +401,9 @@ if hcatOptimizedWordlists: if not os.path.isdir(hcatOptimizedWordlists): fallback_optimized = os.path.join(hate_path, "optimized_wordlists") if os.path.isdir(fallback_optimized): - print(f"[!] hcatOptimizedWordlists directory not found: {hcatOptimizedWordlists}") + print( + f"[!] hcatOptimizedWordlists directory not found: {hcatOptimizedWordlists}" + ) print(f"[!] Falling back to {fallback_optimized}") hcatOptimizedWordlists = fallback_optimized else: @@ -417,8 +419,12 @@ pipalPath = config_parser["pipalPath"] hcatDictionaryWordlist = config_parser["hcatDictionaryWordlist"] hcatHybridlist = config_parser["hcatHybridlist"] hcatCombinationWordlist = config_parser["hcatCombinationWordlist"] -hcatCombinator3Wordlist = config_parser.get("hcatCombinator3Wordlist", ["rockyou.txt", "rockyou.txt", "rockyou.txt"]) -hcatCombinatorXWordlist = config_parser.get("hcatCombinatorXWordlist", ["rockyou.txt", "rockyou.txt"]) +hcatCombinator3Wordlist = config_parser.get( + "hcatCombinator3Wordlist", ["rockyou.txt", "rockyou.txt", "rockyou.txt"] +) +hcatCombinatorXWordlist = config_parser.get( + "hcatCombinatorXWordlist", ["rockyou.txt", "rockyou.txt"] +) hcatMiddleCombinatorMasks = config_parser["hcatMiddleCombinatorMasks"] hcatMiddleBaseList = config_parser["hcatMiddleBaseList"] hcatThoroughCombinatorMasks = config_parser["hcatThoroughCombinatorMasks"] @@ -584,8 +590,12 @@ hcatDictionaryWordlist = _normalize_wordlist_setting( hcatCombinationWordlist = _normalize_wordlist_setting( hcatCombinationWordlist, wordlists_dir ) -hcatCombinator3Wordlist = _normalize_wordlist_setting(hcatCombinator3Wordlist, wordlists_dir) -hcatCombinatorXWordlist = _normalize_wordlist_setting(hcatCombinatorXWordlist, wordlists_dir) +hcatCombinator3Wordlist = _normalize_wordlist_setting( + hcatCombinator3Wordlist, wordlists_dir +) +hcatCombinatorXWordlist = _normalize_wordlist_setting( + hcatCombinatorXWordlist, wordlists_dir +) hcatHybridlist = _normalize_wordlist_setting(hcatHybridlist, wordlists_dir) hcatMiddleBaseList = _normalize_wordlist_setting(hcatMiddleBaseList, wordlists_dir) hcatThoroughBaseList = _normalize_wordlist_setting(hcatThoroughBaseList, wordlists_dir) @@ -2397,8 +2407,14 @@ def hcatMarkovTrain(source_file, hcatHashFile): hcatProcess.wait(timeout=300) if hcatProcess.returncode != 0: _, stderr_data = hcatProcess.communicate() - err_msg = stderr_data.decode("utf-8", errors="replace") if stderr_data else "Unknown error" - print(f"[!] hcstat2gen.bin failed with code {hcatProcess.returncode}: {err_msg}") + err_msg = ( + stderr_data.decode("utf-8", errors="replace") + if stderr_data + else "Unknown error" + ) + print( + f"[!] hcstat2gen.bin failed with code {hcatProcess.returncode}: {err_msg}" + ) return False except subprocess.TimeoutExpired: print("[!] hcstat2gen.bin timed out after 300 seconds") @@ -3011,7 +3027,9 @@ def cleanup(): if os.path.isfile(out_path): print(f"\nCracked passwords combined with original hashes in {out_path}") else: - print(f"\nNo cracked hashes to combine. Raw output (if any): {hcatHashFile}.out") + print( + f"\nNo cracked hashes to combine. Raw output (if any): {hcatHashFile}.out" + ) print("\nCleaning up temporary files...") if os.path.exists(hcatHashFile + ".masks"): os.remove(hcatHashFile + ".masks") @@ -3813,9 +3831,7 @@ def wordlist_filter_req_exclude(infile: str, outfile: str, mask: int) -> bool: return result.returncode == 0 -def wordlist_cutb( - infile: str, outfile: str, offset: int, length: int | None -) -> bool: +def wordlist_cutb(infile: str, outfile: str, offset: int, length: int | None) -> bool: """Extract a substring from each word starting at offset, optionally limited to length bytes.""" cutb_bin = os.path.join(hate_path, "hashcat-utils/bin/cutb.bin") cmd = [cutb_bin, str(offset)] @@ -3853,7 +3869,9 @@ def wordlist_gate(infile: str, outfile: str, mod: int, offset: int) -> bool: """Shard wordlist: keep every mod-th line starting at offset.""" gate_bin = os.path.join(hate_path, "hashcat-utils/bin/gate.bin") with open(infile, "rb") as fin, open(outfile, "wb") as fout: - result = subprocess.run([gate_bin, str(mod), str(offset)], stdin=fin, stdout=fout) + result = subprocess.run( + [gate_bin, str(mod), str(offset)], stdin=fin, stdout=fout + ) return result.returncode == 0 @@ -3871,7 +3889,9 @@ def rules_cleanup(infile: str, outfile: str) -> bool: def rules_optimize(infile: str, outfile: str) -> bool: """Optimize a rule file using rules_optimize.bin. Returns True on success.""" - optimize_path = os.path.join(hate_path, "hashcat-utils", "bin", "rules_optimize.bin") + optimize_path = os.path.join( + hate_path, "hashcat-utils", "bin", "rules_optimize.bin" + ) with open(infile, "rb") as fin, open(outfile, "wb") as fout: result = subprocess.run([optimize_path], stdin=fin, stdout=fout) return result.returncode == 0 diff --git a/hate_crack/notify/settings.py b/hate_crack/notify/settings.py index d6279c5..d5f7fe1 100644 --- a/hate_crack/notify/settings.py +++ b/hate_crack/notify/settings.py @@ -84,8 +84,12 @@ def load_settings(config_parser: dict | None) -> NotifySettings: defaults = NotifySettings() return NotifySettings( enabled=_coerce_bool(cfg.get("notify_enabled"), defaults.enabled), - pushover_token=_coerce_str(cfg.get("notify_pushover_token"), defaults.pushover_token), - pushover_user=_coerce_str(cfg.get("notify_pushover_user"), defaults.pushover_user), + pushover_token=_coerce_str( + cfg.get("notify_pushover_token"), defaults.pushover_token + ), + pushover_user=_coerce_str( + cfg.get("notify_pushover_user"), defaults.pushover_user + ), per_crack_enabled=_coerce_bool( cfg.get("notify_per_crack_enabled"), defaults.per_crack_enabled ), diff --git a/tests/test_notifications_submenu.py b/tests/test_notifications_submenu.py index 57ac41a..6189104 100644 --- a/tests/test_notifications_submenu.py +++ b/tests/test_notifications_submenu.py @@ -6,6 +6,7 @@ and resolves ``toggle_notifications`` / ``toggle_per_crack_notifications`` / We therefore patch that module directly — patching the ``hate_crack.py`` proxy would have no effect on the submenu's internal dispatch. """ + import hate_crack.main as _main_mod import hate_crack.menu as _menu_mod from hate_crack.notify.settings import NotifySettings diff --git a/tests/test_notify_per_crack_toggle.py b/tests/test_notify_per_crack_toggle.py index ecda4b1..8a2c780 100644 --- a/tests/test_notify_per_crack_toggle.py +++ b/tests/test_notify_per_crack_toggle.py @@ -1,4 +1,5 @@ """Unit tests for the toggle_per_crack_enabled runtime toggle.""" + import importlib.util import json from pathlib import Path @@ -80,9 +81,7 @@ class TestTogglePerCrackNotificationsUI: from hate_crack.notify.settings import NotifySettings settings = NotifySettings(enabled=enabled, per_crack_enabled=per_crack) - monkeypatch.setattr( - CLI_MODULE._notify, "get_settings", lambda: settings - ) + monkeypatch.setattr(CLI_MODULE._notify, "get_settings", lambda: settings) return settings def test_guard_refuses_on_when_global_off(self, monkeypatch, capsys): diff --git a/tests/test_notify_settings.py b/tests/test_notify_settings.py index c8201e4..34d592b 100644 --- a/tests/test_notify_settings.py +++ b/tests/test_notify_settings.py @@ -1,4 +1,5 @@ """Unit tests for hate_crack.notify.settings.""" + import json from pathlib import Path @@ -40,16 +41,18 @@ class TestLoadSettings: assert load_settings(None) == NotifySettings() def test_load_full_dict(self) -> None: - s = load_settings({ - "notify_enabled": True, - "notify_pushover_token": "tok", - "notify_pushover_user": "usr", - "notify_per_crack_enabled": True, - "notify_attack_allowlist": ["Brute Force", "Dictionary"], - "notify_suppress_in_orchestrators": False, - "notify_max_cracks_per_burst": 20, - "notify_poll_interval_seconds": 2.5, - }) + s = load_settings( + { + "notify_enabled": True, + "notify_pushover_token": "tok", + "notify_pushover_user": "usr", + "notify_per_crack_enabled": True, + "notify_attack_allowlist": ["Brute Force", "Dictionary"], + "notify_suppress_in_orchestrators": False, + "notify_max_cracks_per_burst": 20, + "notify_poll_interval_seconds": 2.5, + } + ) assert s.enabled is True assert s.pushover_token == "tok" assert s.pushover_user == "usr" @@ -60,12 +63,14 @@ class TestLoadSettings: assert s.poll_interval_seconds == 2.5 def test_load_tolerates_bad_types(self) -> None: - s = load_settings({ - "notify_enabled": "true", - "notify_max_cracks_per_burst": "not-a-number", - "notify_poll_interval_seconds": "also-bad", - "notify_attack_allowlist": "not-a-list", - }) + s = load_settings( + { + "notify_enabled": "true", + "notify_max_cracks_per_burst": "not-a-number", + "notify_poll_interval_seconds": "also-bad", + "notify_attack_allowlist": "not-a-list", + } + ) # string "true" -> True assert s.enabled is True # bad ints fall back to defaults (5, 5.0) @@ -136,10 +141,14 @@ class TestAddToAllowlist: def test_preserves_other_entries(self, tmp_path: Path) -> None: config_path = tmp_path / "config.json" - config_path.write_text(json.dumps({ - "hcatBin": "hashcat", - "notify_attack_allowlist": ["Existing"], - })) + config_path.write_text( + json.dumps( + { + "hcatBin": "hashcat", + "notify_attack_allowlist": ["Existing"], + } + ) + ) add_to_allowlist(str(config_path), "Brute Force") data = json.loads(config_path.read_text()) assert data["hcatBin"] == "hashcat"